Search code examples
wpfdata-bindingicollectionview

ICollectionView filter affects the source


I was experimenting with WPF and encountered some filtering behavior that I did not expect.

I created a simple Window control with a ListView and a DataGrid, which display information about US presidents, such as name, party, and numerical order.

The application instantiates an ObservableCollection with several presidents. In Main, a view is created from the ObservableCollection, and filtering and sorting is applied. The ListView is bound to this view, and the DataGrid is bound to the original ObservableCollection.

I expected the ListView to display the filtered result and the DataGrid to display all the items in the list. However, the DataGrid displays the filtered result as well. Does anyone have an explanation for this?

public partial class MainWindow : Window
{
    ICollectionView presidentView;

    ObservableCollection<President> presidents = new ObservableCollection<President>
    {
        new President{Name = "Barack Obama", Party="Democratic", Order=44},
        new President {Name = "George W Bush", Party="Republican", Order=43},
        new President{Name = "Bill Clinton", Party="Democratic", Order=42},
        new President {Name="George Bush", Party="Republican", Order=41},
        new President{Name="Ronald Reagan", Party="Republican", Order=40},
        new President{Name="Jimmy Carter", Party="Democratic", Order=39},
        new President{Name="Gerald Ford", Party="Republican", Order=38},
        new President{Name="Richard Nixon", Party="Republican", Order=37},
        new President{Name="Lyndon Johnson", Party="Democratic", Order=36}
    };


    public MainWindow()
    {
        InitializeComponent();

        presidentView = CollectionViewSource.GetDefaultView(presidents);
        presidentView.SortDescriptions.Add(new SortDescription("Order", ListSortDirection.Ascending));

        Predicate<object> isRepublican = (x) => 
        {
            President p = x as President;
            return p.Party == "Republican";
        };

        presidentView.Filter = isRepublican;

        list.ItemsSource = presidentView;
        grid.ItemsSource = presidents;
    }
}

public class President
{
    public int Order { set; get; }
    public string Name { set; get; }
    public string Party { set; get; }
}


<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="350" Width="727.416">
    <Grid>
        <ListView HorizontalAlignment="Left" Height="260" Margin="10,10,0,0" Name="list" VerticalAlignment="Top" Width="197">
            <ListView.ItemTemplate>
                <ItemContainerTemplate>
                    <TextBlock Text="{Binding Path=Name}">                        
                    </TextBlock>
                </ItemContainerTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <DataGrid Name="grid" HorizontalAlignment="Left" Margin="224,13,0,0" VerticalAlignment="Top" Height="257" Width="487"/>
    </Grid>
</Window>

Solution

  • CollectionViewSource.GetDefaultView(object) returns the same ICollectionView instance for the given source -- which will be used for any ItemsControl (the DataGrid) when displaying the source collection (presidents).

    You can get around this by creating a new instance of ICollectionView to be used by each control that you want independent from other controls (typically, a different one for each different filter).

    Update your presidentView to be instantiated like this:

    public MainWindow()
    {
        InitializeComponent();
    
        presidentView = new CollectionViewSource { Source= presidents }.View;
        presidentView.SortDescriptions.Add(new SortDescription("Order", ListSortDirection.Ascending));
    
        Predicate<object> isRepublican = (x) =>
        {
            President p = x as President;
            return p.Party == "Republican";
        };
        presidentView.Filter = isRepublican;
    
        list.ItemsSource = presidentView;
        grid.ItemsSource = presidents;
    }