How to Add a Custom Routed Command in Wpf

How do I add a custom routed command in WPF?

I use a static class that I place after the Window1 class (or whatever the window class happens to be named) where I create instances of the RoutedUICommand class:

public static class Command {

public static readonly RoutedUICommand DoSomething = new RoutedUICommand("Do something", "DoSomething", typeof(Window1));
public static readonly RoutedUICommand SomeOtherAction = new RoutedUICommand("Some other action", "SomeOtherAction", typeof(Window1));
public static readonly RoutedUICommand MoreDeeds = new RoutedUICommand("More deeds", "MoreDeeeds", typeof(Window1));

}

Add a namespace in the window markup, using the namespace that the Window1 class is in:

xmlns:w="clr-namespace:NameSpaceOfTheApplication"

Now I can create bindings for the commands just as for the application commands:

<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Open" Executed="CommandBinding_Open" />
<CommandBinding Command="ApplicationCommands.Paste" Executed="CommandBinding_Paste" />
<CommandBinding Command="w:Command.DoSomething" Executed="CommandBinding_DoSomething" />
<CommandBinding Command="w:Command.SomeOtherAction" Executed="CommandBinding_SomeOtherAction" />
<CommandBinding Command="w:Command.MoreDeeds" Executed="CommandBinding_MoreDeeds" />
</Window.CommandBindings>

And use the bindings in a menu for example:

<MenuItem Name="Menu_DoSomething" Header="Do Something" Command="w:Command.DoSomething" />

How to bind a custom routed event to a command in the view model?

You can implement EventToCommandBehavior on your own:

using System.Windows;
using System.Windows.Input;
using Microsoft.Xaml.Behaviors;

namespace WpfApplication1.Behaviors
{
public class EventToCommandBehavior : Behavior<FrameworkElement>
{
public static readonly DependencyProperty RoutedEventProperty = DependencyProperty.Register(
nameof(RoutedEvent),
typeof(RoutedEvent),
typeof(EventToCommandBehavior),
new PropertyMetadata(null));

public RoutedEvent RoutedEvent
{
get => (RoutedEvent)GetValue(RoutedEventProperty);
set => SetValue(RoutedEventProperty, value);
}

public static readonly DependencyProperty WithCommandProperty = DependencyProperty.Register(
nameof(WithCommand),
typeof(ICommand),
typeof(EventToCommandBehavior),
new PropertyMetadata(null));

public ICommand WithCommand
{
get => (ICommand)GetValue(WithCommandProperty);
set => SetValue(WithCommandProperty, value);
}

readonly RoutedEventHandler _handler;

public EventToCommandBehavior()
{
_handler = (s, e) =>
{
var args = e.OriginalSource;

if (WithCommand.CanExecute(args))
{
WithCommand.Execute(args);
}
};
}

protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.AddHandler(RoutedEvent, _handler);
}

protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.RemoveHandler(RoutedEvent, _handler);
}
}
}

Suppose there is CustomButton control that raises custom routed event ConditionalClick. It can be handled with ProcessConditionalClickCommand command in ViewModel using EventToCommandBehavior:

    <StackPanel>
<b:Interaction.Behaviors>
<behaviors:EventToCommandBehavior RoutedEvent="controls:CustomButton.ConditionalClick" WithCommand="{Binding ProcessConditionalClickCommand}">
</behaviors:EventToCommandBehavior>
</b:Interaction.Behaviors>
<controls:CustomButton
Content="Click to trigger a custom routed event"
Background="LightGray">
</controls:CustomButton>
</StackPanel>

Firing and Capturing of Custom RoutedCommands within CustomControls for MenuItems

A RoutedCommand searches the visual tree from the focused element and up for an element that has a matching CommandBinding, executes the Execute delegate for this particular CommandBinding and then stops.

It doesn't route or bubble up any further as Nick Kramer of Microsoft has stated here: https://www.vistax64.com/avalon/852-routedcommand-doesnt-route-ui-tree.html.

How can I handle WPF routed commands in my ViewModel without code-behind?

I would rephrase the question as:

How can I handle WPF routed commands in my ViewModel without code-behind?

To which, I would respond: Great Question!

WPF does not provide a built-in way to do this, which is especially annoying when you're first starting WPF and everybody tells you that "Code-Behind is evil" (it really is). So you have to build it yourself.

Building it Ourselves

