views:

1837

answers:

2

I'm binding a CollectionViewSource to a ListView to group items. It all works great except when I update the ObservableCollection the CollectionViewSource is based on. If I update a value of an object in the collection the UI is never updated. Here is 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'll notice it's calling a converter in the group and giving it the items collection. This is so the converter can calculate the average of the rows and return that 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 the rows and create the 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;
}

The DisplayRow object implements INotifyPropertyChanged, and is just a simple class.

Everything works well and the display is the way I want, but if I change a value in the ObservableCollection the UI doesn't change.

If I add an element to the collection I can see it appear on the screen but the converter is never called to recompute the average. Any ideas?

+1  A: 

Hi,

I have found a hack way around this problem.

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 that helps.

Tri Q
Nice. Yeah it does help. Unfortunate that I need to update the whole thing but at least it gets me by that problem. Thanks.
Kelly
+1  A: 

Without refreshing the whole view, this can still be handled, I implemented this.

Creating databindings inside a CollectionView will allow change notifications for the groups.

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

Amir Burbea