下面的代码是实现一个自定义的WPF命令。我只将第一个按钮(标题为"退出"(与CommandBinding
绑定,因此当单击Exit
按钮并且e.CanExecute
在CommandBinding_CanExecute
事件中为true时,CommandBinding_Executed
事件将关闭应用程序。使用"退出"按钮可以很好地使用此场景。但是,当单击未与任何命令绑定的btnTest
按钮时,也会调用CommandBinding_CanExecute
事件。这可以通过在btnTest_Click
事件上放置断点来测试,并注意到在代码退出该事件后,光标将转到CommandBinding_CanExecute
事件。
问题:尽管CommandBinding
仅用于Exit
按钮,但为什么btnTest
按钮也在调用CommandBinding_CanExecute
事件。我可能遗漏了什么,我们如何解决这个问题?
备注为了简洁起见,我简化了这个问题。但在实际场景中,CommandBinding_CanExecute
中的e.CanExecute
值通过调用一个函数设置为true,该函数执行一个长复杂逻辑,该逻辑根据退出按钮的特定场景返回true或false。我不希望在单击其他按钮(例如btnTest
(时执行那种冗长的逻辑。
主窗口.Xaml:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Exit" Command="local:CustomCommands.Exit">
<Button.CommandBindings>
<CommandBinding Command="local:CustomCommands.Exit" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/>
</Button.CommandBindings>
</Button>
<Button x:Name="btnTest" Content="Test" Click="btnTest_Click" Margin="10"/>
</StackPanel>
</Grid>
</Window>
主窗口.Xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnTest_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Why this event is calling ExitCommand_CanExecute");
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
Application.Current.Shutdown();
}
}
public static class CustomCommands
{
public static readonly RoutedUICommand Exit = new RoutedUICommand
(
"Exit",
"Exit",
typeof(CustomCommands),
new InputGestureCollection()
{
new KeyGesture(Key.F4, ModifierKeys.Alt)
}
);
}
您为什么认为btnTest
在调用CommandBinding_CanExecute
?事实并非如此。
每当CommandManager
想要知道命令的当前状态时,它就会调用命令的CanExecute
方法。你无法控制这种情况何时发生。该框架的确如此。它未连接到btnTest
。
如果CanExecute
中有一些复杂的逻辑,则应考虑创建一个自定义命令类,该类实现ICommand
接口,并在希望框架通过调用其CanExecute
方法刷新命令状态时引发CanExecuteChanged
事件。通过这种方式,您可以控制何时刷新命令。
然后可以将Button
的Command
属性绑定到自定义命令类的实例。如果你在谷歌上搜索";DelegateCommand"或";RelayCommand";,你应该找到很多例子。这篇博文可能是一个很好的起点。
wpf的设计者认为与UI的任何交互都是重要的,这将间接启动对所有绑定的canexecute的检查。想法是你改变了一些事情,做了一些事情或其他事情。最好检查是否仍应启用所有这些命令。
实际上调用的是commandmanager.requerysuggested((。这不会直接调用canexecute。它的作用是告诉命令,它们应该去检查它们是否仍然可以执行。这并不完全疯狂,因为当你的按钮的命令调用一些代码时,如果用户点击其他按钮,你的视图模型将部分更新或处于某种不确定状态,
您永远不应该使用canexecute驱动其他逻辑。
在基本视图模型中添加一个bool IsBusy并检查它是否有任何事情在做,并且不应该允许用户做其他事情,这是非常常见的。
在IsBusy上的命令中进行额外的检查就是这种模式的一部分。