Using ICommand – Silverlight 4
Another article in the ‘- Silverlight 4’ series (read the first two here: DragNDrop, Right-Click Save As and Local File Access). In this one we’ll talk about using the ICommand feature, now implemented in Silverlight. Read the announcement in Tim Heuer’s own words here.
The commanding infrastructure gives us a methodology to develop in pattern-based model like MVVM. Even though I’ve built applications following the MVVM pattern using Prism (previously blogged here), the inclusion of commanding in Silverlight, makes it even easier to develop modular applications.
In order to get to know more about how to use ICommand in Silverlight, I built this app that reads RSS Feeds from a few blog sites. Let’s dig in (here's the full code):
Here’s a screen shot of the UI:
The user chooses how many items need to be shown, selects the feed from the combo box and clicks on the ‘Get RSS’ button. Two things happen: first, the feed name gets displayed above the list box and the items get displayed in the list box.
I’ll start with my Model:
1: public class FeedModel : INotifyPropertyChanged
2: {
3: private int showOnlyTopN;
4: public int ShowOnlyTopN
5: {
6: get
7: {
8: return showOnlyTopN;
9: }
10: set
11: {
12: showOnlyTopN = value;
13: OnPropertyChanged("ShowOnlyTopN");
14: }
15: }
16:
17: private int selectedIndex;
18: public int SelectedIndex
19: {
20: get
21: {
22: return selectedIndex;
23: }
24: set
25: {
26: selectedIndex = value;
27: OnPropertyChanged("SelectedIndex");
28: }
29: }
30:
31: private string selectedFeedName;
32: public string SelectedFeedName
33: {
34: get
35: {
36: return selectedFeedName;
37: }
38: set
39: {
40: selectedFeedName = value;
41: OnPropertyChanged("SelectedFeedName");
42: }
43: }
44:
45: private List<Feed> feedList;
46: public List<Feed> FeedList
47: {
48: get
49: {
50: return feedList;
51: }
52: set
53: {
54: feedList = value;
55: OnPropertyChanged("FeedList");
56: }
57: }
58:
59: private List<string> displayFeedList;
60: public List<string> DisplayFeedList
61: {
62: get
63: {
64: return displayFeedList;
65: }
66: set
67: {
68: displayFeedList = value;
69: OnPropertyChanged("DisplayFeedList");
70: }
71: }
72:
73: public event PropertyChangedEventHandler PropertyChanged;
74: private void OnPropertyChanged(string propertyName)
75: {
76: if (PropertyChanged != null)
77: {
78: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
79: }
80: }
81: }
Just the regular stuff; implements the INotifyPropertyChanged interface and the PropertyChanged event is called anytime there’s a change to the public properties of the class. Next step – the ViewModel class:
1: public class FeedViewerViewModel
2: {
3: public FeedModel FeedModel { get; set; }
4:
5: public FeedViewerViewModel()
6: {
7: // initialize the model and give some default values
8: FeedModel = new FeedModel();
9: FeedModel.ShowOnlyTopN = 10;
10: FeedModel.SelectedIndex = -1;
11:
12: List<Feed> feedList = new List<Feed>();
13: feedList.Add(new Feed
14: {
15: FeedName = "Arun's Blog",
16: FeedUrl = "http://feeds.feedburner.com/nmarun"
17: });
18:
19: FeedModel.FeedList = feedList;
20: }
21:
22: public void GetRssFeed()
23: {
24: // this method makes the async call to pull the RSS data
25: Feed selectedFeed = (Feed) FeedModel.FeedList[FeedModel.SelectedIndex];
26: // assign the selected feed name to the model's property
27: // this is what gets shown on the UI (the blue text)
28: FeedModel.SelectedFeedName = selectedFeed.FeedName;
29:
30: WebClient feedsReaderClient = new WebClient();
31: feedsReaderClient.OpenReadCompleted += FeedsReaderClientOpenReadCompleted;
32: feedsReaderClient.OpenReadAsync(new Uri(selectedFeed.FeedUrl));
33: }
34:
35: void FeedsReaderClientOpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
36: {
37: if (e.Error == null)
38: {
39: // read in the result
40: XElement xElement = XElement.Load(e.Result);
41:
42: // get only 'Top N' item titles
43: var displayableFeeds = (from item in xElement.Descendants("item")
44: select item.Element("title").Value).Take(FeedModel.ShowOnlyTopN)
45: .ToList<string>();
46: FeedModel.DisplayFeedList = displayableFeeds;
47: }
48: }
49:
50: // implementing the ICommand
51: public ICommand GetRss
52: {
53: get { return new GetRssCommand(this); }
54: }
55: }
Ok, a little bit of explanation for this one. The constructor initializes an instance of FeedModel. The GetRssFeed() and FeedsReaderClientOpenReadCompleted() methods are the ones that actually get the feed items and assign it to the DisplayFeedList (the data in the list box).
The public property GetRss is how commanding gets implemented. It returns an instance of ICommand that passes the current ViewModel as a constructor parameter. So let’s see what this GetRssCommand class does:
1: public class GetRssCommand : ICommand
2: {
3: private FeedViewerViewModel _feedViewerViewModel;
4:
5: public GetRssCommand(FeedViewerViewModel feedViewerViewModel)
6: {
7: _feedViewerViewModel = feedViewerViewModel;
8: }
9:
10: public bool CanExecute(object parameter)
11: {
12: if(_feedViewerViewModel.FeedModel.SelectedIndex != -1)
13: {
14: return true;
15: }
16: else
17: {
18: return false;
19: }
20: }
21:
22: public event EventHandler CanExecuteChanged;
23:
24: public void Execute(object parameter)
25: {
26: _feedViewerViewModel.GetRssFeed();
27: }
28: }
The class implements ICommand and provides implementation for the CanExecute() and Execute() methods. The CanExecute() method defines whether the command can execute in its current state and the Execute() method defines what method needs to be called when the command is invoked. Although we don’t need the CanExecuteChanged event handler, we need to declare it to comply with ‘interface-implementation’ rules.
The coding part is pretty much done here. Now let’s see what we need to do on the .xaml file to make all of this work smoothly.
First, declare your ViewModel as a resource on the page:
1: <UserControl.Resources>
2: <my:FeedViewerViewModel x:Key="FeedViewerDataContext" />
3: </UserControl.Resources>
Next, declare the DataContext of your base control (Grid, StackPanel, etc). Instead of typing this up, let’s use Visual Studio to set this for us. Click on the <Grid line in your xaml (or click on the Grid in the design page) and you’ll see the properties of the Grid in the Properties window. Now click on ‘DataContext’ under ‘Common’ section of the properties window and select ‘Apply Data Binding…’ from the menu. This pops up a window. Click on ‘UserControl.Resources’ and then on ‘FeedViewerDataContext’ in the Source category.
The resulting code looks like:
1: <Grid x:Name="LayoutRoot" Background="White" Height="300" VerticalAlignment="Top"
2: DataContext="{Binding Source={StaticResource FeedViewerDataContext}}">
Once this step is done, we can bind other controls to our Model through UI… no more guessing around, thank you Visual Studio!!. Here’s how I bound the ItemsSource property of the combo box to FeedList property:
And then the Command property of the Button
The emitted xaml looks like this after the CommandParameter also has been set.
1: <Button Content="Get RSS" Height="23" HorizontalAlignment="Left" Margin="313,41,0,0"
2: Name="GetRssButton" VerticalAlignment="Top" Width="75"
3: Command="{Binding Path=DataContext.GetRss, ElementName=LayoutRoot}"
4: CommandParameter="{Binding Path=SelectedItem, ElementName=FeedsComboBox}"/>
After all this, what does the code-behind file for the .xaml page look like:
In other words… CLEAN!!.
That’s pretty much it. Download the complete code for the application here.