Showing posts with label ICommand. Show all posts
Showing posts with label ICommand. Show all posts

Tuesday, April 8, 2014

Reactive UI - Part I [ Setting the Ground Up ]

All of us who have been designing and developing using XAML technologies use ICommand interface on a daily basis. The interface requires implementation of Execute() and CanExecute() methods. Here Execute method is used to perform an action in response to a user interaction. However, the action can only be executed if CanExecute() method returns true. You might have commonly seen a Command being bound to a button. In this case, button would be enabled or disabled based on the evaluation of CanExecute() method of the ICommand. Now let me throw the main question in the air...

Can we run into a situation when a button is enabled when it is not supposed to, and How often ICommand.CanExecute() is called?

Let me first give you the answer of first part of the question, YES, we can certainly run into situations when a button should be disabled when it is supposed to be enabled and vice versa.

The second part of the question is tricky. It is dependent upon the implementation of ICommand. Other than the required Execute and CanExecute() methods, the interface also requires implementation of CanExecuteChanged event. So CanExecute method would be called in all those instances when the command raises this event. If you look at Josh Smith's implementation of ICommand i.e. RelayCommand, here we are directly hooking it up with CommandManager.RequerySuggested event. Now all user interactions would result in re-evaluation of CanExecute(). But what if the underlying property changes value in response to an event other than any user action. In all those situations, we could face this problem.



We might have different implementations handling this differently. This is quite similarly handler in MVVMLight Libs.



And this is from Prism's DelegateCommand type. That's why we need to call RaiseCanExecuteChanged for DelegateCommand every time we need re-evaluation of CanExecute method.



In order to understand the severity of the problem, let's create a sample WPF application. The following is a sample view model with FirstName property of type string. It also has an ICommand property, SubmitCommand. Here we are using Josh Smith's implementation of ICommand i.e. RelayCommand. The code can be found at his original article on msdn. In the command's CanExecute() method, we are checking if FirstName is null or empty, we are returning false in this case. This should disable the button.


And here is the view which is using the view model, defined above, as its DataContext. It is Binding the FirstName property to a TextBox. Since UpdateSourceTrigger is set as PropertyChanged, it would continue to update the source property as user keeps modifying the text. It also has a button which binds its Command property to view model's SubmitCommand. Now clicking the button should cause running the Execute method of the command. The button should be enabled only when the command's CanExecute() method returns true.


As we are modifying the text in the TextBox, we see that as we empty the TextBox, the button is disabled, which is as expected.



Actually modifying the content of the TextBox cause CommandManager.RequerySuggested to be triggered. Since RelayCommand uses this, it causes CanExecuteChanged event to be triggered. Since the UI framework hooks up to this event. It causes CanExecute to be called.

In order to understand the problem, let's add the following code to our view model. It just flips the value of FirstName property between string.Empty and "Not Empty" literal. Here we expect that as soon as we set it to String.Empty, the button should be disabled. Here we are using a System.Timers.Timer for this purpose. Since Elapsed event is invoked on a ThreadPool thread, the value is being updated without any user interaction on a non-UI thread. Since WPF is very forgiving about PropertyChanged on a non-UI thread, we don't have Dispatcher issues for assigning a value to FirstName in this thread.


Let's run the application and see the result. As shown here, the button is always enabled. This is how we have defined CanExecuteChanged event in RelayCommand. Since MVVMLight's RelayCommand is also defined similarly, we should have the same problem there. For Prism's DelegateCommand, we need to call RaiseCanExecuteChanged(), which raises CanExecuteChanged event for the command.



Now we understand the problem. In the next post, we are going to see how we can resolve it. Stay tuned!

Sunday, July 11, 2010

Using ICommand for light weight Views in WPF

Routed Commands seems to be great. But as far as writing code on the view is concerned, it seems it doesn't help us making our views more light weight (we still need to provide the definition of CanExecute() and Execute() in our view. As they are on the view, it is not easy to unit test the logic being executed.

A better approach seems to be using ICommand. ICommand allows us to provide command in accordance with Command Pattern. We know that Command Pattern suggests that Tasks should be provided in the form of Command objects and those commands should be used when those tasks need to be executed.

Let us see an example form. In this form there are two text boxes and a button. We want to copy the value of one text box to other when the button is clicked. This form would be using ViewModel and would be binding with its properties. Let us create a form which uses command based on ICommand. It expects its datacontext to provide a command property, named ProcessingCommand.

<Window x:Class="WPF_ICommand.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="23" Margin="68,15,12,0" Name="textBox1" VerticalAlignment="Top" Text="{Binding Value1}"/>
<TextBox Height="23" Margin="68,56,12,0" VerticalAlignment="Top" Text="{Binding Value2}" />
<Label Height="28" Margin="12,10,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50">Value1</Label>
<Label Height="28" HorizontalAlignment="Left" Margin="12,53,0,0" VerticalAlignment="Top" Width="50">Value2</Label>
<Button Height="23" Margin="68,95,134,0" VerticalAlignment="Top" Command="{Binding ProcessingCommand}">Copy</Button>
</Grid>
</Window>


As you can see above, both text boxes are bound with some properties in the Datacontext. Button is also bound with a command in the DataContext, named ProcessingCommand. The code behind of this form is as follows:

public partial class Window1 : Window
{
Window1ViewModel _vm;
public Window1()
{
InitializeComponent();
this._vm = new Window1ViewModel();
this.DataContext = _vm;
}
}

As you can see we have used Window1ViewModel as the DataContext of the form. Now we have a look at how ViewModel (Window1ViewModel) fulfills all these expectations of Window1.

class Window1ViewModel : DependencyObject
{
public string Value1
{
get { return (string)GetValue(Value1Property); }
set { SetValue(Value1Property, value); }
}
private static DependencyProperty Value1Property =
DependencyProperty.Register("Value1", typeof(string), typeof(Window1ViewModel));

public string Value2
{
get { return (string)GetValue(Value2Property); }
set { SetValue(Value2Property, value); }
}
private static DependencyProperty Value2Property =
DependencyProperty.Register("Value2", typeof(string), typeof(Window1ViewModel));

public MyCommand ProcessingCommand {get; set;}

public Window1ViewModel()
{
this.ProcessingCommand = new MyCommand(this);
}
}

As you can see, our view model is a DependencyObject which has allowed us to register its properties Value1 and Value2 with WPF property system. We have also defined a command property named ProcessingCommand. This is the same command that we have specified with the button in the view.

Now at last, we provide the definition of the command, MyCommand.

class MyCommand : ICommand
{
#region ICommand Members

Window1ViewModel _vm;
public MyCommand(Window1ViewModel _vm)
{
this._vm = _vm;
}

bool ICommand.CanExecute(object parameter)
{
bool ret = true;
return ret;
}

event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}

void ICommand.Execute(object parameter)
{
_vm.Value2 = _vm.Value1;
}

#endregion
}

In order to implement ICommand we need to implement CanExecute and Execute method. We also need to provide the EventHandler CanExecuteChanged. You can see that we are using Window1ViewModel for MyCommand to execute its task.

When we run this application. We can notice that when user writes some text in first text box and clicks Copy button then the value of first text box is copied into second text box. For doing this Execute method of MyCommand is used. You might have noticed that CanExecute method just returns true. We might have some logic which makes this task not available. In all those conditions CanExecute should return false.