In this article, I cover how to access Syndicated
Services like RSS feeds from Windows Phone 7, as well as advanced
Model-View-ViewModel (MVVM) techniques to incorporate page navigation,
showing progress, and lazy loading images. The last subsection covers
the IValueConverter interface, which allows you to data bind any data type to just about any other data.
A sample project based on the MVVM Light project template named AdvancedDataBinding is added to
solution. MVVM is leveraged for this example to demonstrate how to
handle slightly additional complexity of separating concerns between the
View and the ViewModel when dealing with more complex scenarios.
By default, the
MainPage.xaml binds to the MainViewModel in the ViewModel folder.
MainPage.xaml presents a menu of four items that navigate to individual
pages corresponding to the sections that follow:
Syndicated Services (/View/SyndicatedServices.xaml)
Showing Progress (/View/ShowingProgress.xaml)
Lazy Load Images (/View/LazyLoadImages.xaml)
Data Bind to Anything (/View/DatabindToAnything.xaml)
MainPage.xaml consists of a ListBoxdatabound to a collection of items that represent the above pages. Here is the XAML:
<ListBox x:Name="listBox1" Margin="24,0,0,0"
ItemsSource="{Binding Pages}"
ItemTemplate="{StaticResource PageItemTemplate}" />
In MainViewModel.cs, the ApplicationTitle and PageName properties are updated for the current project. A Pages property is added to MainViewModel class that is of type List<PageItemViewModel>:
public const string PagesPropertyName = "Pages";
private List<PageItemViewModel> _pages = null;
public List<PageItemViewModel> Pages
{
get
{
return _pages;
}
protected set
{
if (_pages == value)
{
return;
}
var oldValue = _pages;
_pages = value;
// Update bindings, no broadcast
RaisePropertyChanged(PagesPropertyName);
}
}
The MainViewModel class has a method named LoadPageInfo(), which instantiates the Pages collection:
private void LoadPagesInfo()
{
Pages = new List<PageItemViewModel>()
{
new PageItemViewModel(){PageTitle="syndicated services",
PageUri=new Uri("/View/SyndicatedServices.xaml",UriKind.Relative)},
new PageItemViewModel(){PageTitle="showing progress",
PageUri=new Uri("/View/ShowingProgress.xaml",UriKind.Relative)},
new PageItemViewModel(){PageTitle="lazy load images",
PageUri=new Uri("/View/LazyLoadImages.xaml",UriKind.Relative)},
new PageItemViewModel(){PageTitle="data binding to anything",
PageUri=new Uri("/View/DatabindToAnything.xaml",UriKind.Relative)}
};
}
Normally, the PageItem class would be a simple Model class; however, I made it a ViewModel class because of necessary support to allow databinding and navigation from a ListBox displaying the Pages data. When an item is selected in the ListBox shown in Figure 6-1, the application should navigate to the associated page. We don't want to write code to do this in the MainPage.xaml.cs code-behind file. Instead we take advantage of messaging and commanding support provided by the MVVM Light toolkit.
The selected item in the
ListBox on MainPage.xaml should perform navigation to the associated
page pointed to by the selected item. This means that the Commanding
support is required at the PageItem / ItemTemplate level, not at the ListBox level. To support this functionality, a ViewModel class named PageItemViewModel is added that supports GalaSoft Light Messaging and Commanding . Listing 1 shows the PageItemViewModel.
Example 1. PageItemViewModel Class File
using System; using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Messaging; using GalaSoft.MvvmLight; namespace AdvancedDataBinding.ViewModel { public class PageItemViewModel : ViewModelBase { public PageItemViewModel() { //Wire up the NavigateToPageCommand RelayCommand to send the message //for the Uri of the page to navigate to. NavigateToPageCommand = new RelayCommand<Uri>(
param => SendNavigationRequestMessage(param)); }
#region PageItem properties public const string PageTitlePropertyName = "PageTitle"; private string _pageTitle = null; public string PageTitle { get { return _pageTitle; } set { if (_pageTitle == value) { return; }
var oldValue = _pageTitle; _pageTitle = value; RaisePropertyChanged(PageTitlePropertyName); } }
public const string PageUriPropertyName = "PageUri"; private Uri _pageUri = null; public Uri PageUri { get { return _pageUri; } set { if (_pageUri == value) { return; }
var oldValue = _pageUri; _pageUri = value;
// Update bindings, no broadcast RaisePropertyChanged(PageUriPropertyName); } } #endregion
#region Commanding and Messaging public RelayCommand<Uri> NavigateToPageCommand { get;
private set; }
protected void SendNavigationRequestMessage(Uri uri) { Messenger.Default.Send<Uri>(uri, "PageNavRequest"); } #endregion } }
|
The PageItemViewModel class is broken up into two sections, one for the properties and the other for the Commanding and Messaging support. The PageItemViewModel class has two properties, PageTitle and PageUri. The PageUri points to the appropriate XAML file to navigate to for the PageTitle property value.
The Commanding and Messaging support is pretty simple code. The NavigateToPageCommand is what the MainPage.xaml View DataBinds to when the ListBoxItem is clicked via the MVVM Light EventToCommand Behavior. The NavigateToPageCommandRelayCommand<T> is connected to the MVVM Light Messaging infrastructure via this code in the PageItemViewModel constructor:
NavigateToPageCommand = new RelayCommand<Uri>(
param => SendNavigationRequestMessage(param));
The NavigateToPageCommand takes an Uri and passes it to the SendNavigationRequestMessage. This is a bit of indirection, as the Uri property comes from the ListBox's
template and then is sent back to the View via the MVVM Light Messaging
infrastructure but it does promote decoupling of the UI from the
ViewModel.
The MainPage.xaml View receives the message via this line of code added to the PhoneApplicationPage constructor and uses an inline Lambda statement to call the NavigationService:
Messenger.Default.Register<Uri>(
this, "PageNavRequest",
(uri) => NavigationService.Navigate(uri));
I mentioned earlier that the DataTemplate data binds to the NavigateToPageCommand. Here is the ItemTemplate="{StaticResourcePageItemTemplate}" for the MainPage.ListBox control:
<DataTemplate x:Key="PageItemTemplate">
<Grid Margin="0,15">
<TextBlock Margin="0,0,1,0" TextWrapping="Wrap" Text="{Binding PageTitle}"
d:LayoutOverrides="Width, Height" Style="{StaticResource PhoneTextLargeStyle}">
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="MouseLeftButtonDown">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding NavigateToPageCommand, Mode=OneWay}" CommandParameter="{Binding PageUri}"/>
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
</TextBlock>
</Grid>
</DataTemplate>
The EventToCommand behavior is dragged from the Assets | Behaviors section and is dropped on the TextBlock. Figure 2 shows the Properties window for the EventToCommand behavior.
In Figure 6-2, the Command value is databound to the NavigateToPageCommand property on the PageItemViewModel class. The CommandParameter is databound to PageUri property of the selected item from the MainViewModel Pages collection of type PageItemViewModel.
This was a
detailed walkthrough of relatively simple functionality, but it
demonstrates the powerful capabilities of MVVM Light's Commanding and
Messaging infrastructure to support decoupling UI from the ViewModel via
Silverlight's data binding framework.