WPF Commands, How to declare Application level commands?
Asked Answered
M

5

20

I'm interested in creating commands that are available from anywhere in my WPF application.

I'd like them to work in the same way as Cut, Copy, Paste and the other Application level commands, ie:

<Button Command="Paste" />

I assumed I could setup CommandBindings for the Application instance, but that property isn't available.

How is this done?

The best I have managed so far is to create a suite of commands on the top level window and then access them like this...:

<Button Command="{x:Static namespace::MainWindow.CommandName}" />

Which works, but is of course tightly coupled, and so extremely brittle.

Machos answered 17/1, 2011 at 3:51 Comment(0)
E
38

You can setup CommandBindings for "All Windows" of your WPF application and implement command handlers in Application class.

First of all, create a static command container class. For example,

namespace WpfApplication1 
{
    public static class MyCommands
    {
        private static readonly RoutedUICommand doSomethingCommand = new RoutedUICommand("description", "DoSomethingCommand", typeof(MyCommands));

        public static RoutedUICommand DoSomethingCommand
        {
            get
            {
                return doSomethingCommand;
            }
        }
    }
}

Next, set your custom command to Button.Command like this.

<Window x:Class="WpfApplication1.MainWindow"
        ...
        xmlns:local="clr-namespace:WpfApplication1">
    <Grid>
        ...
        <Button Command="local:MyCommands.DoSomethingCommand">Execute</Button>
    </Grid>
</Window>

Finally, implement the command handler of your custom command in Application class.

namespace WpfApplication1 
{

    public partial class App : Application
    {
        public App()
        {
            var binding = new CommandBinding(MyCommands.DoSomethingCommand, DoSomething, CanDoSomething);

            // Register CommandBinding for all windows.
            CommandManager.RegisterClassCommandBinding(typeof(Window), binding);
        }

        private void DoSomething(object sender, ExecutedRoutedEventArgs e)
        {
            ...
        }

        private void CanDoSomething(object sender, CanExecuteRoutedEventArgs e)
        {
            ...
            e.CanExecute = true;
        }
    }
}
Epithet answered 17/1, 2011 at 5:9 Comment(5)
Thanks Shou, I'll give this a try. I assume I can use new command names e.g. ApplicationCommands.MyCommand? If not this doesn't answer my question.Machos
Please change the example to use a custom command, not Paste. I am unable to find any other reference to adding new commands to ApplicationCommands, can you provide a link? - Also, would you show XAML usage, esp if it differs from <Button Command="MyCommand" /> - thank you.Machos
I changed sample code. You don't have to add new commands to ApplicationCommands class because if you create a static command container class, it can be used just like ApplicationCommands class.Epithet
Thank you, that's great, and solves the issue of tight-coupling, which was the most important one for me.Machos
Perfect, this is exactly what I wanted. :)Divergency
T
7

I did not like the complexity of the other solutions, but after a few hours of research I found out it is really simple.

First setup your command as you usually do, but add a static property for WPF so that it can obtain an instance of your command.

class MyCommand : ICommand
{
    // Singleton for the simple cases, may be replaced with your own factory     
    public static ICommand Instance { get; } = new MyCommand();

    public bool CanExecute(object parameter)
    {
        return true; // TODO: Implement
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        // TODO: Implement       
    }   
}

Add a reference to the namespace of your command in your XAML (last line), like this:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:commands="clr-namespace:MyProject.Commands">     

Then just reference your static property in your XAML like this:

<Button Content="Button" Command="commands:MyCommand.Instance" />
Tyus answered 6/6, 2015 at 19:14 Comment(0)
T
5

StackOverflow members helped me so many time that I decide now to contribute and share ;-)

Based on Shou Takenaka's answer, here is my implementation.

My interest was to produce only one reusable file.


First, create a command(s) container class

namespace Helpers
{
    public class SpecificHelper
    {
        private static RoutedUICommand _myCommand = new RoutedUICommand("myCmd","myCmd", typeof(SpecificHelper));
        public static RoutedUICommand MyCommand { get { return _myCommand; } }

        static SpecificHelper()
        {
            // Register CommandBinding for all windows.
            CommandManager.RegisterClassCommandBinding(typeof(Window), new CommandBinding(MyCommand, MyCommand_Executed, MyCommand_CanExecute));
        }

        // TODO: replace UIElement type by type of parameter's binded object
        #region MyCommand
        internal static void MyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (!verifType<UIElement>(e.Parameter)) return;

            e.Handled = true;
            // TODO : complete the execution code ...
        }

        internal static void SelectAll_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (!verifType<UIElement>(e.Parameter)) return;