So, how do go about creating such functionality ourselves? Well, first we need an equivalent of a CommandBinding:

/// <summary>
/// Allows associated a routed command with a non-routed command. Used by
/// <see cref="RoutedCommandHandlers"/>.
/// </summary>
public class RoutedCommandHandler : Freezable
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(RoutedCommandHandler),
new PropertyMetadata(default(ICommand)));

/// <summary> The command that should be executed when the RoutedCommand fires. </summary>
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}

/// <summary> The command that triggers <see cref="ICommand"/>. </summary>
public ICommand RoutedCommand { get; set; }

/// <inheritdoc />
protected override Freezable CreateInstanceCore()
{
return new RoutedCommandHandler();
}

/// <summary>
/// Register this handler to respond to the registered RoutedCommand for the
/// given element.
/// </summary>
/// <param name="owner"> The element for which we should register the command
/// binding for the current routed command. </param>
internal void Register(FrameworkElement owner)
{
var binding = new CommandBinding(RoutedCommand, HandleExecuted, HandleCanExecute);
owner.CommandBindings.Add(binding);
}

/// <summary> Proxy to the current Command.CanExecute(object). </summary>
private void HandleCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = Command?.CanExecute(e.Parameter) == true;
e.Handled = true;
}

/// <summary> Proxy to the current Command.Execute(object). </summary>
private void HandleExecuted(object sender, ExecutedRoutedEventArgs e)
{
Command?.Execute(e.Parameter);
e.Handled = true;
}
}

And then we need a class that will actually associated the RoutedCommandHandler with a specific element. For this, we'll make a collection of RoutedCommandHandlers as an attached property, like so:

/// <summary>
/// Holds a collection of <see cref="RoutedCommandHandler"/> that should be
/// turned into CommandBindings.
/// </summary>
public class RoutedCommandHandlers : FreezableCollection<RoutedCommandHandler>
{
/// <summary>
/// Hide this from WPF so that it's forced to go through
/// <see cref="GetCommands"/> and we can auto-create the collection
/// if it doesn't already exist. This isn't strictly necessary but it makes
/// the XAML much nicer.
/// </summary>
private static readonly DependencyProperty CommandsProperty = DependencyProperty.RegisterAttached(
"CommandsPrivate",
typeof(RoutedCommandHandlers),
typeof(RoutedCommandHandlers),
new PropertyMetadata(default(RoutedCommandHandlers)));

/// <summary>
/// Gets the collection of RoutedCommandHandler for a given element, creating
/// it if it doesn't already exist.
/// </summary>
public static RoutedCommandHandlers GetCommands(FrameworkElement element)
{
RoutedCommandHandlers handlers = (RoutedCommandHandlers)element.GetValue(CommandsProperty);
if (handlers == null)
{
handlers = new RoutedCommandHandlers(element);
element.SetValue(CommandsProperty, handlers);
}

return handlers;
}

private readonly FrameworkElement _owner;

/// <summary> Each collection is tied to a specific element. </summary>
/// <param name="owner"> The element for which this collection is created. </param>
public RoutedCommandHandlers(FrameworkElement owner)
{
_owner = owner;

// because we auto-create the collection, we don't know when items will be
// added. So, we observe ourself for changes manually.
var self = (INotifyCollectionChanged)this;
self.CollectionChanged += (sender, args) =>
{
// note this does not handle deletions, that's left as an exercise for the
// reader, but most of the time, that's not needed!
((RoutedCommandHandlers)sender).HandleAdditions(args.NewItems);
};
}

/// <summary> Invoked when new items are added to the collection. </summary>
/// <param name="newItems"> The new items that were added. </param>
private void HandleAdditions(IList newItems)
{
if (newItems == null)
return;

foreach (RoutedCommandHandler routedHandler in newItems)
{
routedHandler.Register(_owner);
}
}

/// <inheritdoc />
protected override Freezable CreateInstanceCore()
{
return new RoutedCommandHandlers(_owner);
}
}

Then, it's as simple as using the classes on our element:

<local:RoutedCommandHandlers.Commands>
<local:RoutedCommandHandler RoutedCommand="Help" Command="{Binding TheCommand}" />
</local:RoutedCommandHandlers.Commands>

Interaction.Behavior implementation

Knowing the above, you might then ask:

