Scott Hanselman

CollectionViewSource is crazy useful for binding to filtered Observable Collections on Windows Phone 8

October 21, '13 Comments [8] Posted in WinPhone
Sponsored By

I've been working on this Windows Phone 8 app on the side (it's a news app, but mark my words, it's gonna be huge). 

For the initial development I've been binding to a Pivot to a basic ObservableCollection of type "FeedItem," so basically my XAML was like this.

Really, the only thing you care about here is that FIRST line...ItemsSource=""

<phone:Pivot Title="MAGICAL FREAKING NEWS" x:Name="MainPivot" 
ItemsSource="{Binding Path=NewsData.Feeds}" >
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=key}"/>
</DataTemplate>
</phone:Pivot.HeaderTemplate>

<phone:Pivot.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Text="{Binding Path=title}" Style="{StaticResource PhoneTextTitle2Style}" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Path=updated_at, StringFormat=F}" HorizontalAlignment="Center" TextWrapping="Wrap" />
<Button x:Name="PlayButton" Margin="0,40,0,0" HorizontalAlignment="Center" Style="{StaticResource PlayStyle}" Click="Play_Click">
<Button.RenderTransform>
<!-- Changes to .5 when in Landscape -->
<ScaleTransform x:Name="OrientationScale" ScaleX="1" ScaleY="1" CenterX="60" CenterY="0"/>
</Button.RenderTransform>
</Button>
</StackPanel>

...snip...you get the idea.

As an aside, I really like the idea of Design Time Data, meaning that I can layout my page in Visual Studio and it will actually LOOK like my app using static data that happens at Design Time. For this, I just add this at the PhoneApplicationPage level:

 d:DataContext="{d:DesignData Content/NewsDataSample.xaml}"

And that sample XAML file just looks like a "XAML-shaped" version of my Object Model, which is actually fed by JSON at runtime:

<vm:AppViewModel
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:toawesometolive_portable.Data;assembly=icantstandit-portable"
    OperationInProgress="True"
    >
    <vm:AppViewModel.NewsData Force="False">
        <vm:NewsData>
            <vm:NewsData.Feeds>
                <vm:Feed id="0" key="NPR" subtitle="National Public Radio"
...snip...

Fast forward some and requirements changed. Now I needed to be able to individually enable and disable new sources, as well as reorder sources on the fly.

image

That means, if a source is not enabled, it shouldn't be in the first page's pivot. The Sources page and the Main Pivot both bind to the same ObservableCollection, except I need a filter of some kind of the Pivot for "where enabled == true."

I tried lots of stuff things like other sub-collections, properties that returned filtered collections, IQueryable this and that, then discovered the (not really well documented) CollectionViewSource.

Turns out, though, that WPF folks have been using this for YEARS. Here's Beth Massi talking about CollectionViewSource in 2008, for crying out loud (as I discover it a half-decade later on the phone.)

You can have more than one CollectionViewSource on your page. You can use them with Master/Detail forms, for Pathing and for Filtering, which is what I'm interested in. It's also nice because any controls that you bind to the same CollectionViewSource will always have the same current item.

I put one in my Phone Page's Resources like this:

<phone:PhoneApplicationPage.Resources>
    <CollectionViewSource x:Key="src" Source="{Binding Path=NewsData.Feeds}"/>
</phone:PhoneApplicationPage.Resources>

Later, my Pivot binds to the CollectionViewSource by name:

ItemsSource="{Binding Source={StaticResource src}}"

I know folks love to do EVERYTHING in their XAML, but that's not how I roll. (Nor do I have any idea what I'm doing.)

In my page's code behind I set a filter:

collectionView.Source = App.ViewModel.NewsData.Feeds;
collectionView.View.Filter = item =>
{
Feed f = item as Feed;
if (f == null) return false;
if (!f.enabled) return false;
return true;
};

That's it. Now I can enable and disable my items in my source view and the Pivot updates nicely only showing those news sources that are enabled.

Ideally I would have been able to express this somehow in XAML with some kind of where clause as an attribute, but once I figured this solution out it worked famously. I suspect there's actually a LOT of depth to CollectionViewSource and I may end up using it in other parts of my app.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Monday, October 21, 2013 4:32:00 AM UTC
:)
bill
Monday, October 21, 2013 6:35:33 AM UTC
Alternatively you could use a DataTemplateSelector and conditionally display items based on what filters you may need
Monday, October 21, 2013 6:37:36 AM UTC
I'm sure you've heard of ReactiveUI. An framework that allows you to do the bindings in a reactive TYPESAFE way. It also has the possibility to create derived lists that can be filtered & ordered. Much cooler than collectionviewsource imo. doing everything in XAML is a very bad idea.
phil
Monday, October 21, 2013 6:38:16 AM UTC
Keep in mind though that CollectionViewSource has severe performance problems. Any filtering in code behind is much faster. Further filtering is not available in the CollectionViewsource of Windows 8 so you have to implement filtering in your code behind in any way if you want to go portable.
Amenti
Monday, October 21, 2013 7:19:57 AM UTC
Hey Scott,

If you like design time data (and who wouldn't, it's awesome), you should check how we do it in MVVM Light. Instead of having to flatten your object model to XAML, you can use code to create the design time data using the actual model objects of your app. This makes maintaining the design time data easier.

Some pointers:
The basics
Advanced
Additional material

Enjoy!
Laurent
Monday, October 21, 2013 7:47:00 AM UTC
I like CollectionViewSource. Too bad that LonglistSelector doesn't work with CVS.
Monday, October 21, 2013 8:33:31 AM UTC
I'm surprised you are doing this native rather than using web tech, just given your background. Did you evaluate Cordova/PhoneGap and discount it?
Tuesday, October 22, 2013 2:29:44 AM UTC
Problem is, CollectionViewSource and its subclasses are DependencyObjects, thus have thread affinity, thus are a PITA to test and in highly asynchronous scenarios. ReactiveUI's reactive collections is where it's at for me.
Kent Boogaart
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.