WPF Grouping Using CollectionViewSource and DataBinding

I bind CollectionViewSource

to ListView

to group items. This all works fine, except when I update ObservableCollection

from a CollectionViewSource

. If I update the value of an object in the collection, the UI is never updated. Here's an example:

<ListView x:Name="MyListView" Margin="0,0,0,65">
    <ListView.GroupStyle>
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Margin" Value="0,0,0,5"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                <Expander IsExpanded="true" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
                                    <Expander.Header>
                                        <DockPanel>
                                            <TextBlock FontWeight="Bold" Text="{Binding Name}" Margin="5,0,0,0" Width="80"/>
                                            <TextBlock FontWeight="Bold" Width="60" TextAlignment="Center" Margin="16,0,0,0" Text="{Binding Items, Converter={StaticResource Converter2}}" />
                                        </DockPanel>
                                    </Expander.Header>
                                    <ItemsPresenter />
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
    </ListView.GroupStyle>
    <ListView.View>
        <GridView>
            <GridViewColumn Width="300" Header="Amount" >
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Amount}" Margin="80,0,0,0"/>
                    </DataTemplate>
                 </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

      

You will notice that it calls the converter on the group and gives it a collection of items. This means that the converter can calculate the average of the rows and return the result:

public class AverageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        IEnumerable<object> rows = (IEnumerable<object>) value;
        double average = rows.Average(a => ((DisplayRow) a).Amount);
        return average;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

      

In the code behind, I add lines and create CollectionViewSource

:

private readonly ObservableCollection displayRows = new ObservableCollection ();

public Window1()
{
    InitializeComponent();

    displayRows.Add(new DisplayRow { Title = "Sales", Amount=16} );
    displayRows.Add(new DisplayRow { Title = "Marketing", Amount=14} );
    displayRows.Add(new DisplayRow { Title = "Technology", Amount=13} );
    displayRows.Add(new DisplayRow { Title = "Sales", Amount=11} );
    displayRows.Add(new DisplayRow { Title = "Marketing", Amount=13} );
    displayRows.Add(new DisplayRow { Title = "Technology", Amount=12} );
    CollectionViewSource viewSource = new CollectionViewSource { Source = displayRows };
    viewSource.GroupDescriptions.Add(new PropertyGroupDescription("Title"));
    MyListView.ItemsSource = viewSource.View;
}

      

An object DisplayRow

implements INotifyPropertyChanged

and is just a class.

Everything works well and the display is the way I want it, but if I change the value in ObservableCollection

, the UI doesn't change.

If I add an item to the collection, I see it appear on the screen, but the converter is never called to recalculate the mean. Any ideas?

+2


source to share


3 answers


I found a problem with this issue.

private CollectionViewSource _viewSource;

private void ModifyData()
{
    // Modify some data

    // This will cause the ListView to refresh its data and update the UI
    // and also cause the converter to be called to reformat the data.
    _viewSource.View.Refresh();
}

      



Hope it helps.

+4


source


Without updating the entire view, this can still be handled, I implemented it.

Creating data bindings in the CollectionView will allow you to change notifications for groups.



Check out http://stumblingaroundinwpf.blogspot.com/2009/11/building-smarter-wpf-collectionview-one.html

+2


source


If the data source is an ObservableCollection, the view will be updated whenever the collection changes, that is, when there are added or removed items. If you want the view to update when you edit the underlying data, this class must implement the INotifyPropertyChanged interface.

In your case, the DisplayRow class should implement INotifyPropertyChanged and displayRows should be an ObservableCollection.

This is the official way to do it based on what I understand. Maybe I'm wrong.

+1


source







All Articles