Click here to Skip to main content
15,885,988 members
Articles / Desktop Programming / WPF

Command binding with Events – a way from simple to advanced

Rate me:
Please Sign up or sign in to vote.
4.38/5 (15 votes)
11 Apr 2012CPOL11 min read 105.6K   29   36   8
Descriptions of Command, Attached Properties and a clever way if binding control events with Commands.

Introduction

Commands are very powerful tool in WPF. Moreover when it comes the question of MVVM pattern in WPF commands become great tool to execute an action defined in the ViewModel and triggered from the control in the view. I assume that the reader of this article is familiar with MVVM in WPF, RelayCommand and ICommand even though I will try to keep it very simple to grasp the Idea.

Using Command in MVVM

In this section step by step I will show how to

  1. Declare a command in the Viewmodel
  2. How to bind a control-in-a-window with that command

Goal: Our goal is to bind a button control with a command which is declared in a viewmodel. This command will invoke a user defined function defined in that viewmodel.

Declare a command in the Viewmodel

To declare a command in the viewmodel is simple. Just declare a property of type ICommand and use the RelayCommand to delegate the actions defined in the viewmodel.

public class StudentModel : ViewModelBase
 {
// other codes in the model.
//….
 
// the callback function of the Compare commad
       void CompareExecute(object prm)
       {
            if (this.Grade == "F")
                this.Name = "Rizvi - Not " + prm.ToString();
            if (this.Grade == "A+")
                this.Name = "Rizvi - " + prm.ToString();
       }
 
       private RelayCommand _Compare;
       public ICommand Compare
       {
            get
            {
                if (this._Compare == null)
                    this._Compare = new RelayCommand(CompareExecute);
                return this._Compare;
            }
       }
}
    There are two things here to notice,
  • new RelayCommand(CompareExecute); - Here in the RelayCommand class constructer accepts a pointer of the function. This function will actually be executed when this command will be invoked by the button.
  • void CompareExecute(object prm)  { … } – This function is taking a parameter of type object. This parameter will be supplied by the CommandParameter binding of the botton control.

Well at this point we can use lambda expression in the RelayCommand declaration. If your callback action function is very simple as change a property or just comparing two values then you can use lambda expression. Just supply the lambda expression as the constructor parameter of the RelayCommand initialization as bellow.

public class StudentModel : ViewModelBase
 {
// other codes in the model.
//….
 
        private RelayCommand _Compare;
        public ICommand Compare
        {
            get
            {
                if (this._Compare == null)
this._Compare = new RelayCommand((prm) => { this.Grade = "A+"; });
                return this._Compare;
            }
        }
}

How to bind a control-in-a-window

Now is the time to bind a button control with that command. We do this simply as bellow.

<button command="{Binding Compare}" commandparameter="Good Student" content="Evaluate" name="button1" />
    There are again two things here to notice,
  • Command="{Binding Compare}" – By this we are actually binding the button’s click event with the command Compare.
  •  
  • CommandParameter="Good Student" – By this you are actually providing a command parameter which will be passed to the callback function defined in the model - void CompareExecute(object prm) { … } at step 1.

Up to now this is a very simple implementation of the Command in the MVVM and with this approach you can bind any control that has command property with a function in the viewmodel.

Rise of Attached Property

Command is fantastic in MVVM. But still there are some limitations in command. First, command cannot be bound with the controls that doesn’t implement ICommandSource interface. In other words in a control that hasn’t implemented ICommandSource you will not find Command property to assign a binding. For example you will not find any command property attribute in the Label element in XAML as you will find it for Button element. Second, you are forced to bind a command to an event that has been implemented in a control using ICommandSource. For example you only can bind the click event of a Button control with a command not the LostFocus event. Third, you cannot bind an event with a command in a control. For example you will not be able to bind MouseEnter event with a command. Basically you cannot write a control element like this

<button MouseEnterCommand="{Binding MyCommand}" MouseEnterCommandParameter="{Binding MyCommandParameter}" content="Button1" name="button1" />

To overcome those limitations there is a very simple workaround - Using dependency property. At first dependency property could seem much complicated (at least to me) but it is not so. At least in this article I will try to keep it very simple.

