xamlxamarin.formsbindingviewmodeldatatemplate

How bind a command in DataTemplate in Resource Dictionary?


I'm trying to make a better solution architecture, for that I've separated many parts of code in differents files. Because my application use a lot of DataTemplates, I push them in different ResourceDictionary.xaml files.

Problem :

I have a view Agenda.xaml, with the viewModel AgendaViewModel. This view have a ListView which call's datatemplate in external ResourceDictionary file. But if I want put a Binding Command in the dataTemplate, the command is never executed because (I guess) the resource Dictionary where is my DataTemplate not reference ViewModel.

What can I do ?

I've already tried some weird Binding code like

<TapGestureRecognizer Command="{Binding BindingContext.OpenActiviteCommand, Source={x:Reference agendaPage}}" CommandParameter="{Binding .}"/>

Where "agendaPage" is the x:Name of Agenda.xaml.

All I found on Google was about WPF and Binding property not available on Xamarin Forms (RelativeSource, ElementName etc...)

I know I can put dataTemplate in my Agenda.xaml view, but I really want keep it in an external file. I want avoid view files with 1500 lines....

This is my Agenda.xaml view

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Corim.Portable.CorimTouch.ViewForms.Agenda.AgendaViewDetail"
             xmlns:converters="clr-namespace:Corim.Portable.CorimTouch.Converters"
             Title="Agenda"
             x:Name="agendaPage">
    <ContentPage.Content>
        <Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"  BackgroundColor="{StaticResource LightGrayCorim}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <!-- Liste itv,pointage,activite -->
            <ListView 
                x:Name="listAgenda"
                Grid.Row="1"
                SeparatorVisibility="None"
                HasUnevenRows="True"
                SelectionMode="None"
                CachingStrategy="RecycleElement"
                ItemsSource="{Binding AgendaList}"
                ItemTemplate="{StaticResource agendaTemplateSelector}"
                BackgroundColor="{StaticResource LightGrayCorim}">
            </ListView>
        </Grid>
    </ContentPage.Content>
</ContentPage>

And this is one part of Datatemplate in AgendaTemplates.xaml

<DataTemplate x:Key="agenda-adresse-intervention">
  <ViewCell>
    <Frame Margin="10,5,10,0"
            Padding="0"
            CornerRadius="10"
            HasShadow="False"
            IsClippedToBounds="True">
      <controls:CustomTappedStackLayout Padding="10"
                                        BackgroundColor="White"
                                        HorizontalOptions="FillAndExpand"
                                        Orientation="Horizontal"
                                        TappedBackgroundColor="{StaticResource RollOver}">
        <StackLayout.GestureRecognizers>
          <TapGestureRecognizer Command="{Binding Path=BindingContext.OpenParcCommand, Source={x:Reference agendaPage}}"
                                CommandParameter="{Binding .}"
                                NumberOfTapsRequired="1" />
        </StackLayout.GestureRecognizers>
        <Image Margin="10"
               Source="localisation_adresse"
               Aspect="AspectFit"
               HeightRequest="30"
               HorizontalOptions="Start"
               VerticalOptions="StartAndExpand"
               WidthRequest="30" />

        <StackLayout HorizontalOptions="FillAndExpand" Orientation="Vertical">
          <Label Text="{Binding Client}"
                 FontFamily="{StaticResource SemiBoldFont}"
                 FontSize="{StaticResource MediumTextSize}"
                 IsVisible="{Binding Client, Converter={StaticResource StringEmptyBooleanConverter}}"
                 TextColor="Black" />

          <Label Text="{Binding Title}"
                 FontFamily="{StaticResource RegularFont}"
                 FontSize="{StaticResource DefaultTextSize}"
                 IsVisible="{Binding Title, Converter={StaticResource StringEmptyBooleanConverter}}"
                 TextColor="Gray" />
        </StackLayout>
      </controls:CustomTappedStackLayout>
    </Frame>
  </ViewCell>
</DataTemplate>

Solution

  • But if I want put a Binding Command in the dataTemplate, the command is never executed because (I guess) the resource Dictionary where is my DataTemplate not reference ViewModel.

    You guess wrong: it's totally fine to do what you are doing and should work transparently. The binding is resolved at runtime your data template does not know anything about the object that will be bound.

    1st: drop the BindingContext.OpenActiviteCommand nonsense :) Just bind to OpenActiviteCommand, the only question is:

    2nd: Where is your OpenActiviteCommand ?

    The data context of your AgendaTemplates is the item in your AgendaList. If the type of the AgendaList is an ObservableCollection<AgendaViewModel>, and your AgendaViewModel has a OpenParcCommand then it should be fine:

    public class AgendaViewModel
    {
        public AgendaViewModel(ICommand openParcCommand)
        {
            OpenParcCommand = openParcCommand;
        }
    
        public ICommand OpenParcCommand { get; }
    }
    

    and in your AgendaPageViewModel:

    public class AgendaPageViewModel
    {
        public ObservableCollection<AgendaViewModel> AgendaList { get; }
    }