backgroundTop
backgroundTop
Front Page
GreenBackgroundTop

Prism Commands

How Commanding Works in Prism and MVVM

Prism Commanding allows designers/develops to specify, in XAML, events that fire back into the View Model/Presenter. Imagine this: the user hovers a mouse over a Silverlight control. Commanding allows that action to be bound to a method (more specifically, a delegate) in the ViewModel. The View Model then handles that action. This Commanding support overcomes a deficiency (lack of ICommand implementation) in Silverlight that makes proper MVVM implementation difficult. If you're using MVVM, Commanding is a must. That can take the form of a home-grown solution, Prism or any of a number of other libraries: Caliburn, Silverlight.FX, etc. In Prism Commanding, the Commands are implemented as attached properties. This video shows how to use the existing command (button click), handle command parameters, use the CanExecute functionality, and create new commands.

erikmork

6k Votes

GreenBackgroundBottom
 

Important Code

Mark as Inappropriate Content

This video is licensed under a Creative Commons License.

Creative Commons License Download the Video

Information From the Video

GreenBackgroundTop

Sample ViewModel for MVVM

This is a sample View Model for MVVM. Notice that it uses a simple service to get a list of Companies. It also has a couple of public properties that the View will use to bind to. If you're unfamiliar with the MVVM pattern, see the references for a link to the MVVM video. The idea is that the View binds to these exposed properties, and the ViewModel updates these values to have them displayed in the UI. This separation of concerns increases the testability and maintainability of the application. In this example, a Brush type is exposed. This isn't generally best practice (exposing types that derive from DependencyObject), but for the purposes of the sample, it allows for an easy visual effect.
GreenBackgroundBottom
    public class ModuleAViewModel : INotifyPropertyChanged
    {
        Brush normal = new SolidColorBrush(Colors.White);
        Brush blue = new SolidColorBrush(Colors.Blue);

        public ModuleAViewModel()
        {
            Companies = new ObservableCollection<Company>();

            HighlightItems = normal;
        }

        //Services
        public ICompanyService CompanyService { get; set; }

        //General Properties
        public ObservableCollection<Company> Companies { get; set; }
        public Brush HighlightItems { get; set; }

        //Commands





        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
 
GreenBackgroundTop

Adding the DelegateCommand

The DelegateCommand<T>is a class that implements ICommand and wraps up delegates. It's the entity that is responsible for firing the event into our ViewModel. We add it as a public property so that the View can bind against it.
GreenBackgroundBottom
        
        //Commands
        public DelegateCommand<object> GetCompanyData { get; set; }
 
GreenBackgroundTop

Instantiating the DelegateCommand

The DelegateCommand takes an Action<object> delegate. Rather than define the method as a member of the ViewModel, we'll define it as a lambda. This allows us to handle the event inline. The lambda that we define reaches out to the CompanyService, gets a list of companies and adds that to the publicly available binding property Companies.
GreenBackgroundBottom
        public ModuleAViewModel()
        {
            Companies = new ObservableCollection<Company>();


            GetCompanyData = new DelegateCommand<object>(o =>
            {
                if (CompanyService != null)
                {
                    Companies.Clear();
                    foreach (Company c in CompanyService.getCompanies())
                        Companies.Add(c);
                }
            });

            HighlightItems = normal;
        }
 
GreenBackgroundTop

Adding XAML Namespace

To use the Prism built-in command (Button click), add this namespace in the XAML.
GreenBackgroundBottom
xmlns:cal="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"

 
GreenBackgroundTop

Command Attached Property

This attached property is of type ICommand and can be bound to the DelegateCommand<T> in the ViewModel. This binding is enough to wire up the "Click" so that our ViewModel will receive the command.
GreenBackgroundBottom
        <Button Content="SimpleButton"
                HorizontalAlignment="Center"
                VerticalAlignment="Bottom"
                cal:Click.Command="{Binding GetCompanyData}"
                />
 
GreenBackgroundTop

CanExecute

The DelegateCommand has an overloaded constructor that takes a CanExecute delegate. The DelegateCommand will call this delegate (wired up to a method in the ViewModel). If that method returns false, the DelegateCommand will disable the visual element that it's bound to (it will do this by setting the item's "IsEnabled" property). In this case, returning false causes the button to be greyed out. The sample code returns "true" to demonstrate the most common scenario (keeping the Visual Element enabled).
GreenBackgroundBottom
            GetCompanyData = new DelegateCommand<object>(o =>
                {
                    if (CompanyService != null)
                    {
                        Companies.Clear();
                        foreach (Company c in CompanyService.getCompanies())
                            Companies.Add(c);
                    }
                }, o =>
                {
                    return true;
                });
 