First thing first - let’s get familiar with dependency property. Dependency property is a mechanism where you register a completely new property in a control. This dependency property becomes an attached property when you register this property with a static class and use or attach this property to another control or class. In this article I am not going to elaborate details about dependency property. Rather I will recommend this links(Custom Dependency Objects and Dependency Properties and How to: Register an Attached Property) on dependency and attached properties to read but surely I will show you simply how to use it.

Goal: Register a dependency property. Attach the property with a Label control. Bind this attached property with a viewmodel property.

Ground Work

If you follow my project CommandExamples2 you will find some basic groundwork to implement the Model-View-ViewModel pattern. To keep this example simple I created a Student class with the properties Id, Name and Advice. This is our model. Then I created StudentModel class which implements ViewModelBase interface – hence it’s understandable (according to MVVM in WPF) that this is going to be a viewmodel. In this ViewModel there is a property LocalStudent of type Student. For simplicity we will be using the MainWindow as our view and set the datacontext to the viewmodel instance in our case the StudentModel instance. You can set this datacontext either in the windows loaded event or in the XAML.

In the MainWindow I have a Label. In this Label element I want to introduce a completely new property called Show which will be bound to the LocalStudent property in our model and will show the student information in a special way. And here comes the role of dependency property. I will create and register this Show property in a different shared class MyAttachedBehaviour which is conventionally called the Provider Class. This provider class will actually provide the attached property in a control; for our case the Label like bellow.

<Label local:MyAttachedBehaviour.Show="{Binding LocalStudent}"  Content="Label1" Name="Label1"></Label>

Now registering the show property in the MyAttachedBehaviour contains three steps:

  1. Property registration,
  2. Getter and setter of the property 3. Change Handler of the property
  3. Change Handler of the property

Property registration

The registration is very simple as calling the function RegisterAttached in the DependencyProperty class.

C#
public static class MyAttachedBehaviour
{
        public static DependencyProperty ShowProperty = DependencyProperty.RegisterAttached(
                                                                        "Show",
                                                                        typeof(Student),
                                                                        typeof(MyAttachedBehaviour),
                                                                        new FrameworkPropertyMetadata(new Student(0,"","X"), new PropertyChangedCallback(MyAttachedBehaviour.ShowChanged)));
 … Other codes coming …
}

The first three parameters are self-explanatory so I feel the forth one needs some explanation. This FrameworkPropertyMetadata has two parameters in the constructor. The first one is the default value of the property – in other words, this value will be loaded by default just after the property has been registered. In our case a default is new Student(0,"","X").

The second one is for pointing a callback function which will execute on property change. In our case ShowChanged will execute on property value change. It has been supplied by the help of the object PropertyChangedCallback.

Getter and setter of the property

C#
public static class MyAttachedBehaviour
{
        public static DependencyProperty ShowProperty = DependencyProperty.RegisterAttached(
                                                                        "Show",
                                                                        typeof(Student),
                                                                        typeof(MyAttachedBehaviour),
                                                                        new FrameworkPropertyMetadata(new Student(0,"","X"), new PropertyChangedCallback(MyAttachedBehaviour.ShowChanged)));
        public static void SetShow(DependencyObject target, Student value)
        {
            target.SetValue(MyAttachedBehaviour.ShowProperty, value);
        }
        public static Student GetShow(DependencyObject target)
        {
            return (Student)target.GetValue(MyAttachedBehaviour.ShowProperty);
        }
 … Other codes coming …
}

The getter and setter follows a convention get[attachedPropertyName] and set[attachedPropertyName]. These accessors are required so that the acting XAML reader can recognize the property as an attribute in XAML and resolve the appropriate types. Notice that they should call the Getvalue and SetValue to handle data. The parameter target will actually point to our Label at execution.

Change Handler of the property

Change handler is relatively simple. With the help of the FrameworkPropertyMetadata  object I have attached a property change handler ShowChanged. In this handler I am actually stealing the new value form the Show property of the Label and set it to the content property as string in that Label.

