据我所知,Command模式的目标是帮助将UI交互与应用程序逻辑分开.使用正确实现的命令,单击"打印"菜单项可能会产生一系列交互,如下所示:
(button) ---click executes command----> (command) ---calls Print() in app logic ---> (logic)
Run Code Online (Sandbox Code Playgroud)
这鼓励您将UI与应用程序逻辑分开.
我一直在研究WPF命令,在大多数情况下,我看到他们是如何实现这种模式的.但是,我觉得在某种程度上它们使命令模式变得复杂,并设法以不鼓励将UI与应用程序逻辑分离的方式实现它.
例如,考虑这个简单的WPF窗口,它有一个按钮将文本粘贴到文本框中:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Paste"
Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<StackPanel>
<TextBox x:Name="txtData" />
<Button Command="Paste" Content="Paste" />
</StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)
这是代码隐藏:
namespace WpfApplication1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
ApplicationCommands.Paste.Execute(null, txtData);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我从命令中获得了什么?在我看来,我可以轻松地将命令绑定事件处理程序中的代码放入按钮的Click事件中.当然,现在我可以将多个UI元素与粘贴命令相关联,我只需要使用一个事件处理程序,但是如果我想粘贴到几个不同的文本框呢?我必须使事件处理程序逻辑更复杂或编写更多的事件处理程序.所以现在,我觉得我有这个:
(button) ---executes Routed Command---> (Window) ---executes command …Run Code Online (Sandbox Code Playgroud) 我正在使用MVVM设计模式构建应用程序,我想使用ApplicationCommands类中定义的RoutedUICommands.由于View的CommandBindings属性(读取UserControl)不是DependencyProperty,因此我们无法将ViewModel中定义的CommandBindings直接绑定到View.我通过定义一个抽象的View类来解决这个问题,该类基于ViewModel接口以编程方式绑定它,该接口确保每个ViewModel都有一个ObBableCollection的CommandBindings.这一切都很好,但是,在某些情况下我想执行在不同类(View和ViewModel)相同命令中定义的逻辑.例如,保存文档时.
在ViewModel中,代码将文档保存到磁盘:
private void InitializeCommands()
{
CommandBindings = new CommandBindingCollection();
ExecutedRoutedEventHandler executeSave = (sender, e) =>
{
document.Save(path);
IsModified = false;
};
CanExecuteRoutedEventHandler canSave = (sender, e) =>
{
e.CanExecute = IsModified;
};
CommandBinding save = new CommandBinding(ApplicationCommands.Save, executeSave, canSave);
CommandBindings.Add(save);
}
Run Code Online (Sandbox Code Playgroud)
乍一看,前面的代码就是我想要做的,但是文档绑定到的View中的TextBox只在它失去焦点时才更新它的Source.但是,我可以通过按Ctrl + S保存文档而不会失去焦点.这意味着文档在源中更新的更改之前保存,实际上忽略了更改.但是,由于出于性能原因将UpdateSourceTrigger更改为PropertyChanged不是一个可行的选项,因此在保存之前必须强制更新其他内容.所以我想,让我们使用PreviewExecuted事件强制更新PreviewExecuted事件,如下所示:
//Find the Save command and extend behavior if it is present
foreach (CommandBinding cb in CommandBindings)
{
if (cb.Command.Equals(ApplicationCommands.Save))
{
cb.PreviewExecuted += (sender, e) =>
{
if (IsModified)
{
BindingExpression be = rtb.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
} …Run Code Online (Sandbox Code Playgroud) 我在应用程序中遇到了MVVM.我有一个TabControl,其每个Tab都有一个控件,允许以某种方式编辑文件.例如,文本文件或图像.每个控件都绑定到ViewModel区域中的一个类(如图所示).我也有RoutedUICommands.其中一个命令是WordWrap,只有在文本文件的情况下才有意义.此命令在MenuItem或Button等中使用.我想仅在文本控件是此命令的目标时启用它.如何根据MVVM正确地做到这一点?
更多细节:
窗口的XAML具有:
<Window.CommandBindings>
<CommandBinding Command="local:EditorCommands.WordWrap"
Executed="WordWrapExecuted"
CanExecute="CommandCanBeExecutedWhenAnythingIsOpen"/>
</Window.CommandBindings>
Run Code Online (Sandbox Code Playgroud)
菜单项以这种方式使用:
<MenuItem Command="local:EditorCommands.WordWrap"/>
Run Code Online (Sandbox Code Playgroud)
第一件事是Executed和CanExecute事件处理程序:它们在Window类中,虽然我理解MVVM是什么,逻辑应该只在ViewModel中.
而且,这些函数中的发送者是窗口的实例.为什么它不是命令的目标?
我应该为每个命令编写自己的RoutedUICommand后继吗?
我来自第三方控制.它正在实现ApplicationCommands.SelectAll.但是我想要的行为略有不同.没有我可以覆盖的虚方法,当我注册类处理程序时,就像这样
CommandManager.RegisterClassCommandBinding(typeof(MyDerivedControl), new CommandBinding(ApplicationCommands.SelectAll, new ExecutedRoutedEventHandler(OnExecutedSelectAll), new CanExecuteRoutedEventHandler(OnCanExecuteSelectAll)));
Run Code Online (Sandbox Code Playgroud)
我的方法没有被调用.我得出的第三方控制是标记
e.Handled=true;
在它的命令处理程序(我知道这个原因,我已经看到了源,但我不能修改它)
我能做什么?
我正在尝试了解如何使用RoutedCommands.我的印象是,如果我没有在Button上指定CommandTarget,任何聚焦元素都将收到命令.但由于某种原因,它不起作用.这是不起作用的xaml:
<Window x:Class="WpfTest11_Commands2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Height="177" HorizontalAlignment="Left"
Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="233" AcceptsReturn="True" />
<TextBox Height="177" HorizontalAlignment="Left"
Margin="258,12,0,0" Name="textBox2" VerticalAlignment="Top" Width="233" AcceptsReturn="True" />
<Button Content="Cut"
Height="23" HorizontalAlignment="Left" Margin="12,195,0,0" Name="button1" VerticalAlignment="Top" Width="75"
Command="ApplicationCommands.Cut"/>
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
如果我将CommandTarget添加到Button它可以工作,但仅适用于当然指定的文本框.
<Window x:Class="WpfTest11_Commands2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Height="177" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="233" AcceptsReturn="True" />
<TextBox Height="177" HorizontalAlignment="Left" Margin="258,12,0,0" Name="textBox2" VerticalAlignment="Top" Width="233" AcceptsReturn="True" />
<Button Content="Cut"
Height="23" HorizontalAlignment="Left" Margin="12,195,0,0" Name="button1" VerticalAlignment="Top" Width="75"
Command="ApplicationCommands.Cut"
CommandTarget="{Binding ElementName=textBox1}"/>
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
如何使任何聚焦元素接收命令?
谢谢!
列表中的项目具有上下文菜单.上下文菜单项绑定到路由命令.
如果列表控件是a ListBox,则上下文菜单项可正常工作,但是一旦我将其降级为ItemsControl不再有效,它就会正常工作.具体来说,菜单项始终是灰色的.CanExecute我的回调CommandBinding也没有被调用.
它是什么ListBox允许上下文菜单项与命令正确绑定?
以下是一些示例应用程序的摘录,我将它们放在一起以突出显示问题:
<!-- Data template for items -->
<DataTemplate DataType="{x:Type local:Widget}">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="UseWidget"
Command="{x:Static l:WidgetListControl.UseWidgetCommand}"
CommandParameter="{Binding}" />
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=Price}" />
</StackPanel>
</DataTemplate>
<!-- Binding -->
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static l:WidgetListControl.UseWidgetCommand}"
Executed="OnUseWidgetExecuted"
CanExecute="CanUseWidgetExecute" />
</UserControl.CommandBindings>
<!-- ItemsControl doesn't work... -->
<ItemsControl ItemsSource="{Binding Path=Widgets}" />
<!-- But change it to ListBox, and it works! -->
<ListBox ItemsSource="{Binding Path=Widgets}" />
Run Code Online (Sandbox Code Playgroud)
这是视图模型和数据项的C#代码: …
我想重写 WPF 文本框的 RoutedUICommand“复制”行为。
是否可以不创建一个继承自 TextBox 的新 TextBoxExtended 类?
我已经达到了这一点,但现在我有点迷失了。
Private Sub tbSource_PreviewExecuted(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)
Dim commandName = DirectCast(e.Command, Input.RoutedUICommand).Text
If commandName = "Copy" Then
End If
End Sub
Run Code Online (Sandbox Code Playgroud)
您知道如何继续吗?
我正在尝试为我的MVVM应用程序实现高度响应的UI,因此我选择让所有命令处理程序自动执行,BackgroundWorker以便它们不会阻止UI.
但与此同时,我不希望用户在后台执行时仍能执行相同的命令.解决方案似乎很明显:
Executed调用处理程序,有CanExecute处理程序返回falseBackgroundWorker异步BackgroundWorker完成后,有CanExecute处理程序返回true一次.问题是,我需要在步骤1之后通知WPF,然后在步骤3之后再次通知WPF CanExecute,并且应该是必需的.我知道我可以通过调用来完成它CommandManager.InvalidateRequerySuggested,但这会导致CanExecute所有其他命令的处理程序被重新查询.有很多命令,这不好.
有没有办法要求重新查询特定命令 - 即当前正在执行的命令?
TIA
基本上,我有一个自定义控件FooControl。
public class FooControl : ItemsControl
{
//Code
}
Run Code Online (Sandbox Code Playgroud)
我需要添加一些事件处理,但我更喜欢使用 Commanding,而不是使用 RoutedEvent。但我不太确定如何去做这件事。如果我想要它,那么当 Bar1Property (DependencyProperty) 更改时,它会引发 Execute 关联的执行属性。我通过 .NET Reflector 查看了 ButtonBase 代码,哇,这看起来太复杂了。添加命令这么复杂吗?显然,我还必须做到这一点,以便我的控件根据 CanExecuteChanged 是否更改来启用/禁用其自身的某些部分。但我想那是另一部分。
这是到目前为止我的 OnBar1Changed 函数......
private static void OnBar1Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FooControl element = (FooControl)obj;
//What to do here?
}
Run Code Online (Sandbox Code Playgroud) 这是我的代码:
var commandBinding = new CommandBinding(ApplicationCommand.New);
commandBinding.PreviewExecuted += OnPreviewExecuted;
commandBinding.Executed += OnExecuted;
CommandBindings.Add(commandBinding);
void OnPreviewExecuted(object sender, ExecutedRoutedEventArgs e) {
e.Handled = false;
}
void OnExecuted(object sender, ExecutedRoutedEventArgs e) {
DoSomething();
}
Run Code Online (Sandbox Code Playgroud)
MSDN说:"......如果未处理预览事件,则会在命令目标上引发Executed事件."
这对PreviewCanExecute事件可以正常工作.但在这种情况下,当PreviewExecuted-Event正在侦听时,不会调用Executed-Event.
我没有找到关于这个主题的任何内容,所以我想问一下,这个行为是有意还是不正确.
我正在尝试根据列表视图项目数据执行方法。除此之外,触发命令的按钮应该只在列表视图项的“CanExecute”方法返回 true 时启用。
“MyCommand”和“CanExecute”这两种方法都包含在我的 ViewModel 中。不幸的是,我不确定如何将项目信息正确地传递给这两种方法以符合 PRISM 6 框架。
所以我的第一种方法是这样做:
模型
public class MyModel
{
public string Name { get; set; }
public string Version { get; set; }
public int Identifier { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
视图模型
public class MyViewModel : BindableBase
{
private ObservableCollection<MyModel> _models = new ObservableCollection<MyModel>();
public ObservableCollection<MyModel> Models
{
get { return _models; }
set { SetProperty(ref _models, value); }
}
public DelegateCommand VerifyCommand { get; set; }
public MyViewModel()
{
//Add test data
for …Run Code Online (Sandbox Code Playgroud) RoutedUICommand从代码隐藏手动执行自定义时,如下所示:
MyCommands.MyCommand.Execute(parameter, target)
Run Code Online (Sandbox Code Playgroud)
我需要先调用CanExecute方法还是已经在Execute方法中完成了?
我正在玩RoutedCommand,我遇到了一个问题,我找到了如何传递一个参数,以便我的Executed方法可以在e.Parameter中使用它?
我的路由命令:
public static readonly RoutedCommand Foo = new RoutedCommand();
Run Code Online (Sandbox Code Playgroud)
用法:
menuItem.Command = Commands.Foo;
Run Code Online (Sandbox Code Playgroud)
执行:
private void Foo_Executed(object sender, ExecutedRoutedEventArgs e)
{
object parameter = e.Parameter; // this is always null
}
Run Code Online (Sandbox Code Playgroud) routed-commands ×13
wpf ×11
c# ×5
mvvm ×3
.net ×2
c#-4.0 ×1
command ×1
icommand ×1
itemscontrol ×1
listbox ×1
prism ×1
wpf-controls ×1
xaml ×1