            e.CanExecute = true;
            var item = (e.Parameter as UIElement);
            // TODO : complete the execution code ...
        }
        #endregion

        private static bool verifType<T>(object o)
        {
            if (o == null) return false;
            if (!o.GetType().Equals(typeof(T))) return false;
            return true;
        }
    }
}

Next, declare a resource in App.xaml:

<Application x:Class="Helper.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:h="clr-namespace:Helpers"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" 
             StartupUri="MainWindow.xaml" >
    <Application.Resources>
        <h:SpecificHelper x:Key="sh" />
    </Application.Resources>
</Application>

Finally, bind any command property to the property of your application resource:

<Button Content="Click to execute my command"
        Command="{Binding Source={StaticResource sh}, Path=MyCommand}"
        CommandParameter="{Binding ElementName=myElement}" />

that's all folks :-)

Totality answered 28/3, 2013 at 15:40 Comment(0)
M
1

If you try to define CommandBindings or InputBindings as resources in your App.xaml, you will find that you cannot use them, because XAML doesn't allow you to use either:

<Window ... CommandBindings="{StaticResource commandBindings}">

or to set command bindings with a style setter:

<Setter Property="CommandBindings" Value="{StaticResource commandBindings}">

because neither of these properties have a "set" accessor. Using the idea in this post, I came up with a clean way of using resources from App.xaml or any other resource dictionary.

First you define your command bindings and input bindings indirectly, like you would any other resource:

    <InputBindingCollection x:Key="inputBindings">
        <KeyBinding Command="Help" Key="H" Modifiers="Ctrl"/>
    </InputBindingCollection>
    <CommandBindingCollection x:Key="commandBindings">
        <CommandBinding Command="Help" Executed="CommandBinding_Executed"/>
    </CommandBindingCollection>

and then you refer to them from the XAML of another class:

<Window ...>
    <i:Interaction.Behaviors>
        <local:CollectionSetterBehavior Property="InputBindings" Value="{StaticResource inputBindings}"/>
        <local:CollectionSetterBehavior Property="CommandBindings" Value="{StaticResource commandBindings}"/>
    </i:Interaction.Behaviors>
    ...
</Window>

The CollectionSetterBehavior is a reusable behavior that doesn't "set" the property to it's value, but instead clears the collection, and re-populates it. So the collection doesn't change, only it's contents.

Here's the source for the behavior:

public class CollectionSetterBehavior : Behavior<FrameworkElement>
{
    public string Property
    {
        get { return (string)GetValue(PropertyProperty); }
        set { SetValue(PropertyProperty, value); }
    }

    public static readonly DependencyProperty PropertyProperty =
        DependencyProperty.Register("Property", typeof(string), typeof(CollectionSetterBehavior), new UIPropertyMetadata(null));

    public IList Value
    {
        get { return (IList)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(IList), typeof(CollectionSetterBehavior), new UIPropertyMetadata(null));

    protected override void OnAttached()
    {
        var propertyInfo = AssociatedObject.GetType().GetProperty(Property);
        var property = propertyInfo.GetGetMethod().Invoke(AssociatedObject, null) as IList;
        property.Clear();
        foreach (var item in Value) property.Add(item);
    }
}

If you are not familiar with behaviors, first add this namespace:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

and add the corresponding reference to your project.

Magnetoelectricity answered 17/1, 2011 at 7:30 Comment(1)
Please show how you'd then use these commands in XAML, e.g. <Button Command="MyCustomCommand"/>Machos
T
1

Declare the CommandBinding at Application level from where it can be re-used everywhere.

<Application.Resources>       
    <CommandBinding x:Key="PasteCommandKey" Command="ApplicationCommands.Paste" CanExecute="CommandBinding_CanExecute_1"/>
</Application.Resources>

In your App.xaml.cs file, define corresponding handlers :

  private void CommandBinding_CanExecute_11(object sender, System.Windows.Input.CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = false;
    }

Usage

In any xaml file, use it like below :

 <RichTextBox x:Name="Rtb1" ContextMenuOpening="Rtb1_ContextMenuOpening_1" FontSize="15" Margin="10,10,10,-73">            
        <RichTextBox.CommandBindings>
            <StaticResourceExtension ResourceKey="PasteCommandKey"/>
        </RichTextBox.CommandBindings>
Turpentine answered 15/10, 2016 at 8:59 Comment(3)
Given the explanations and quality of the answers already posted. Your answer isn't really adding value. Please avoid posting code without explanation. I recommend editing your answer to explain first, and show code second. Thank you.Machos
This very specific answer helped me a lot. Nobody else said that it's possible to put <CommandBinding> inside <Application.Resources>. @Machos your comment is invalid.Typo
@Typo - please look at dates and edit history before you commentMachos

© 2022 - 2024 — McMap. All rights reserved.