WPF 命令,如何声明应用程序级命令?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4709906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF Commands, How to declare Application level commands?
提问by ocodo
I'm interested in creating commands that are available from anywhere in my WPF application.
我有兴趣创建可从我的 WPF 应用程序中的任何位置使用的命令。
I'd like them to work in the same way as Cut
, Copy
, Paste
and the other Application level commands, ie:
我希望他们在工作方式相同Cut
,Copy
,Paste
和其他应用程序级别的命令,即:
<Button Command="Paste" />
I assumed I could setup CommandBindings for the Application instance, but that property isn't available.
我以为我可以为 Application 实例设置 CommandBindings,但该属性不可用。
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.
哪个有效,但当然是紧密耦合的,因此非常脆弱。
回答by Shou Takenaka
You can setup CommandBindings for "All Windows" of your WPF application and implement command handlers in Application class.
您可以为 WPF 应用程序的“所有 Windows”设置 CommandBindings,并在 Application 类中实现命令处理程序。
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.
接下来,像这样将自定义命令设置为 Button.Command。
<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.
最后,在 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;
}
}
}
回答by LauLo
StackOverflow members helped me so many time that I decide now to contribute and share ;-)
StackOverflow 成员帮助了我很多次,我现在决定贡献和分享 ;-)
Based on Shou Takenaka's answer, here is my implementation.
基于 Shou Takenaka 的回答,这是我的实现。
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:
接下来,在 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 :-)
这就是所有人:-)
回答by Oskar Sj?berg
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.
首先像往常一样设置您的命令,但为 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
}
}
Add a reference to the namespace of your command in your XAML (last line), like this:
在 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">
Then just reference your static property in your XAML like this:
然后只需在 XAML 中引用您的静态属性,如下所示:
<Button Content="Button" Command="commands:MyCommand.Instance" />
回答by AnjumSKhan
Declare the CommandBinding
at Application
level from where it can be re-used everywhere.
声明可以在任何地方重新使用的CommandBinding
atApplication
级别。
<Application.Resources>
<CommandBinding x:Key="PasteCommandKey" Command="ApplicationCommands.Paste" CanExecute="CommandBinding_CanExecute_1"/>
</Application.Resources>
In your App.xaml.cs
file, define corresponding handlers :
在您的App.xaml.cs
文件中,定义相应的处理程序:
private void CommandBinding_CanExecute_11(object sender, System.Windows.Input.CanExecuteRoutedEventArgs e)
{
e.CanExecute = false;
}
Usage
用法
In any xaml file, use it like below :
在任何 xaml 文件中,像下面这样使用它:
<RichTextBox x:Name="Rtb1" ContextMenuOpening="Rtb1_ContextMenuOpening_1" FontSize="15" Margin="10,10,10,-73">
<RichTextBox.CommandBindings>
<StaticResourceExtension ResourceKey="PasteCommandKey"/>
</RichTextBox.CommandBindings>
回答by Rick Sladkey
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:
如果您尝试将CommandBindings
或定义InputBindings
为您的 中的资源App.xaml
,您会发现您不能使用它们,因为 XAML 不允许您使用:
<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.
因为这些属性都没有“设置”访问器。使用这篇文章中的想法,我想出了一种使用来自App.xaml
或任何其他资源字典的资源的干净方法。
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:
然后从另一个类的 XAML 中引用它们:
<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.
这CollectionSetterBehavior
是一种可重用的行为,它不会将属性“设置”为其值,而是清除集合并重新填充它。所以集合不会改变,只是它的内容。
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.
并将相应的引用添加到您的项目中。