WPF Commands, как объявить команды уровня приложения? - PullRequest
16 голосов
/ 17 января 2011

Я заинтересован в создании команд, доступных в любом месте моего приложения WPF.

Я бы хотел, чтобы они работали так же, как Cut, Copy, Paste идругие команды уровня приложения, например:

<Button Command="Paste" />

Я предполагал, что мог бы установить привязки команд для экземпляра приложения, но это свойство недоступно.

Как это сделать?

Лучшее, что мне удалось сделать, - это создать набор команд в окне верхнего уровня и затем обращаться к ним следующим образом ...:

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

, который работает, но, конечно, тесно связани так чрезвычайно хрупкий.

Ответы [ 5 ]

34 голосов
/ 17 января 2011

Вы можете настроить привязки команд для «всех окон» своего приложения WPF и реализовать обработчики команд в классе приложений.

Прежде всего, создайте класс контейнера статических команд.Например,

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

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

Далее установите для вашей пользовательской команды значение Button.Command следующим образом.

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

Наконец, реализуйте обработчик команды вашей пользовательской команды в классе приложения.*

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;
        }
    }
}
5 голосов
/ 28 марта 2013

Участники StackOverflow помогали мне так много раз, что я решил сейчас внести свой вклад и поделиться; -)

Основываясь на ответе Шоу Такенака, вот моя реализация.

Мой интерес состоял в том, чтобы производить только один файл многократного использования .


Сначала создайте класс контейнера (ов) для команд

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;
        }
    }
}

Затем объявите ресурс в 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>

Наконец, свяжите любое свойство команды со свойством ресурса приложения:

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

, вот и все: -)

4 голосов
/ 06 июня 2015

Мне не понравилась сложность других решений, но после нескольких часов исследований я обнаружил, что это действительно просто.

Сначала настройте свою команду, как обычно, но добавьте статическое свойство дляWPF, чтобы он мог получить экземпляр вашей команды.

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       
    }   
}

Добавьте ссылку на пространство имен вашей команды в вашей XAML (последняя строка), например:

<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">     

Затем просто укажите ссылку на ваше статическое свойство в вашем XAML следующим образом:

<Button Content="Button" Command="commands:MyCommand.Instance" />
1 голос
/ 15 октября 2016

Объявите CommandBinding на уровне Application, откуда он может быть использован везде.

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

В вашем файле App.xaml.cs определите соответствующие обработчики:

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

Использование

В любом файле xaml используйте его, как показано ниже:

 <RichTextBox x:Name="Rtb1" ContextMenuOpening="Rtb1_ContextMenuOpening_1" FontSize="15" Margin="10,10,10,-73">            
        <RichTextBox.CommandBindings>
            <StaticResourceExtension ResourceKey="PasteCommandKey"/>
        </RichTextBox.CommandBindings>
1 голос
/ 17 января 2011

Если вы попытаетесь определить CommandBindings или InputBindings как ресурсы в вашем App.xaml, вы обнаружите, что вы не можете их использовать, поскольку XAML не позволяет вам использовать либо:

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

или для установки привязки команд с помощью установщика стиля:

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

потому что ни одно из этих свойств не имеет метода доступа set. Используя идею в этом посте , я придумал чистый способ использования ресурсов из App.xaml или любого другого словаря ресурсов.

Сначала вы определяете привязки команд и ввода косвенно, как и любой другой ресурс:

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

и затем вы ссылаетесь на них из XAML другого класса:

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

CollectionSetterBehavior - это повторно используемое поведение, которое не «устанавливает» свойство в его значение, а вместо этого очищает коллекцию и повторно заполняет ее. Таким образом, коллекция не меняется, только ее содержимое.

Вот источник поведения:

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);
    }
}

Если вы не знакомы с поведением, сначала добавьте это пространство имен:

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

и добавьте соответствующую ссылку на ваш проект.

...