Search code examples
listviewxamarin.formsxamarin.forms.listview

Binding of image trigger in the GroupHeaderTemplate of Xamarin Forms not working


This is the group header template. The binding for the image trigger in the view model is not working. I want to change the image according to a tapped event of grouped header. When group header is expanded the image should be ArrowDown and when the group header is collapsed, the image should be up ArrowUp


                        <ListView.GroupHeaderTemplate>
                            <DataTemplate>
                                <ViewCell >
                                    <ContentView >
                                        <ContentView.GestureRecognizers>
                                            <TapGestureRecognizer Command="{Binding Source={x:Reference TheMainPage}, Path=BindingContext.HeaderSelectedCommand}" CommandParameter="{Binding .}" Tapped="TapGestureRecognizer_OnTapped"/>
                                        </ContentView.GestureRecognizers>
                                        <StackLayout Padding="15,12,15,12" Orientation="Horizontal"  BackgroundColor="{StaticResource DefaultBackgroundColor}">
                                            <StackLayout Orientation="Vertical" HorizontalOptions="Start">
                                                <Label Text="{Binding Key.EpisodeModel.EpisodeTitle}"  FontSize="16" TextColor="Black"/>
                                                <Label Text="{Binding Key.EpisodeModel.Department}" />
                                                <Label Text="{Binding Key.EpisodeModel.PeriodOfEpisode}"/>
                                            </StackLayout>
                                            <Label  BackgroundColor="{StaticResource LightAirColor}"  Text="{Binding Key.EpisodeModel.DocumentCount}" VerticalOptions="Center" HorizontalOptions="EndAndExpand" FontSize="Medium" TextColor="Black"/>

                                            <Image HeightRequest="15">
                                                <Image.Triggers>
                                                    <DataTrigger TargetType="Image" Binding="{Binding headerTappedImage}" Value="true">
                                                        <Setter Property="Source" Value="ArrowDown.png"></Setter>
                                                    </DataTrigger>
                                                    <DataTrigger TargetType="Image" Binding="{Binding headerTappedImage}" Value="false">
                                                        <Setter Property="Source" Value="ArrowDown.png"></Setter>
                                                    </DataTrigger>
                                                </Image.Triggers>
                                            </Image>

                                        </StackLayout>
                                    </ContentView>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.GroupHeaderTemplate>

This is my code behind

 private bool _headerTappedImage;

        public bool headerTappedImage
        {
            get { return _headerTappedImage; }
            set { _headerTappedImage = value; OnPropertyChanged("headerTappedImage");}
        }




        public JournalHistoryViewPage(JournalHistoryPageViewModel journalHistoryPageViewModel) : base(
            journalHistoryPageViewModel)
        {
            headerTappedImage= false;
            InitializeComponent();
            _journalHistoryPageViewModel = journalHistoryPageViewModel;

            DocumentList.RefreshCommand = new Command(RefreshJournalHistoryPage);
        }

Logic is not written. but an image should be shown in the header template. but it's not shown. what is the reason for that?


Solution

  • Assuming BindingContext of LayoutRoot below is the same as the BindingContext of the Page, triggers can be used as suggested in the question:

    <StackLayout x:Name="LayoutRoot">
      <ListView x:Name="listView"
                ItemsSource="{Binding ItemsGroups}"
        ...
        <ListView.GroupHeaderTemplate>
          <DataTemplate>
            <ViewCell>
              <StackLayout>
                <StackLayout.GestureRecognizers>
                  <TapGestureRecognizer Command="{Binding Source={x:Reference LayoutRoot}, 
                                                          Path=BindingContext.HeaderSelectedCommand}" 
                                        CommandParameter="{Binding .}" />
                </StackLayout.GestureRecognizers>
                ...
                <Image BackgroundColor="White">
                  <Image.Triggers>
                    <DataTrigger TargetType="Image" 
                                 Binding="{Binding IsVisible}" Value="False">
                      <Setter Property="Source" 
                              Value="{OnPlatform Android=add_black.png, UWP=Assets/add_black.png}" />
                    </DataTrigger>
                    <DataTrigger TargetType="Image" 
                                 Binding="{Binding IsVisible}" Value="True">
                      <Setter Property="Source" 
                              Value="{OnPlatform Android=remove_black.png, UWP=Assets/remove_black.png}" />
                    </DataTrigger>
                  </Image.Triggers>
                </Image>
                ...
    

    The IsVisible property is part of Grouping, example implementation:

    public class Grouping<K, T> : ObservableCollection<T>
    {
        public K Key { get; private set; }
    
        private bool isVisible;
    
        public bool IsVisible
        {
            get { return isVisible; }
            set
            {
                isVisible = value;
                OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsVisible)));
            }
        }
    
        public Grouping(K key, IEnumerable<T> items)
        {
            Key = key;
            foreach (var item in items)
                this.Items.Add(item);
        }
    }
    

    ItemsGroups might be defined in the ViewModel for BindingContext of LayoutRoot as follows, definition of ItemViewModel not shown:

    public ObservableCollection<Grouping<string, ItemViewModel>> ItemsGroups { get; set; }
    

    A group of items might be added to ItemsGroups:

    var keyForGroupA = "A";
    var itemsForGroupA = new ObservableCollection<ItemViewModel>();
    var item1 = new ItemViewModel();
    itemsForGroupA.Add(item1);
    var item2 = new ItemViewModel();
    itemsForGroupA.Add(item2);
    ...
    ItemsGroups.Add(new Grouping<string, ItemViewModel>(keyForGroupA, itemsForGroupA);
    

    Example implementation of the HeaderSelectedCommand:

    // in constructor:
    HeaderSelectedCommand = new Command(p => HeaderSelectedCommandExecute(p));
    
    // property
    public ICommand HeaderSelectedCommand { get; private set; }
    
    // action
    void HeaderSelectedCommandExecute(object p)
    {
        var grp = (Grouping<string, ItemViewModel>)p;
        grp.IsVisible = !grp.IsVisible;
    }