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:

screen1

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.

screen2

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:

screen3

And then the Command property of the Button

screen4 screen5

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:

screen6

In other words… CLEAN!!.

That’s pretty much it. Download the complete code for the application here.

5 Comments

  • Wow this was a really well written article. I was able to easily follow it and you completely explained every aspect and did not assume that the reader "already knew" some of the aspects.

    The examples showing how to wire-up the binding in Visual Studio was very important.

  • random comments/questions:

    * OnPropertyChanged needs to assign to a local var to prevent a race condition where another thread unsubs the last listener

    * when creating feedList local var, use the collection initializer syntax. &nbsp;With that, you don't even need a local var, you can do it inline when assigning the property

    * AFAICT you don't need this cast? &nbsp;(Feed) FeedModel.FeedList[FeedModel.SelectedIndex];

    * if e.Error != null, it seems like you should show the error in a UI of some sort

    * CanExecute could just return SelectedIndex != -1

    * you don't need the type param to .ToList&lt;string&gt;() - the compiler can figure it out

    * Execute and CanExecute don't appear to use the passed-in param, so did we need to bind CommandParameter?

    * does CanExecuteChanged need to be hooked up to SelectedIndex changed? &nbsp;Why is it ok not to fire that event? &nbsp;it seems odd

  • Hi [Anomymous],
    * OnPropertyChanged needs to assign to a local var to prevent a race condition where another thread unsubs the last listener
    Not sure what exactly you're talking here. Please give an example of where it is used like the way you're talking.

    wanted to make sure I get a List and not an IEnumerable
    * you don't need the type param to .ToList() - the compiler can figure it out

    not in this case, but did it to show MS provided the CommandParam as well along with the Command property
    * Execute and CanExecute don't appear to use the passed-in param, so did we need to bind CommandParameter?

    You seem to be thinking that I'm putting this application on a prod server. No, this is for tutorial purpose only, applies for the next few points:
    * CanExecute could just return SelectedIndex != -1

    * AFAICT you don't need this cast? (Feed) FeedModel.FeedList[FeedModel.SelectedIndex];

    * if e.Error != null, it seems like you should show the error in a UI of some sort

    * when creating feedList local var, use the collection initializer syntax. With that, you don't even need a local var, you can do it inline when assigning the property

    why would I fire the event when 'nothing' is selected?
    * does CanExecuteChanged need to be hooked up to SelectedIndex changed? Why is it ok not to fire that event? it seems odd

    Hope I've answered all your questions.

  • Hello,

    i like your article it's easy to understand. I got one question. The GetRssFeed() function is implemented in the ViewModel. Wouldn't it be logical to put it in the ViewModel. Can explain this?

  • Jonam, thanks for your comment, but I'm not sure what your question is (seems like you've used ViewModel in both places).

    Arun

Comments have been disabled for this content.