C#
public static class MyAttachedBehaviour
    {
        public static DependencyProperty ShowProperty = DependencyProperty.RegisterAttached(
                                                                        "Show",
                                                                        typeof(Student),
                                                                        typeof(MyAttachedBehaviour),
                                                                        new FrameworkPropertyMetadata(new Student(0,"","X"), new PropertyChangedCallback(MyAttachedBehaviour.ShowChanged)));
 
 
        public static void SetShow(DependencyObject target, Student value)
        {
            target.SetValue(MyAttachedBehaviour.ShowProperty, value);
        }
 
        public static Student GetShow(DependencyObject target)
        {
            return (Student)target.GetValue(MyAttachedBehaviour.ShowProperty);
        }
 
        private static void ShowChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            Label element = target as Label;
            if (element!=null)
            {
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    Student _s = e.NewValue as Student;
                    element.Content = _s.Id.ToString() + " # " + _s.Name + " # " + _s.Grade;
                }               
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    element.Content  = string.Empty;
                }
            }           
        }
    }

Now it’s time to run our CommandExample2 project and see the output.

Figur 1: Runing the project

Figur 2: Click 'Show Student' button

There is a button ‘Show Student’ in the MainWindow. In the code behind click event handler of this button I have set our attached property Show to a Student object.

Food for thought: You might think that if I have set a default value in the attached property registration then why doesn’t it come and show at the form load time?

Answer: If we debug a little bit we’ll be able to see that the attached property Show of the Label gets the default value(new Student(0,"","X")) for sure but as soon as this property gets null value from the first binding with the LocalStudent property of the viewmodel, the value gets changed – ShowChanged handler fired – and the Label content becomes string.Empty.

Command Binding with Events

If you have followed me this far then you should have some idea about how to bind a control with a command and if a control don’t have any suitable property to bind with then we can make one (attached property). Now we are pretty close to binding an event with a custom command in your viewmodel.

Goal: Define a command property in the viewmodel. Register an attached property in a control and bind that with the command. In the attached property-change-handler, hook the event handler with the control’s event.

Ground Work

Our next example project CommandExample3 is pretty much like the previous one. Here’s the same Student class with the properties Id, Name and Grade. The same StudentModel class which implements ViewModelBase interface but in this viewmodel there are three properties, LocalStudent of type Student, Advice of type string and QuickCommant which is a command property of type ICommand. At the first section(Using Command in MVVM) of this article we have learned how to create a command property in viewmodel. We should use that knowledge here. Our StudentModel is as bellow. 

C#
public class StudentModel : ViewModelBase
    {
 
        public StudentModel()
        {
            this.LocalStudent = new Student(1, "Rizvi", "B");
        }
 
        private Student _LocalStudent;
        public Student LocalStudent
        {
            get { return this._LocalStudent; }
            set { if(value != this._LocalStudent) { this._LocalStudent = value; OnPropertyChanged("LocalStudent");} }
        }
 
        private string _Advice;
        public string Advice
        {
            get { return this._Advice; }
            set { if (value != this._Advice) { this._Advice = value; OnPropertyChanged("Advice"); } }
        }
 
        private RelayCommand _QuickCommant;
        public ICommand QuickCommant
        {
            get
            {
                if (this._QuickCommant == null)
                    this._QuickCommant = new RelayCommand((f) => { var f1 = (StudentModel)f; f1.Advice = (f1.LocalStudent.Grade == "B" ? "Try to improve Geography":"No Commant"); });
                return this._QuickCommant;
            }
        }
       
    }

The MainWindow is used as a view as before. There are four textboxes to display Id, Name, Grade and Advice. In the constructor of the StudentModel a Student instance is initialized with new Student(1, "Rizvi", "B") and at the form load this student information is viewed in the textboxes. There is a Label(Mouse sencitive area) in the window which is mouse sensitive and if the mouse is over this Label the Advice textbox gets populated with an advice text. This happens coz the MouseEnter event of the Label is bound with a command QuickCommant in the StudentModel. This command evaluates the Grade of a student and sets an advice text to the Advice property of the StudentModel.

Figur 3: The output of project.

Create attached property

To make a mouse sensitive Label we will have to use the MouseEnter event of this control. But I am not going to write this event handler in the code behind. Rather I will register an attached property called MouseOverCommand for the Label control. Then I will bind it’s MouseEnter event with the QuickCommant command of the StudentModel. From the previous section (Rise of Attached Property) we learned how to attach a property with a control. So I am not going to describe it this time but you can see the code. 

