filtering collections from xaml using collectionviewsource
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.
This is an imported post. It was imported from my old blog using an automated tool and may contain formatting errors and/or broken images.
Leave a Comment