Data Binding Pivot Control WP7 MVVM
by Peter Daukintis
Ok, having had access to the official WP7 Pivot control in the RTM Tools for Windows Phone 7 for a few days I thought it was time to explore and particularly in it’s data binding capabilities. I knocked up a quick and dirty example for a forum response but felt the need to explore a bit further…
The example consisted of binding the Pivot control to a homogenous view model collection. All well and good but in real life the requirements would be more like handling a heterogenous data source and binding the page headers, etc..
So, we start with the control bound to a PageCollection
<controls:Pivot ItemsSource="{Binding PageCollection}" Title="MY APPLICATION" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
The PageCollection is defined as follows:
public class MainViewModel : INotifyPropertyChanged { private ObservableCollection<PageViewModel> _pageCollection; public ObservableCollection<PageViewModel> PageCollection { get { return _pageCollection; } set { if (_pageCollection != value) { _pageCollection = value; NotifyPropertyChanged("PageCollection"); } } }
However, we are going to want to have differing objects in this collection so I will also create some PageViewModel derived types:
public class PageViewModel : INotifyPropertyChanged { private string _titleText; public string TitleText { get { return _titleText; } set { if (_titleText != value) { _titleText = value; OnPropertyChanged("TitleText"); } } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } public class ListPageViewModel : PageViewModel { private ObservableCollection<string> _contextCollection; public ObservableCollection<string> ContextCollection { get { return _contextCollection; } set { if (_contextCollection != value) { _contextCollection = value; OnPropertyChanged("ContextCollection"); } } } } public class ImagePageViewModel : PageViewModel { private Uri _uri; public Uri Uri { get { return _uri; } set { if (_uri != value) { _uri = value; OnPropertyChanged("Uri"); } } } }
One has a list of strings and another has a Uri which will define the location of an image.
In my main view model constructor I will create the runtime data (consisting of one instance of each type)
public MainViewModel() { PageCollection = new ObservableCollection<PageViewModel> { new ListPageViewModel { TitleText = "List Page", ContextCollection = new ObservableCollection<string> { "item1", "item2" } }, new ImagePageViewModel { TitleText = "Image Page", Uri = new Uri("Images/pic.png", UriKind.Relative) } }; }
And to bind the page header I’ll add a header template
<controls:Pivot ItemsSource="{Binding PageCollection}" Title="MY APPLICATION" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" > <controls:Pivot.HeaderTemplate> <DataTemplate> <Grid x:Name="grid"> <TextBlock TextWrapping="Wrap" Text="{Binding TitleText}" d:LayoutOverrides="Width, Height" /> </Grid> </DataTemplate> </controls:Pivot.HeaderTemplate> </controls:Pivot>
and we’re also going to need a data template selector to allow us to select a data template at runtime depending on the type of the instance we are binding. This is a little tricky since this is not natively supported in Silverlight but fortunately not too hard to implement. I drew upon this http://geekswithblogs.net/tkokke/archive/2009/09/28/datatemplateselector-in-silverlight.aspx and http://forums.silverlight.net/forums/t/190667.aspx to implement the following (see those posts for explanation):
public static class Extensions { public static T FindResource<T>(this DependencyObject initial, string key) where T : DependencyObject { DependencyObject current = initial; while (current != null) { if (current is FrameworkElement) { if ((current as FrameworkElement).Resources.Contains(key)) { return (T)(current as FrameworkElement).Resources[key]; } } current = VisualTreeHelper.GetParent(current); } if (Application.Current.Resources.Contains(key)) { return (T)Application.Current.Resources[key]; } return default(T); } } public class DataTemplateSelector : ContentControl { protected override void OnContentChanged(object oldContent, object newContent) { ContentTemplate = this.FindResource<DataTemplate>(newContent.GetType().FullName); } }
which then allowed me to declare the ItemTemplate for the Pivot control like this:
<controls:Pivot.ItemTemplate> <DataTemplate> <WindowsPhonePivotApplication1:DataTemplateSelector Content="{Binding}" /> </DataTemplate> </controls:Pivot.ItemTemplate>
and to add the data templates (one for each derived type) like this:
<phone:PhoneApplicationPage.Resources> <DataTemplate x:Key="WindowsPhonePivotApplication1.ViewModels.ListPageViewModel"> <ListBox ItemsSource="{Binding ContextCollection}"> </ListBox> </DataTemplate> <DataTemplate x:Key="WindowsPhonePivotApplication1.ViewModels.ImagePageViewModel"> <Image Source="{Binding Uri}"></Image> </DataTemplate>
and that was it – here is the result…
Technorati Tags: Pivot,Control,MVVM,Beta,data,example,forum,response,collection,life,requirements,PageCollection,ItemsSource,Title,APPLICATION,HorizontalContentAlignment,Stretch,VerticalContentAlignment,MainViewModel,ObservableCollection,PageViewModel,_pageCollection,objects,_titleText,TitleText,event,PropertyChangedEventHandler,PropertyChangedEventArgs,ListPageViewModel,_contextCollection,ContextCollection,ImagePageViewModel,_uri,location,image,instance,List,Page,Images,Relative,header,template,HeaderTemplate,DataTemplate,Grid,Name,TextBlock,Wrap,Text,LayoutOverrides,Width,archive,explanation,FindResource,DependencyObject,FrameworkElement,Resources,VisualTreeHelper,GetParent,Current,DataTemplateSelector,ContentControl,ContentTemplate,GetType,FullName,ItemTemplate,Content,PhoneApplicationPage,ViewModels,ListBox,Source,result,forums,Extensions,templates,propertyName,runtime,aspxWindows Live Tags: Pivot,Control,MVVM,Beta,data,example,forum,response,collection,life,requirements,PageCollection,ItemsSource,Title,APPLICATION,HorizontalContentAlignment,Stretch,VerticalContentAlignment,MainViewModel,ObservableCollection,PageViewModel,_pageCollection,objects,_titleText,TitleText,event,PropertyChangedEventHandler,PropertyChangedEventArgs,ListPageViewModel,_contextCollection,ContextCollection,ImagePageViewModel,_uri,location,image,instance,List,Page,Images,Relative,header,template,HeaderTemplate,DataTemplate,Grid,Name,TextBlock,Wrap,Text,LayoutOverrides,Width,archive,explanation,FindResource,DependencyObject,FrameworkElement,Resources,VisualTreeHelper,GetParent,Current,DataTemplateSelector,ContentControl,ContentTemplate,GetType,FullName,ItemTemplate,Content,PhoneApplicationPage,ViewModels,ListBox,Source,result,forums,Extensions,templates,propertyName,runtime,aspx
Comments