C#
public class MyAttachedBehaviour
    {
        public static DependencyProperty MouseOverCommandProperty = DependencyProperty.RegisterAttached(
                                                                        "MouseOverCommand",
                                                                        typeof(ICommand),
                                                                        typeof(MyAttachedBehaviour),
                                                                        new FrameworkPropertyMetadata(
 new PropertyChangedCallback(MyAttachedBehaviour.MouseOverChanged)));
        public static void SetMouseOverCommand(DependencyObject target, ICommand value)
        {
            target.SetValue(MyAttachedBehaviour.MouseOverCommandProperty, value);
        }
        public static ICommand GetMouseOverCommand(DependencyObject target)
        {
            return (ICommand)target.GetValue(MyAttachedBehaviour.MouseOverCommandProperty);
        }
             … Other codes coming …
}

Notice the only difference from the previously described attached property is this time the type of the attached property is ICommand. Absolutely this is reasonable because we are going to bind it with a command. 

<label content="Mouse sencitive area" behave:myattachedbehaviour.mouseovercommand="{Binding QuickCommant}" behave:myattachedbehaviour.commandperam="{Binding}" background="#FFE8DF2E" />

Now I will show you how to hook an event handler with a control in an attached property-change-handler.

Hook Event Handler

Observe in the class MyAttachedBehaviour bellow. In our attached property, notice that the property-change-handler is MouseOverChanged. In this handler the target parameter is the Label. So, we casted it to UIElement and hook up the handler element_MouseOver with its MouseEnter event. Then in this element_MouseOver handler in the first line we got the UIElement(our Label) from the sender. Most importantly in the second line we are getting the command(our QuickCommant command) that is bound to our Label. Finally in the third line we are actually executing that command with the execute method. 

C#
public class MyAttachedBehaviour
    {
        public static DependencyProperty MouseOverCommandProperty = DependencyProperty.RegisterAttached(
                                                                        "MouseOverCommand",
                                                                        typeof(ICommand),
                                                                        typeof(MyAttachedBehaviour),
                                                                        new FrameworkPropertyMetadata( new PropertyChangedCallback(MyAttachedBehaviour.MouseOverChanged)));
 
 
        public static void SetMouseOverCommand(DependencyObject target, ICommand value)
        {
            target.SetValue(MyAttachedBehaviour.MouseOverCommandProperty, value);
        }
 
        public static ICommand GetMouseOverCommand(DependencyObject target)
        {
            return (ICommand)target.GetValue(MyAttachedBehaviour.MouseOverCommandProperty);
        }
 
        private static void MouseOverChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = target as UIElement;
            if (element!=null)
            {
                // If we're putting in a new command and there wasn't one already
                // hook the event
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    element.MouseEnter += element_MouseOver;
                }
                // If we're clearing the command and it wasn't already null
                // unhook the event
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    element.MouseEnter -= element_MouseOver;
                }
            }           
        }       
        static void element_MouseOver(object sender, MouseEventArgs e)
        {
            UIElement element = (UIElement)sender;
            ICommand command = (ICommand)element.GetValue(MyAttachedBehaviour.MouseOverCommandProperty);
            command.Execute(element.GetValue(MyAttachedBehaviour.CommandPeramProperty));
        }
 
             … Other codes coming …
 
}

Well in this execute method we are passing the command parameter. To get this command parameter we will have to register another attached property with the Label called CommandPeram.

Create another attached property 

Now we will have to create another attached property CommandPeram for binding the command parameter with the Label. In the XAML we will use this property as bellow. Here with {Binding} we actually bound the whole viewmodel(our StudentModel) with the attached property - CommandPeram.

<label content="Mouse sencitive area" behave:myattachedbehaviour.mouseovercommand="{Binding QuickCommant}" behave:myattachedbehaviour.commandperam="{Binding}" background="#FFE8DF2E"/>

Registering a new attached property called CommandPeram with the Label is pretty much straight forward like before. We have created this property in the same provider class MyAttachedBehaviour. Observe the text area under "Command peremeter section" comment.

