#989 – Enabling Live Filtering in a CollectionViewSource

Like sorting, filtering in a CollectionViewSource is not automatically done when you change the contents of one of the data bound items.  By default, you need to call the Refresh method of the CollectionViewSource.

In the example below, we filter on first name “Joan” and then change Joan Fontaine’s first name to “Bob”.  Notice that the list is not re-filtered–Bob remains in the list.

989-001

989-002

You can fix this by adding the FirstName property to the LiveFilteringProperties collection of the CollectionViewSource and setting IsLiveFilteringRequested to true.

        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}"
                              IsLiveFilteringRequested="True">
            <CollectionViewSource.LiveFilteringProperties>
                <clr:String>FirstName</clr:String>
            </CollectionViewSource.LiveFilteringProperties>
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>

Now, when we change Joan to Bob and we’re filtering on “Joan”, Bob automatically disappears from the list.

989-003

#988 – Enabling Live Sorting in a CollectionViewSource

By default, when you’re using a CollectionViewSource to do sorting, grouping and filtering in a list-based control, the sorting/grouping/filtering behavior will only updated when you explicitly refresh the CollectionViewSource (by calling Refresh) or when you add or remove something to the collection.

You can enable live sorting in the CollectionViewSource to cause it to resort items when one or more properties on the bound objects change.  In the example below, we set the IsLiveSortingRequested property to true and specify that the Actor.LastName property is the property to live sort on.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}"
                              IsLiveSortingRequested="True">
            <CollectionViewSource.LiveSortingProperties>
                <clr:String>LastName</clr:String>
            </CollectionViewSource.LiveSortingProperties>
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>

Now when we make a change to the last name of one of the actors, the sorting is updated.

988-001

 

988-002

#987 – CollectionViewSource Updates on Refresh or Change to Collection

By default, when you’re using a CollectionViewSource to do sorting, grouping and filtering in a list-based control, the sorting/grouping/filtering behavior will only updated when you explicitly refresh the CollectionViewSource (by calling Refresh) or when you add or remove something to the collection.

For example, if we add an actor to a list of actors and we are sorting by last name, the actor will be inserted at the correct spot.

987-001

 

987-002

 

If you change the contents of one of the bound objects, however, the sorting behavior is not updated.  For example, if we change the last name of “Jennifer Jones” to “Garner” and we do not call the Refresh method of the CollectionViewSource, the sorting will be wrong.

987-003

#986 – Filtering a ListBox Using a CollectionViewSource

When populating a ListBox from a CollectionViewSource, you can also filter the data.  Below is an example of a ListBox that is bound to a CollectionViewSource.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}" >
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <StackPanel>
        <ListBox Name="lbActors" Margin="15" Width="200" Height="200"
                 ItemsSource="{Binding Source={StaticResource cvsActors}}"/>
        <CheckBox Content="Only Joans" IsChecked="{Binding OnlyJoans}"
                  Margin="15"/>
    </StackPanel>

In code, we add a handler for the Filter event of the CollectionViewSource.  The handler is called for each item in the list.

            // Requires: using System.Windows.Data
            ((CollectionViewSource)this.Resources["cvsActors"]).Filter += ActorList_Filter;

In the handler, we set the Accepted property of the argument if the item should be included in the list.

        void ActorList_Filter(object sender, FilterEventArgs e)
        {
            // Set e.Accepted to true to include the item in the list
            if (!onlyJoans)
                e.Accepted = true;
            else
            {
                Actor a = e.Item as Actor;
                e.Accepted = (a.FirstName == "Joan") ? true : false;
            }
        }

986-001
986-002

We also have to make sure to “refresh” the CollectionViewSource when the OnlyJoans property changes.  This will trigger it to re-filter the collection.

        private bool onlyJoans;
        public bool OnlyJoans
        {
            get { return onlyJoans; }
            set
            {
                if (value != onlyJoans)
                {
                    onlyJoans = value;
                    RaisePropertyChanged("OnlyJoans");
                    ((CollectionViewSource)this.Resources["cvsActors"]).View.Refresh();
                }
            }
        }

#985 – Displaying Expandable Groups within a ListBox

You can group items in a ListBox using a CollectionViewSource.  You can then set the GroupStyle property of the ListBox to be an Expander control so that the groups can be expanded and collapsed.

In the example below, we group a collection of Actors by the decade of their birth.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}" >
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="DecadeBorn" />
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
            <CollectionViewSource.GroupDescriptions>
                <data:PropertyGroupDescription  PropertyName="DecadeBorn"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <StackPanel>
        <ListBox Name="lbActors" Margin="15" Width="200" Height="240"
                 ItemsSource="{Binding Source={StaticResource cvsActors}}">
            <ListBox.GroupStyle>
                <GroupStyle>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate>
                                        <Expander Header="{Binding Name}" IsExpanded="True">
                                            <ItemsPresenter />
                                        </Expander>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </ListBox.GroupStyle>
        </ListBox>
    </StackPanel>

We can now expand and collapse the groups representing an actor’s birth decade.

985-001

 

985-002

#984 – Grouping Items in a ListBox with a CollectionViewSource

Similar to how you can use a CollectionViewSource to sort a collection of items in a ListBox, you can also use the CollectionViewSource to group the items, based on a property on the underlying object that you’re binding to.

Assume that you have an Actor object that has a LastName property and a DecadeBorn property that indicates what decade an actor was born in.  You can group the actors by decade and then sort within each decade by last name as shown below.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}" >
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="DecadeBorn" />
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
            <CollectionViewSource.GroupDescriptions>
                <data:PropertyGroupDescription  PropertyName="DecadeBorn"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <StackPanel>
        <ListBox Name="lbActors" Margin="15" Width="200" Height="240"
                 ItemsSource="{Binding Source={StaticResource cvsActors}}">
            <ListBox.GroupStyle>
                <GroupStyle/>
            </ListBox.GroupStyle>
        </ListBox>
    </StackPanel>

The empty GroupStyle element will cause the group to be rendered using a string representation of the DecadeBorn property.

We sort by decade and by last name within each decade.

984-001

984-002

#983 – Using a CollectionViewSource to Sort Items in a ListBox

You can sort items within a ListBox using a CollectionViewSource, which is a wrapper around a view of a collection.  The CollectionViewSource provides support for sorting, filtering and grouping items in the underlying collection.  It provides a mechanism for configuring the view from XAML.

In the example below, we define a CollectionViewSource that wraps a collection of Actor objects and specifies a property to sort on (the actor’s last name).  Our ListBox then binds to the CollectionViewSource rather than to the collection.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}" >
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <StackPanel>
        <ListBox Name="lbActors" Margin="15" Width="200" Height="190"
                 ItemsSource="{Binding Source={StaticResource cvsActors}}"/>
    </StackPanel>

983-001
The Actor objects in our ListBox are now sorted by their LastName. (LastName is a property of the Actor object).
983-002