WPF Commands, как объявить команды уровня приложения?
Я заинтересован в создании команд, которые доступны из любого места в моем приложении WPF.
Я бы хотел, чтобы они работали так же, как Cut
, Copy
, Paste
и другие команды прикладного уровня, а именно:
<Button Command="Paste" />
Я предполагал, что могу установить CommandBindings для экземпляра приложения, но это свойство недоступно.
Как это сделать?
Лучшее, что мне удалось сделать, - это создать набор команд в окне верхнего уровня и затем обращаться к ним следующим образом...:
<Button Command="{x:Static namespace::MainWindow.CommandName}" />
Который работает, но, конечно, тесно связан, и поэтому очень хрупкий.
5 ответов
Вы можете настроить привязки команд для "всех окон" вашего приложения 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>
Наконец, реализуйте обработчик команды вашей пользовательской команды в классе Application.
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;
}
}
}
Мне не понравилась сложность других решений, но после нескольких часов исследований я обнаружил, что это действительно просто.
Сначала настройте свою команду, как обычно, но добавьте статическое свойство для WPF, чтобы он мог получить экземпляр вашей команды.
class MyCommand : ICommand
{
// Singleton for the simple cases, may be replaced with your own factory if needed.
static MyCommand()
{
Instance = new MyCommand();
}
public static ICommand Instance { get; private set; }
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" />
Члены Stackru помогли мне так много раз, что я решил сейчас внести свой вклад и поделиться;-)
Основываясь на ответе Шоу Такенака, вот моя реализация.
Мой интерес состоял в том, чтобы создать только один файл многократного использования.
Сначала создайте команду (ы) класса контейнера
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}" />
вот и все, ребята:-)
Если вы попытаетесь определить CommandBindings
или же InputBindings
как ресурсы в вашем App.xaml
вы обнаружите, что не можете их использовать, потому что XAML не позволяет вам использовать также:
<Window ... CommandBindings="{StaticResource commandBindings}">
или установить привязки команд с помощью установщика стиля:
<Setter Property="CommandBindings" Value="{StaticResource commandBindings}">
потому что ни одно из этих свойств не имеет "установленного" метода доступа. Используя идею в этом посте, я придумал чистый способ использования ресурсов из 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"
и добавьте соответствующую ссылку на ваш проект.
Объявить 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>