CollectionViewSource is crazy useful for binding to filtered Observable Collections on Windows Phone 8
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.
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.
About Newsletter
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
Comments are closed.