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
-
Declare a command in the Viewmodel
-
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
{
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
{
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:
- Property registration,
- Getter and setter of the property 3. Change Handler of the property
- Change Handler of the property
Property registration
The registration is very simple as calling the function RegisterAttached
in the DependencyProperty
class.
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
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
.
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.
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.
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.
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 ((e.NewValue != null) && (e.OldValue == null))
{
element.MouseEnter += element_MouseOver;
}
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.
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 ((e.NewValue != null) && (e.OldValue == null))
{
element.MouseEnter += element_MouseOver;
}
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));
}
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 ((e.NewValue != null) && (e.OldValue == null))
{
element.SetValue(MyAttachedBehaviour.CommandPeramProperty, e.NewValue);
}
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.
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.