Nico's digital footprint

I grew up in the nineties, that makes me awesome by default

Filtering collections from XAML using CollectionViewSource

by Nico

I find that I often run into the need of separating a collection of items into several collections just so I can bind them to multiple listboxes, for example a list of sessions spanning several tracks and each track is shown in his own listbox in a pivotitem. To get this done you can start by adding multiple collections to your viewmodel and divide the items there. However this makes your viewmodel very big in a very short time. A better way to do this is using CollectionViewSource items in XAML. Let me show you how.

First thing I did was building a demo class existing out of a title and a description, these two properties will be shown in the listbox later on. A third property is the one we’ll use to filter the data, here’s the completed class.

public class DemoClass
{
    public string Title { get; set; }
    public string Description { get; set; }
    public Pivot PivotToAppearIn { get; set; }
}

Nothing special here. Notice the Pivot instance, this is just an Enum that will be the way to filter later on.

public enum Pivot
{
    First,
    Second,
    All
}

For the demo’s purpose I’ll be creating a bunch of dummy data in the viewmodel. The project template I’ve used here is the default pivot app template in the Windows Phone 7.1.1 SDK. It comes with a bunch of dummy data, I’ve used the same data but put them in instances of the DemoClass. Those instances are put inside an ObservableCollection.

This is the viewmodel

public class MainViewModel : INotifyPropertyChanged
{
    public const string ItemsPropertyName = "Items";
    private ObservableCollection<DemoClass> items;
    public ObservableCollection<DemoClass> Items
    {
        get
        {
            return items;
        }

        set
        {
            if (items == value)
            {
                return;
            }

            items = value;
            NotifyPropertyChanged(ItemsPropertyName);
        }
    }

    public MainViewModel()
    {
        this.Items = new ObservableCollection<DemoClass>();
        LoadData();
    }

    public void LoadData()
    {
        Items.Add(new DemoClass 
        { 
            Title = "runtime one", 
            Description = "Maecenas praesent accumsan bibendum", 
            PivotToAppearIn = Pivot.First 
        });
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (null != handler)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

So we’ve got our basic bindable property here and a method that loads in the dummy data. In the demo project there’s obviously more then one item in the collection, there’s about 16 to be precise.

In the design I didn’t change a lot from the default template. I’ve just copied the ItemTemplate from the listbox to the pageresources so that it can be reused in the second listbox.

This is the template.

<phone:PhoneApplicationPage.Resources>
    <!-- template for the listboxes -->
    <DataTemplate x:Name="ListBoxTemplate">
            <StackPanel Margin="0,0,0,17" Width="432" Height="78">
                <TextBlock Text="{Binding Title}" TextWrapping="Wrap" 
                           Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                <TextBlock Text="{Binding Description}" TextWrapping="Wrap" 
                           Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
            </StackPanel>
        </DataTemplate>
</phone:PhoneApplicationPage.Resources>

All right now that the preparations are set, time to get into the filtering. First step is to add a CollectionViewSource for each listbox. These are set on the same place as I’ve put the listbox ItemTemplate, in the pageresources. For this demo I need two CollectionViewSources.

<CollectionViewSource x:Name="FirstPivot" Filter="FirstPivot_Filter" Source="{Binding Items}" />
<CollectionViewSource x:Name="SecondPivot" Filter="SecondPivot_Filter" Source="{Binding Items}" />

So what’s all this? x:Name is like in any other XAML object, it’s just the name that can be used to reference the object. The source is the ObservableCollection that was created in the viewmodel. And last but definitely not least is the Filter event. This event will fire for every item in the collection that is bound to the Source property.

Now for the event handler, I’ll just post the event handler FirstPivot_Filter here because they are basically the same.

private void FirstPivot_Filter(object sender, System.Windows.Data.FilterEventArgs e)
{
    e.Accepted = (e.Item as DemoClass).PivotToAppearIn == Model.Pivot.First || 
        (e.Item as DemoClass).PivotToAppearIn == Model.Pivot.All;
}

FilterEventArgs has two properties, Accepted is a boolean that when true shows the item in the listbox that is bound to the CollectionViewSource. Item is the current item in the collection. Remember that this event is triggered for each item in the collection. So what we do here is casting the Item property to an instance of DemoClass then check if the PivotToAppearIn property, that was an instance of the enum, is either First or All.

Now that we have the CollectionViewSources and the event handlers in place it’s time to bind the ViewSource to the listbox.

<controls:PivotItem Header="first">
    <ListBox x:Name="FirstListBox" Margin="0,0,-12,0" 
             ItemsSource="{Binding Source={StaticResource FirstPivot}}"
             ItemTemplate="{StaticResource ListBoxTemplate}" />
</controls:PivotItem>

The bindingsource of ItemsSource is bound to the CollectionViewSource that filters for this listbox. And that’s it!

 

In this article I’ve shown how you can filter a collection using a CollectionViewSource in XAML. This is an easy and fast way to visually filter data while keeping a clean ViewModel.

Download the Demo project here.


Tags:

.Net | Binding | MVVM Light | Windows 8 | XAML | WP7 | WP8 | Silverlight | Metro | Devices

blog comments powered by Disqus
  Log in

About the author

Hi,

My name is Nico, I’m an MVP Windows Platform Development living in Belgium.
I’m currently employed as a .NET consultant at RealDolmen, one of Belgium’s leading IT single source providers.

I'm also founding member and board member of the Belgian Metro App Developer Network, a user group focussed on Windows 8 and Windows Phone development. If you're in Belgium feel free to drop by if we're doing an event. http://www.madn.be

Since June 2012 I'm a proud member of Microsoft's Extended Experts Team Belgium. And in February 2013 I became a member of DZone's Most Valuable Bloggers family.

In 2013 I became a book author and wrote "Windows 8 app projects, XAML & C# edition".

In 2014 I received the MVP award for the very first time.

I hope to get feedback from my readers either through comments, mail (nico_vermeir@hotmail.com), twitter, facebook, …

 

mvp

 

 

MeetLogo

 

MVBLogo

mybook

 

Month List