为什么带有一个按钮的WPF CommandBinding会影响另一个按钮



下面的代码是实现一个自定义的WPF命令。我只将第一个按钮(标题为"退出"(与CommandBinding绑定,因此当单击Exit按钮并且e.CanExecuteCommandBinding_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事件。通过这种方式,您可以控制何时刷新命令。

然后可以将ButtonCommand属性绑定到自定义命令类的实例。如果你在谷歌上搜索";DelegateCommand"或";RelayCommand";,你应该找到很多例子。这篇博文可能是一个很好的起点。

wpf的设计者认为与UI的任何交互都是重要的,这将间接启动对所有绑定的canexecute的检查。想法是你改变了一些事情,做了一些事情或其他事情。最好检查是否仍应启用所有这些命令。

实际上调用的是commandmanager.requerysuggested((。这不会直接调用canexecute。它的作用是告诉命令,它们应该去检查它们是否仍然可以执行。这并不完全疯狂,因为当你的按钮的命令调用一些代码时,如果用户点击其他按钮,你的视图模型将部分更新或处于某种不确定状态,

您永远不应该使用canexecute驱动其他逻辑。

在基本视图模型中添加一个bool IsBusy并检查它是否有任何事情在做,并且不应该允许用户做其他事情,这是非常常见的。

在IsBusy上的命令中进行额外的检查就是这种模式的一部分。

最新更新