One of the exciting new features in the Silverlight 4 beta is a pair of properties added to the ButtonBase and Hyperlink classes named Command and CommandParameter. These properties represent the first real support in Silverlight for commanding, which is primarily beneficial to to Model-View-ViewModel (MVVM) applications. MVVM has long been popular among WPF developers and is rapidly gaining popularity with Silverlight developers. And now, thanks to commanding, Silverlight comes a little closer to supporting pure MVVM implementations without help from auxiliary frameworks such as Prism.
To demonstrate Silverlight 4’s new commanding infrastructure, I took a sample that I originally wrote to show how to consume RSS in Silverlight. You type an RSS URL into a TextBox and click the “View” button. The app uses a WebClient to fetch the RSS feed, and then renders the feed items into a ListBox using data binding:
I then rewrote the app as an MVVM app and took advantage of commanding to move all the logic that used to appear in the code-behind to the view-model. You can download the source code to see how it works. Here are some of the highlights.
First, my model is implemented in the RssModel class. RssModel exposes a method named GetRssFeedAsync that the view-model can use to fetch a live RSS feed. It also implements a couple of events: an RssLoadComplete event that’s fired following a successful RSS download, and an RssLoadError event that’s fired if an RSS download fails. These methods and events are part of an interface named IRssProvider, which you could mock in a unit-testing scenario to provide RSS content from a local source rather than a network source.
Second, my view-model is implemented in a class named RssViewModel. Part of the intent of the view-model is to provide properties that the view can bind to to populate controls with data. To that end, RssViewModel exposes a string property named RssUri which can be bound to the TextBox in the view, and an ObservableCollection property named RssFeed that can be bound to the ListBox. It also implements a read-only ICommand property named GetRssFeed that returns an instance of a class named GetRssFeedCommand. This is where commanding enters the picture in Silverlight 4.
Open the code-behind file for the view (RssView.xaml.cs) and you’ll see that it contains no code other than a constructor that calls InitializeComponent. In Silverlight 3, I would have had to include a handler for the button’s Click command that calls into the view-model to fetch the RSS feed and stuff the RSS items into the RssFeed property. Thanks to the new commanding infrastructure in Silverlight 4, I was able to accomplish this declaratively. First, the view declares an instance of the view-model:
<UserControl.Resources>
<local:RssViewModel x:Key=”RssViewModel” />
</UserControl.Resources>
Then it binds the view-model to the DataContext property of the Grid that contains the TextBox, ListBox, and Button:
<Grid x:Name=”LayoutRoot” Background=”#FFF4B3FA” DataContext=”{StaticResource RssViewModel}”>
The TextBox’s Text property and the ListBox’s ItemsSource property are bound to the view-model’s RssUri and RssFeed properties:
<TextBox Text=”{Binding RssUri, Mode=TwoWay}” … />
<ListBox ItemsSource=”{Binding RssFeed}” … >
More importantly, the Button is bound to the view-model this way:
<Button Command=”{Binding GetRssFeed}” CommandParameter=”{Binding RssUri}” … />
Observe that the Button declaration doesn’t contain a Click attribute; in other words, the Click event isn’t bound to an event handler in the code-behind. Instead, when the button is clicked, it invokes the command exposed through the view-model’s GetRssFeed property. That command is implemented this way:
public class GetRssFeedCommand : ICommand
{
private IRssProvider _model;
private ObservableCollection<SyndicationItem> _feed;
public GetRssFeedCommand(ObservableCollection<SyndicationItem> feed, IRssProvider model)
{
_feed = feed;
_model = model;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_model.RssLoadComplete += new EventHandler<RssEventArgs>(OnRssLoadComplete);
_model.GetRssFeedAsync(new Uri((string)parameter, UriKind.Absolute));
}
void OnRssLoadComplete(object sender, RssEventArgs e)
{
if (_feed != null) // Just to be sure
{
_feed.Clear();
using (XmlReader reader = XmlReader.Create(e.Result))
{
SyndicationFeed feed = SyndicationFeed.Load(reader);
foreach (SyndicationItem item in feed.Items)
_feed.Add(item);
}
}
}
}
At run-time, invoking the command causes its Execute method to be called, and the command responds by using the model to fetch the RSS feed and parsing the feed items into the view-model’s RssFeed property. Thanks to the data binding between that property and the ListBox, and because RssFeed is an ObservableCollection, the items promptly appear in the ListBox.
The binding between the Button and the view-model’s GetRssFeed property is possible because of the Command property added to the Button class (actually, to ButtonBase) in Silverlight 4. In addition, Button now contains a CommandParameter property that can be used to pass a parameter to the command. In this example, CommandParameter is bound to the view-model’s RssUri property, which ensures that the URL typed by the user is available to the command when the command is invoked.
The upshot of all this is that Command and CommandParameter enabled me to build a pure MVVM implementation that wasn’t possible in Silverlight 3 without outside help. It’s not the final word in commanding—after all, Silverlight still lacks commanding support in other controls such as ListBox—but it’s a step in the right direction. And it’s indicative of the fact that the Silverlight team is listening to developers, because commanding, along with printing and support for webcams and other devices, is one of the features that developers lobbied for the most.