How to associate user data with CollectionViewGroup?

I have an ItemsControl element in XAML where I show an expander for each group so that I can expand / collapse the group. I want to keep the state of the IsExpanded property (and possibly other settings related to the display of the group header). Usually you just have a class with properties on it and get attached to it. However, the data context for the CollectionViewGroup . Now this class is not very useful, since it gives you the Name property and the elements in the group (this is normal if you just want a title and maybe display some kind of metric based on the number of elements in the group or them and not if you just want to save user data about the status of the user interface of the group header) What I would like to do is extract from this class and add other properties to my derived class and bind it to this. But there seems to be no easy way to do this. All the details of the generations of groups seem to be hidden in the inner classes, which is very frustrating. Has anyone followed the path of implementing ICollectionView itself (and therefore all other related classes, presumably)? It seems like a huge job to replicate everything in a ListCollectionView just to create your own CollectionViewGroup class and bind to it instead! Thanks.

+6
source share
3 answers

One approach is to use MultiBinding to search or calculate user data and bind time.

I made a DataGrid with groups that show in the header the sum of the specific values ​​in the group in order to update this amount when the elements of the group change. I made a multi-valued binding with a custom multi-valued converter, a multi-valued binding with the ItemCount property allows you to receive notifications when group items change, and then update the amount and display the value of the news feed.

Here is the code for the multi-valued converter class:

 Public Class UserBalanceConverter Implements IMultiValueConverter Private Function GetSubTotal(ByVal obj As CollectionViewGroup) As String Dim total As Decimal For Each objItem As Object In obj.Items If TypeOf objItem Is Account Then Dim a As Account = DirectCast(objItem, Account) Dim rate As Decimal = 1 rate = 1 / ExchangeRatesInfo.GetExchangeRate(a.currencyCode.ToString) total += a.Balance * rate Else total += GetSubTotal(objItem) End If Next Return total.ToString("C") End Function Public Function Convert(ByVal value() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) _ As Object Implements System.Windows.Data.IMultiValueConverter.Convert Dim cvg As CollectionViewGroup = CType(value(1), CollectionViewGroup) Return GetSubTotal(cvg) End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) _ As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack Throw New NotImplementedException End Function End Class 

Then in XAML you use a multi-valued converter in the style used for GroupItem:

  <Style TargetType ="{x:Type GroupItem}" x:Key="UserGroupHeaderStyle"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type GroupItem}"> <Expander x:Name="exp" IsExpanded="False"> <Expander.Header> <StackPanel > <TextBlock Text="{Binding Name}" /> <StackPanel Orientation="Horizontal" > <TextBlock Text="{Binding ItemCount}"> <TextBlock Text=" "/> <TextBlock Text="items" /> <TextBlock Text=" "/> <TextBlock Text="Balance: " /> <TextBlock> <TextBlock.Text> <MultiBinding Converter="{StaticResource UserBalanceConverter}"> <Binding Path="ItemCount"/> <Binding /> </MultiBinding> </TextBlock.Text> </TextBlock> </StackPanel> </StackPanel> </Expander.Header> <ItemsPresenter /> </Expander> </ControlTemplate> </Setter.Value> </Setter> </Style> 

Finish applying the style to the DataGrid:

 <DataGrid.GroupStyle> <GroupStyle ContainerStyle="{StaticResource UserGroupHeaderStyle}"> <GroupStyle.Panel> <ItemsPanelTemplate> <DataGridRowsPresenter/> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </DataGrid.GroupStyle> 

Also, be sure to declare your conversion class in the resource section of your XAML:

  <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <local:UserBalanceConverter x:Key="UserBalanceConverter"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> 

Et Voilà! It works like a charm!

NTN

+1
source

A simple approach would be to simply wrap the CollectionViewGroup in another ViewModel class that will provide the additional required display properties, such as IsExpanded. The lesson I learned is not to bend xaml / view to fit business data. Instead, bend / wrap or convert business data according to user interface requirements.

0
source

I was able to solve exactly this problem (i.e. binding to IsExpanded ) using an approach that is somewhat similar to Cedric's suggestion, but apparently more MVVM:

 <ControlTemplate TargetType="GroupItem"> <TreeViewItem IsExpanded="{Binding Items[0].IsGroupExpanded, Mode=TwoWay}"> <TreeViewItem.Header> <TextBlock Text="{Binding Name}" /> </TreeViewItem.Header> <TreeViewItem.Items> <ItemsPresenter /> </TreeViewItem.Items> </TreeViewItem> </ControlTemplate> 

Both Parameters ItemViewModel.IsGroupExpanded setter and getter are redirected to Group.IsExpanded .

Note that the Mode=TwoWay must be specified, since IsExpanded to IsExpanded by default.

0
source

All Articles