GreenBackgroundTop

Command Parameters

When a Command is bound to a visual element, we sometimes want to pass data when the event is fired. An example of this is when there are multiple buttons firing the same DelegateCommand, we want to be able to sort out which button was clicked. Command Parameters allow us to specify in the XAML, or indeed as an attached property, the parameter to pass when the DelegateCommand fires. Adding a Command Parameter is as simple as adding a CommandParameter attached property.
GreenBackgroundBottom
        <Button Content="SimpleButton"
                HorizontalAlignment="Center"
                VerticalAlignment="Bottom"
                cal:Click.Command="{Binding GetCompanyData}"
                cal:Click.CommandParameter="SimpleButton"
                />
 
GreenBackgroundTop

Adding Commands

Out of the box, Prism only includes Button Click Commands. While Button Click is useful, it's often necessary to create our own Commands.
GreenBackgroundBottom
 
GreenBackgroundTop

Behavior Class

The Behavior class is the class that wires up to a visual element's events. The Behavior class derives from a Prism helper class called the CommandBehaviorBase<T> class. The Behavior constructor is passed a reference to the visual element, and after forwarding this to the base class, the Behavior class is free to wire up events. In this case, we're interested in the MouseEnter event for a Control.
GreenBackgroundBottom
    public class MouseOverBehavior : CommandBehaviorBase<Control>
    {
        public MouseOverBehavior(Control element)
            : base(element)
        {
            element.MouseEnter += new MouseEventHandler(element_MouseEnter);

        }

        void element_MouseEnter(object sender, MouseEventArgs e)
        {
            base.ExecuteCommand();

        }


    }
 
GreenBackgroundTop

CommandBehaviorBase

The Prism CommandBehaviorBase class contains helpers for implementing the Behavior. It has public properties for both the ICommand and the CommandParameter (of type object). It's a generic type that will accept Control or derived classes.
GreenBackgroundBottom
    public class CommandBehaviorBase<T> where T : System.Windows.Controls.Control
    {
        public CommandBehaviorBase(T targetObject);

        public ICommand Command { get; set; }
        public object CommandParameter { get; set; }
        protected T TargetObject { get; }

        protected virtual void ExecuteCommand();
        protected virtual void UpdateEnabledState();
    }
 
GreenBackgroundTop

Attached Properties

Attached properties are required for injecting the Behavior into a visual element. Confusingly, two attached properties are required. There's an ICommand property called "Command", and this property is the one that the ViewModel binds against. When this property is set (via the Binding), it looks to see if the Behavior class is already attached, and if it isn't, it creates and attaches one. That is, the attached property creates and attaches another attached property. The thing to remember is that the Behavior is not set in the XAML. Rather, it is almost invisible. It's there to negotiate the event firings and bring together the Commands and CommandParameters (not shown in this sample).
GreenBackgroundBottom
    public static class MouseOver
    {
        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(MouseOver), new PropertyMetadata(OnSetCommandCallback));


        private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            Control element = dependencyObject as Control;
            if (element != null)
            {
                MouseOverBehavior behavior = GetOrCreateBehavior(element);
                behavior.Command = e.NewValue as ICommand;
            }
        }

        private static MouseOverBehavior GetOrCreateBehavior(Control element)
        {
            MouseOverBehavior behavior = element.GetValue(MouseOverCommandBehaviorProperty) as MouseOverBehavior;
            if (behavior == null)
            {
                behavior = new MouseOverBehavior(element);
                element.SetValue(MouseOverCommandBehaviorProperty, behavior);
            }

            return behavior;
        }



        public static MouseOverBehavior GetMouseOverCommandBehavior(DependencyObject obj)
        {
            return (MouseOverBehavior)obj.GetValue(MouseOverCommandBehaviorProperty);
        }

        public static void SetMouseOverCommandBehavior(DependencyObject obj, MouseOverBehavior value)
        {
            obj.SetValue(MouseOverCommandBehaviorProperty, value);
        }

        // Using a DependencyProperty as the backing store for MouseOverCommandBehavior.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MouseOverCommandBehaviorProperty =
            DependencyProperty.RegisterAttached("MouseOverCommandBehavior", 
            typeof(MouseOverBehavior), typeof(MouseOver), null);
    }
 