C#
public class MyAttachedBehaviour
    {
        public static DependencyProperty MouseOverCommandProperty = DependencyProperty.RegisterAttached(
                                                                        "MouseOverCommand",
                                                                        typeof(ICommand),
                                                                        typeof(MyAttachedBehaviour),
                                                                        new FrameworkPropertyMetadata( new PropertyChangedCallback(MyAttachedBehaviour.MouseOverChanged)));
 
 
        public static void SetMouseOverCommand(DependencyObject target, ICommand value)
        {
            target.SetValue(MyAttachedBehaviour.MouseOverCommandProperty, value);
        }
 
        public static ICommand GetMouseOverCommand(DependencyObject target)
        {
            return (ICommand)target.GetValue(MyAttachedBehaviour.MouseOverCommandProperty);
        }
 
        private static void MouseOverChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = target as UIElement;
            if (element!=null)
            {
                // If we're putting in a new command and there wasn't one already
                // hook the event
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    element.MouseEnter += element_MouseOver;
                }
                // If we're clearing the command and it wasn't already null
                // unhook the event
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    element.MouseEnter -= element_MouseOver;
                }
            }           
        }       
 
        static void element_MouseOver(object sender, MouseEventArgs e)
        {
            UIElement element = (UIElement)sender;
            ICommand command = (ICommand)element.GetValue(MyAttachedBehaviour.MouseOverCommandProperty);
            command.Execute(element.GetValue(MyAttachedBehaviour.CommandPeramProperty));
        }
 
 
        // Command peremeter section
        public static DependencyProperty CommandPeramProperty = DependencyProperty.RegisterAttached(
                                                                        "CommandPeram",
                                                                        typeof(object),
                                                                        typeof(MyAttachedBehaviour),
                                                                        new FrameworkPropertyMetadata(new PropertyChangedCallback(MyAttachedBehaviour.CommandPeramChanged)));
        public static void SetCommandPeram(DependencyObject target, object value)
        {
            target.SetValue(MyAttachedBehaviour.CommandPeramProperty, value);
        }
        public static object GetCommandPeram(DependencyObject target)
        {
            return (object)target.GetValue(MyAttachedBehaviour.CommandPeramProperty);
        }
        private static void CommandPeramChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = target as UIElement;
            if (element != null)
            {
                // If we're putting in a new command and there wasn't one already
                // hook the event
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    element.SetValue(MyAttachedBehaviour.CommandPeramProperty, e.NewValue);
                }
                // If we're clearing the command and it wasn't already null
                // unhook the event
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    element.SetValue(MyAttachedBehaviour.CommandPeramProperty, e.NewValue);
                }
            }
        }      
}
    }

With this attached property we actually supply our viewmodel(StudentModel) as command property, to our command QuickCommant. In this command there is a command handler written in lambda expression. There the viewmodel is extracted and a value is set to the Advice property. Observe the QuickCommant command in the StudentModel as bellow.

C#
private RelayCommand _QuickCommant;
 public ICommand QuickCommant
 {
     get
     {
         if (this._QuickCommant == null)
             this._QuickCommant = new RelayCommand((f) => { var f1 = (StudentModel)f; f1.Advice = (f1.LocalStudent.Grade == "B" ? "Try to improve Geography":"No Commant"); });
         return this._QuickCommant;
     }
 }

Final Words

Finally we can summarize what we have done so far. We saw how to use a simple command; then we learned to register a simple attached property. After that we used this attached property to bind an event of a control with a command. Finally we learned who to send command parameter to this command using another attached property.

This is my way of binding an event with a command in WPF. There could be some other way too. I welcome all your comments, suggestions or advice on this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCan Excecute Pin
Freed from here20-Jan-14 11:05
Freed from here20-Jan-14 11:05 
GeneralMy vote of 1 Pin
Naila Akbar2-Jan-14 22:38
Naila Akbar2-Jan-14 22:38 
QuestionThere is no download link for the source code. Pin
csmanul15-Feb-13 0:47
csmanul15-Feb-13 0:47 
QuestionAwesome article Pin
ram_t27-Jan-13 22:45
ram_t27-Jan-13 22:45 
QuestionI can't download .zip. Pin
asdasdasz2-Dec-12 9:06
asdasdasz2-Dec-12 9:06 
GeneralMy vote of 5 Pin
Leo5617-Jul-12 0:03
Leo5617-Jul-12 0:03 
QuestionMistake in Command Name: Evaluate is not on the ViewModel Pin
User 27100911-Apr-12 9:09
User 27100911-Apr-12 9:09 
AnswerRe: Mistake in Command Name: Evaluate is not on the ViewModel Pin
RizviHasan11-Apr-12 11:15
RizviHasan11-Apr-12 11:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.