0
votes

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

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

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

2

2 Answers

0
votes

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; }
}
0
votes

Thanks to @Roubachof

The soluce was replace my ListView of InterventionModel by ListView of AgendaDataViewModel.

AgendaViewModel is a new class which contains all the commands I need, and an InterventionModel.

this is AgendaDataViewModel :

public class AgendaDataViewModel : HybridContentViewModel
    {
        private InterventionModel _model;

        public InterventionModel Model
        {
            get => _model;
            set { _model = value; }
        }
        public ICommand OpenActiviteCommand { get; private set; }

        public AgendaDataViewModel()
        {
            this.OpenActiviteCommand = new Command<InterventionModel>(this.OpenActivite);
        }

        /// <summary>
        /// Ouvre le formulaire d'édition de l'activité
        /// </summary>
        /// <param name="model"></param>
        private void OpenActivite(InterventionModel model)
        {
            //TODO amener sur le formulaire d'activité
        }
    }

my AgendaTemplate.xaml


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

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

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

As you can see, the values binding is made by this line :

{Binding Model.Client}

where Client is the name of Binded property. And to Bind a Command, you don't need Model, and just bind like this :

Command={Binding CommandName}

Hope it helps someone in the future !