5 Simple Steps to Commanding in Silverlight
Implementing ICommand in your Silverlight 4 project only requires a few steps. I have had a dozens of questions about this lately so I decided to share a very simple technique to implement commanding in Silverlight 4.
Step 1 – Implement ICommand
The first step is to implement the ICommand interface with a class that will manage the commanding aspects. There are other options to do this, but here is a simplified yet powerful implementation of ICommand.
The DelegatedCommand class implements ICommand’s CanExecute method, the Execute method, and the CaneExecuteChanged event. This code can be copied and used “as is”.
1: public class DelegateCommand : ICommand
2: {
3: Func<object, bool> canExecute;
4: Action<object> executeAction;
5: bool canExecuteCache;
6:
7: public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute)
8: {
9: this.executeAction = executeAction;
10: this.canExecute = canExecute;
11: }
12:
13: #region ICommand Members
14:
15: public bool CanExecute(object parameter)
16: {
17: bool temp = canExecute(parameter);
18:
19: if (canExecuteCache != temp)
20: {
21: canExecuteCache = temp;
22: if (CanExecuteChanged != null)
23: {
24: CanExecuteChanged(this, new EventArgs());
25: }
26: }
27:
28: return canExecuteCache;
29: }
30:
31: public event EventHandler CanExecuteChanged;
32:
33: public void Execute(object parameter)
34: {
35: executeAction(parameter);
36: }
37:
38: #endregion
39: }
Step 2 – Define the Command
Add a public property to your ViewModel to represent the ICommand. This property will be bound to your View through a button, generally.
public ICommand LoadProductsCommand { get; set; }
Step 3 – Create the Command
In the constructor of your ViewModel, set the command property you created in step 1.
LoadProductsCommand = new DelegateCommand(LoadProducts, CanLoadProducts);
Step 4 – Create the VM
You must then make sure your ViewModel is accessible in your View. This is can be done in many ways. But for simplicity I am showing the ViewModel created as a static resource in the View’s XAML.
<UserControl.Resources>
<local:ProductViewModel x:Key="vm"/>
</UserControl.Resources>
Step 5 – Bind the Command
Add a button control and bind the Command property to the command you created in the ViewModel. Then if you need to pass a parameter into the command you can bind the CommandParameter property to an element in the View. I find that I generally do not need to pass a parameter in, but I added this here as an example.
<Button Content="Load" Width="120"
Command="{Binding LoadProductsCommand}"
CommandParameter="{Binding ElementName=FilterTextBox, Path=Text}" />
That’s it! 5 simple steps and now you have commanding in your application.
Want More …
I included the key code files in the post, below. No need to dive into them unless you want to grab the code as a starting point.
The complete ProductViewModel code can be seen here:
1: public class ProductViewModel : ViewModelBase
2: {
3: public ProductViewModel()
4: {
5: this.Products = new ObservableCollection<Product>();
6:
7: // Warning: DEMO CODE AHEAD
8: // Your ViewModel should not define your data for your Model :-)
9: // Instead, it could make a call to a service to get the data for the Model.
10: this.AllProducts = new ObservableCollection<Product>();
11: this.AllProducts.Add(new Product { ProductId = 1, ProductName = "Apple" });
12: this.AllProducts.Add(new Product { ProductId = 2, ProductName = "Orange" });
13: this.AllProducts.Add(new Product { ProductId = 3, ProductName = "Banana" });
14: this.AllProducts.Add(new Product { ProductId = 4, ProductName = "Pear" });
15: this.AllProducts.Add(new Product { ProductId = 5, ProductName = "Grape" });
16: this.AllProducts.Add(new Product { ProductId = 6, ProductName = "Grapefruit" });
17: this.AllProducts.Add(new Product { ProductId = 7, ProductName = "Strawberry" });
18: this.AllProducts.Add(new Product { ProductId = 8, ProductName = "Melon" });
19: this.AllProducts.Add(new Product { ProductId = 9, ProductName = "Guava" });
20: this.AllProducts.Add(new Product { ProductId = 10, ProductName = "Kiwi" });
21: this.AllProducts.Add(new Product { ProductId = 11, ProductName = "Pineapple" });
22: this.AllProducts.Add(new Product { ProductId = 12, ProductName = "Mango" });
23:
24: LoadProductsCommand = new DelegateCommand(LoadProducts, CanLoadProducts);
25: }
26:
27: private void LoadProducts(object param)
28: {
29: string filter = param as string ?? string.Empty;
30: this.Products.Clear();
31: var query = from p in this.AllProducts
32: where p.ProductName.ToLower().StartsWith(filter.ToLower())
33: select p;
34: foreach (var item in query)
35: {
36: this.Products.Add(item);
37: }
38: }
39:
40: private bool CanLoadProducts(object param)
41: {
42: return true;
43: }
44:
45: public ICommand LoadProductsCommand { get; set; }
46:
47: public ObservableCollection<Product> AllProducts { get; set; }
48:
49: private ObservableCollection<Product> products;
50: public ObservableCollection<Product> Products
51: {
52: get
53: {
54: return products;
55: }
56: set
57: {
58: products = value;
59: this.FirePropertyChanged("Product");
60: }
61: }
62: }
63:
Here is the ViewModelBase class. It simply contains the FirePropertyChanged event that all ViewModel classes will use in my project.
1: public abstract class ViewModelBase : INotifyPropertyChanged
2: {
3: public ViewModelBase()
4: {
5: }
6:
7: public event PropertyChangedEventHandler PropertyChanged;
8:
9: protected void FirePropertyChanged(string propertyname)
10: {
11: var handler = PropertyChanged;
12: if (handler != null)
13: handler(this, new PropertyChangedEventArgs(propertyname));
14: }
15:
16: }