Monday, 3 November 2008

DelegateCommand vs RoutedCommand vs EventHandler

I wanted to evaluate the difference between using the different types of Command mechanisms. The following code snippets aren't intended to be a working example, they simply highlight the code differences.


DelegateCommand
SimplePresenter.cs

public DelegateCommand<string> GetCustomerByDC { get; private set; }

GetCustomerByDC = new DelegateCommand(ExecuteGetCustomer, CanExecuteGetCustomer);

private void ExecuteGetCustomer(string parameter)
{
try
{
int customerId = int.Parse(parameter);
GetCustomer(customerId);
}
catch { }
}
}
private bool CanExecuteGetCustomer(string parameter)
{
try
{
int customerId = int.Parse(parameter);
return true;
}
catch
{
return false;
}
}

SimpleView.xaml.cs
ObjectDataProvider odpPresenter = (ObjectDataProvider)this.FindResource("MyPresenter");
Presenter = (SimplePresenter)odpPresenter.ObjectInstance;
Presenter.Model = newModel as SimpleModel;
Presenter.View = this;
private void txtCustomerId_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
Presenter.GetCustomerByDC.RaiseCanExecuteChanged();
}

SimpleView.xaml

xmlns:Presenters="clr-namespace:Sandstorm.Documents.Patterns.Presenters"
<ObjectDataProvider x:Key="MyPresenter" d:IsDataSource="True" ObjectType="{x:Type Presenters:SimplePresenter}"/>
<Button
x:Name="btnGetByDC"
Command="{Binding Source={StaticResource MyPresenter}, Path=GetCustomerByDC}"
CommandParameter="{Binding ElementName=txtCustomerId, Path=Text}"
Width="170"
>Get By DelegateCommand</Button>
<TextBox
TextChanged="txtCustomerId_TextChanged"
/>

RoutedCommand

SimpleView.xaml.cs
private void GetCustomer_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
if (e.Parameter != null)
{
try
{
int customerId = int.Parse(e.Parameter as string);
Presenter.GetCustomer(customerId);
}
catch { }
}
}
private void GetCustomer_CanExecute(object sender, System.Windows.Input.CanExecuteRoutedEventArgs e)
{
try
{
int customerId = int.Parse(e.Parameter as string);
e.CanExecute = true;
}
catch
{
e.CanExecute = false;
}
}

SimpleView.xaml
<UserControl.CommandBindings>
<CommandBinding
Command="local:LeisureCommands.GetCustomerCommand"
Executed="GetCustomer_Executed"
CanExecute="GetCustomer_CanExecute"
/>
</UserControl.CommandBindings>
<Button
x:Name="btnGetByRC"
Command="local:LeisureCommands.GetCustomerCommand"
CommandParameter="{Binding ElementName=txtCustomerId, Path=Text}"
Width="170"
>Get By Routed Command</Button>

LeisureCommands.cs
public class LeisureCommands : RoutedUICommand
{
public readonly static RoutedUICommand GetCustomerCommand;
static LeisureCommands()
{
GetCustomerCommand = new RoutedUICommand("Get Customer...", "GetCustomerCommand", typeof(LeisureCommands));
GetCustomerCommand.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt));
}
}



Event
SimpleView.xaml.cs
private void btnGetByClick_Click(object sender, RoutedEventArgs e)
{
try
{
int customerId = int.Parse(txtCustomerId.Text);
Presenter.GetCustomer(customerId);
}
catch { }
}

SimpleView.xaml
<Button
x:Name="btnGetByClick"
Click="btnGetByClick_Click"
CommandParameter="{Binding ElementName=txtCustomerId, Path=Text}"
Width="170"
>Get By Click Event</Button>


Lines of code
TypeRawOneOffPer CommandCan ExecuteTotal
DelegateCommand406341222
RoutedCommand457381226
Event15015n/a15


Conclusion
The RoutedCommand is a little more expensive in LOC terms than the DelegateCommand but the DelegateCommand does not support all the features of the RoutedCommand, in particular: Gestures.
If the "CanExecute" facility is not required the Event is by far the cheapest / easiest to implement.
I guess my conclusion was: "What are the DelegateCommands bringing to the party?"

1 comment:

swythan said...

The useful thing (to me) about DelegateCommands is that the command AND the Execute/CanExecute handlers can be set-up way across the application, and just the ICommand reference is needed to bind it into the UI.

Note that if your CustomerID field was also bound to the Presenter (and not used as a CommandParameter) then there would be no logic at all in SimpleView.xaml.cs.