GreenBackgroundTop

New DelegateCommand

This new event requires that we add a DelegateCommand in the ViewModel. This DelegateCommand will be fired whenever a user mouses over a specific control.
GreenBackgroundBottom
    public class ModuleAViewModel : INotifyPropertyChanged
    {
        Brush normal = new SolidColorBrush(Colors.White);
        Brush blue = new SolidColorBrush(Colors.Blue);

        public ModuleAViewModel()
        {
            Companies = new ObservableCollection<Company>();

            GetCompanyData = new DelegateCommand<object>(o =>
                {
                    if (CompanyService != null)
                    {
                        Companies.Clear();
                        foreach (Company c in CompanyService.getCompanies())
                            Companies.Add(c);
                    }
                }, o =>
                {
                    return true;
                });


            MousedOver = new DelegateCommand<object>(itemMousedOver);

            HighlightItems = normal;
        }

        //Services
        public ICompanyService CompanyService { get; set; }

        //General Properties
        public ObservableCollection<Company> Companies { get; set; }
        public Brush HighlightItems { get; set; }

        //Commands
        public DelegateCommand<object> GetCompanyData { get; set; }
        public DelegateCommand<object> MousedOver { get; set; }


        public void itemMousedOver(object o)
        {
            HighlightItems = blue;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("HighlightItems"));
        }



        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
 
GreenBackgroundTop

New Namespace

In the XAML, we need a new namespace to refer to the attached properties. In this case, the properties and Behavior were added to the Infrastructure project.
GreenBackgroundBottom
xmlns:commands="clr-namespace:Infrastructure.Commands;assembly=Infrastructure"
 
GreenBackgroundTop

Using the New Command

Using the new Command is as simple as adding the attached property and Binding it to the DelegateCommand<T> on the ViewModel.
GreenBackgroundBottom
        <ListBox Width="250"
                 Height="200"
                 ItemsSource="{Binding Companies, Mode=TwoWay}"
                 Background="{Binding HighlightItems, Mode=OneWay}"
                 commands:MouseOver.Command="{Binding MousedOver}"
                 >
 
GreenBackgroundTop

Command Targets

Since the Behavior class was designed against Control, we can use our new Command on any element derived from Control. Here, we attach the Command to a button.
GreenBackgroundBottom
        <Button Content="SimpleButton"
                HorizontalAlignment="Center"
                VerticalAlignment="Bottom"
                cal:Click.Command="{Binding GetCompanyData}"
                cal:Click.CommandParameter="SimpleButton"
                commands:MouseOver.Command="{Binding MousedOver}"
                />
 
 
GreenBackgroundTop

References

MVVM Video

If you're unfamiliar with the MVVM pattern, this video shows you how to do it in Silverlight. The benefit of this pattern is that it allows us to have more maintainable and testable applications.

Silverlight Prism Video

This video shows you how to get started with Prism in Silverlight. There's a lot in Prism, and this video shows you how to get a Prism project up and running.

Why Use Prism?

This is an interview with the Patterns and Practices team at Microsoft and Shawn Wildermuth. They explain when to use Prism and when not to.

GreenBackgroundBottom
backgroundBottom
backgroundBottom