Recently I had this issue where we had a bunch of routed commands but those which were to be bound to an event instead of directly on a Button.Command. Anyway with all the event to command redirection (hint: use System.Windows.Interactivity EventTriggers and TriggerAction<FrameworkElement>), the requirement boiled down to executing a RoutedCommand in code behind.
Consider the command had a base class which looks as shown below.
public abstract class CommandBase
{
public ICommand Command { get; private set; }
public CommandBinding CommandBinding { get; private set; }
public CommandBase()
{
Command = new RoutedCommand();
CommandBinding = new CommandBinding(Command, HandlerExecute, HandlerCanExecute);
}
private void HandlerExecute(object sender, ExecutedRoutedEventArgs args)
{
Execute(args.Parameter);
}
private void HandlerCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
args.CanExecute = CanExecute(args.Parameter);
}
protected abstract void Execute(object parameter);
protected abstract bool CanExecute(object parameter);
public static void DoCommandBind(CommandBase command, FrameworkElement element)
{
element.CommandBindings.Add(command.CommandBinding);
}
}
The base class simply provides everything that you need to make use of RoutedCommand. It wraps the command and its command binding whose execute and can execute handlers are redirected to the abstract methods. So the implementation would be as simple as the one shown below and you would have a full fledged RoutedCommand
public class SampleCommand : CommandBase
{
protected override void Execute(object parameter)
{
MessageBox.Show(parameter.ToString());
}
protected override bool CanExecute(object parameter)
{
return parameter != null;
}
}
In the above command, I simply execute the command if there is a parameter sent and when executed display the parameter passed in a MessageBox. In order to use this command in XAML, the markup would be as simple as :
<Button Command="{Binding Sample.Command}" CommandParameter="This is from XAML" Content="From XAML" />
But for the RoutedCommand to work, the CommandBinding associated with it should be added to one of the elements up in the Visual Tree. So one of the parents for the Button should have the CommandBinding for the command to be registered with them. This is performed using the CommandBase.DoCommandBind() method. This is required because - the RoutedCommand - even though it implements ICommand interface, the Execute() and CanExecute() methods simply trigger the events that result in the CommandBinding execute the handlers that were specified when the command binding was being created. Read on MSDN for a much better english explanation. If the command binding cannot be found on any of the ancestors, the command would never fire! This is important to understand when we later look at the C# way to execute a RoutedCommand.
For now, look at the code behind. It is simple and what happens in the constructor is self explanatory.
private CommandBase sample = new SampleCommand();
public CommandBase Sample
{
get { return sample; }
}
public CommandDemo()
{
InitializeComponent();
this.DataContext = this;
//do a command binding on this UserControl itself.
CommandBase.DoCommandBind(sample, this);
}
Gotcha 1 : RoutedCommand does not fire!
If this is the case, then make sure the CommandBinding for the RoutedCommand has been registered properly. You can do it in Code-Behind (like CommandBase.DoCommandBind() in my example) or in XAML (loads of examples online for that).
How do I execute a RoutedCommand from code-behind?
Again, same rules apply. The command binding should be available to be found on the ancestors where the command will be fired. If that is the case, then you can do it in the following way.
var rc = (sample.Command as RoutedCommand);
if (rc.CanExecute("This is command parameter", e.OriginalSource as Control))
rc.Execute("This is command parameter", e.OriginalSource as Control);
Executing the ICommand.Execute(parameter) (eg: sample.Command.Execute("parameter")) would work, but if in any case it does not work, use the method above.
Gotcha 2 : RoutedCommand.CanExecute(parameter) does not fire when executing a RoutedCommand from code-behind!
Yes, ICommand.CanExecute() is just an interface method, its the job the command invoker (if done in code-behind, it is you who is the command invoker) to verify if the command can be executed using CanExecute().
Well, thats all for now, I hope this helps some of us who were struggling with one place solution to execute RoutedCommands in code-behind. Hopefully, I will write one more brief article on using System.Windows.Interactivity.Triggers to redirect an event to a command, the MVVM way of executing actions.
No comments:
Post a Comment