Wow, that's great, but that's a lot of code. I'm using Expression Behaviors already, so is there a way to simplify this a bit?

To which, I would respond: Great Question!

If you're already using Interaction.Behaviors, then you can use the following implementation instead:

/// <summary>
/// Allows associated a routed command with a non-ordinary command.
/// </summary>
public class RoutedCommandBinding : Behavior<FrameworkElement>
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(RoutedCommandBinding),
new PropertyMetadata(default(ICommand)));

/// <summary> The command that should be executed when the RoutedCommand fires. </summary>
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}

/// <summary> The command that triggers <see cref="ICommand"/>. </summary>
public ICommand RoutedCommand { get; set; }

protected override void OnAttached()
{
base.OnAttached();

var binding = new CommandBinding(RoutedCommand, HandleExecuted, HandleCanExecute);
AssociatedObject.CommandBindings.Add(binding);
}

/// <summary> Proxy to the current Command.CanExecute(object). </summary>
private void HandleCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = Command?.CanExecute(e.Parameter) == true;
e.Handled = true;
}

/// <summary> Proxy to the current Command.Execute(object). </summary>
private void HandleExecuted(object sender, ExecutedRoutedEventArgs e)
{
Command?.Execute(e.Parameter);
e.Handled = true;
}
}

With the corresponding XAML:

<i:Interaction.Behaviors>
<local:RoutedCommandBinding RoutedCommand="Help" Command="{Binding TheCommand}" />
</i:Interaction.Behaviors>

adding commands to custom control for button binding wpf

At first, you should define a command binding on the Window like that(create handlers for Executed and CanExecuteevents):

<Window x:Class="CommandBindingWPF.MainWindow"
...The code omitted for the brevity...
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute" />
</Window.CommandBindings>

and declare your Button ix xaml:

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="ApplicationCommands.New">New</Button>
</StackPanel>

Handlers should be created in code-behind after you command binding created:

private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Hello from Command");
}

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{ }

Update:

For MVVM application:

public class RelayCommand : ICommand
{
#region Fields

readonly Action<object> _execute;
readonly Predicate<object> _canExecute;

#endregion // Fields

#region Constructors

public RelayCommand(Action<object> execute)
: this(execute, null)
{
}

public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");

_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors

#region ICommand Members

public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}

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

public void Execute(object parameter)
{
_execute(parameter);
}

#endregion // ICommand Members
}

Then create a property in your viewModel. For instance:

public class YourViewModel
{
public RelayCommand YourCommand { get; set; }
public YourViewModel()
{
YourCommand = new RelayCommand(DoSmth, CanDoSmth);
}

private void DoSmth(object obj)
{
Message.Box("Hello from viewModel");
}

private bool CanDoSmth(object obj)
{
//you could implement your logic here. But by default it should be
//set to true
return true;
}
}

And XAML should be look like:

<Button Content="Click me!" Command="{Binding YourCommand}"/> 

To get acquainted with MVVM, I recommend you to read Rachel Lim's blog. She has a talent to teach people and she can explain by simple terms. Read Rachel Lim's blog.
To get acquainted with MVVM commands see that post

Set RelativeSource CommandTarget in RoutedCommand.Execute in code behind

Routed commands are, by definition, routed. If I understand your problem well, you just have to pass this as the second parameter of your command (assuming you're into the control class). The command will be bubbling up the visual tree until it encounters the command binding on the parent.

adding custom routed event of user control to xaml of a window in wpf

I'm not sure I fully understand your question but I hope one of my answers below helps you out.

To attach a "direct" event handler in XAML, just do the following:

<c:MyUserControl x:Name="uc1" CustomClick="uc1_CustomClickHandler"/>

To hook up a handler for the (routed) event of one element (e.g. the CustomClick event in your example) to another element (e.g. the parent window):

<Window c:MyUserControl.CustomClick="ucCustomEvent_CustomClick"/>

Now, if you want to tie up an event in your UI to a Command in your ViewModel, you will need attached behaviors to do that. There are lots of frameworks around featuring different implementations of this. Here's one you can try out: http://sachabarber.net/?p=514. It will allow you to do something like the following in your code:

 <c:MyUserControl local:CommandBehavior.RoutedEventName="MyCustomClick"
local:CommandBehavior.TheCommandToRun="{Binding MyViewModelCommand}"/>

Hope this helps.



Related Topics



Leave a reply



Submit