DataGrid grouping doesn't update on change in CollectionViewSource

2.8k views Asked by At

I have a CollectionViewSource CollectionOfVideos bound to ObservableCollection ListOfVideos. That CollectionViewSource is the ItemsSource for the DataGrid dataGrid_Video. I've implemented a grouping by lShortName of label of ListOfVideos. When items are added to DataGrid, grouping seems to work, because it groups all added items by their default label.

Problem is, grouping doesn't update when label of item is changed. Here's my code:

Window.Resources in MainWindow.xaml

<CollectionViewSource x:Key="CollectionOfVideos" Source="{Binding fosaModel.ListOfVideos, UpdateSourceTrigger=PropertyChanged}" IsLiveGroupingRequested="True" IsLiveSortingRequested="True" >
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="label" Converter="{StaticResource LTSConverter}"/>
    </CollectionViewSource.GroupDescriptions>
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="label.lShortName"/>
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

dataGrid_Video in MainWindow.xaml

       <DataGrid x:Name="dataGrid_Video" 
          AutoGenerateColumns="False" 
          Margin="0,0,0,0" 
          Grid.Row="1" 
          ItemsSource="{Binding Source={StaticResource CollectionOfVideos}}" 
          GridLinesVisibility="Horizontal"
          SelectedItem="{Binding fosaModel.SelectedVideo}"
          SelectedIndex="{Binding fosaModel.SelectedVideoIndex}"
          SelectionUnit="FullRow"
          SelectionMode="Single">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding fName}" Header="Nazwa" IsReadOnly="True"></DataGridTextColumn>
                <DataGridTextColumn Binding="{Binding AudioToSync}" Header="Dźwięk"></DataGridTextColumn>
            </DataGrid.Columns>
            <DataGrid.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <TextBlock Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" />
                            </StackPanel>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <Expander IsExpanded="True">
                                            <Expander.Header>
                                                <StackPanel Orientation="Horizontal">
                                                    <TextBlock Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}"/>
                                                </StackPanel>
                                            </Expander.Header>
                                            <ItemsPresenter />
                                        </Expander>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </DataGrid.GroupStyle>
        </DataGrid>

Here's my fosaModel

   public class fosaModel : INotifyPropertyChanged

    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public ObservableCollection<fosaVideo> ListOfVideos { get; set; } = new ObservableCollection<fosaVideo>();
        public ObservableCollection<fosaAudio> ListOfAudios { get; set; } = new ObservableCollection<fosaAudio>();
        public ObservableCollection<fosaLabel> ListOfLabels { get; set; } = new ObservableCollection<fosaLabel>();
        public fosaVideo SelectedVideo { get; set; }
        public int SelectedVideoIndex { get; set; } = -1;
        public fosaAudio SelectedAudio { get; set; }
        public int SelectedAudioIndex { get; set; } = -1;
        public fosaLabel SelectedLabel { get; set; }
        public int SelectedLabelIndex { get; set; } = -1;
        public string WorkingDir { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        public double videoProgress { get; set; }
        public int SliderValue { get; set; }
        public bool IsDialogClosed { get; set; } = true;

    }

There are few things you should know:

  • I use Fody.PropertyChanged, so it's not PropertyChanged notification problem. Adding new items to the list updates the DataGrid.
  • Sorting doesn't work either.
  • Changing label of particular item in ListOfVideos does work, I checked in Debug
  • I thought it was a problem with dot notation of PropertyName in GroupingDescriptions, so I've added a converter. With or without converter - problem still exists.
  • There is also very similar CollectionViewSource, with sorting implemented, and it works on adding new item.
  • I use MVVM Light and I'd like to keep it MVVM way.
1

There are 1 answers

1
mm8 On BEST ANSWER

You should add the names of the properties to live group by to the LiveGroupingProperties collection of the CollectionViewSource:

<CollectionViewSource x:Key="CollectionOfVideos" Source="{Binding fosaModel.ListOfVideos, UpdateSourceTrigger=PropertyChanged}" 
                              IsLiveGroupingRequested="True" IsLiveSortingRequested="True"
                              xmlns:s="clr-namespace:System;assembly=mscorlib">
    <CollectionViewSource.LiveGroupingProperties>
        <s:String>label</s:String>
    </CollectionViewSource.LiveGroupingProperties>
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="label" Converter="{StaticResource LTSConverter}"/>
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

It's the same for the live sorting: https://wpf.2000things.com/2014/01/16/988-enabling-live-sorting-in-a-collectionviewsource/

I don't think this will work with nested properties such as "label.lShortName" though so if you want to live sort by a sub-property of the type T in your ObservableCollection<T> you need to add a property to the type T that wraps the sub-property, e.g.:

public string lShortName
{
    get { return label.lShortName; }
    set { label.lShortName = value; NotifyPropertyChanged(); }
}