WPF Commands, How to declare Application level commands?

后端 未结 5 673
不思量自难忘°
不思量自难忘° 2020-12-09 03:41

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

相关标签:
5条回答
  • 2020-12-09 04:21

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-09 04:24

    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" />
    
    0 讨论(0)
  • 2020-12-09 04:29

    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>
    
    0 讨论(0)
  • 2020-12-09 04:33

    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.

    0 讨论(0)
  • 2020-12-09 04:42

    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 :-)

    0 讨论(0)
提交回复
热议问题