为什么 WPF ApplicationCommands.Close 命令在按钮和菜单项中被禁用?

Why is the WPF ApplicationCommands.Close command disabled on the button and in the menu item?

问题

为什么 WPF ApplicationCommands.Close 命令在按钮和菜单项中被禁用?

代码

<Window x:Class="ApplicationCloseCommand.Views.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:viewModels="clr-namespace:ApplicationCloseCommand.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="169.493" Width="319.273">
    <Window.DataContext>
        <viewModels:MainWindowViewModel />
    </Window.DataContext>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <MenuItem Header="_Exit" Command="Close"/>
            </MenuItem>
        </Menu>
        <Canvas>
            <Button Content="Close" Command="Close" Height="23" VerticalAlignment="Top" Canvas.Left="142" Canvas.Top="31"/>
        </Canvas>
    </DockPanel>
</Window>

研究

Some sources 说自己实现 ICommand 并在命令处理程序中执行 this.Close()。但是,我根本不明白为什么 ApplicationCommands.Close 存在。

Some sources 表示需要对 return true 实施 CanExecute 方法。但是,如果我必须自己做,包括 CloseCommandHandler(来自链接示例),那么 ApplicationCommands.Close 有什么好处?

Some sources提一个CommandTarget。这没有用:

<MenuItem Header="_Exit" Command="Close" CommandTarget="{Binding ElementName=MyMainWindow}"/>

我是不是用 CommandTarget 做错了?

ApplicationCommands,如 NavigationCommandsComponentCommands 公开了一组预定义的通用 UI 命令。它们都是RoutedCommand的实现。 RoutedCommandRoutedUICommand 实施 ICommand

RoutedCommand 没有实现 ExecuteCanExecute 方法。它只是引发相应的 RoutedEvent

此事件将遍历可视化树,直到被 CommandBinding 处理。 CommandBinding 定义或执行 CanExecuteExecute 方法或特殊事件处理程序。这意味着可以在引发 RoutedCommand 的树上的某处定义实际的命令实现。

有一些实现默认 CommandBinding(或命令实现)的控件。 TextBox 可以处理例如ApplicationCommands.Copy RoutedCommand。或者 DocumentViewer 可以处理 NavigationCommands.NextPage RoutedCommand。 ApplicationCommands.Close 没有任何控件提供的支持者或默认实现。

RoutedCommand 旨在用于 UI 上下文,例如控件的执行行为。这种逻辑不应该在视图模型中处理。因此 ApplicationCommands.Close 必须由挂接到可视化树中的控件或附加行为来实现。
对于数据相关的行为或逻辑,您必须提供自己的 ICommand 实现,它具有在视图模型中定义的处理程序。

如果您将 RoutedCommand 例如 ApplicationCommands.Close 分配给命令源,例如Button,但不为此 RoutedCommand 提供处理程序,因此命令源中不存在 CanExecute 处理程序,例如Button 假定 CanExecute 为假并禁用自身。

这意味着您必须提供 CommandBinding:

MainWindow.xaml

<Window>
  <Window.CommandBindings>
    <CommandBinding Command="{x:Static ApplicationCommands.Close}"
                    Executed="ExecutedCloseCommand"
                    CanExecute="CanExecuteCloseCommand" />
  </Window.CommandBindings>

  <Button Command="{x:Static ApplicationCommands.Close}"
          Content="Close Window"/>
</Window>

MainWindow.xaml.cs

private void CanExecuteCloseCommand(object sender,
    CanExecuteRoutedEventArgs e)
{
   e.CanExecute = true;
}

private void ExecutedCloseCommand(object sender,
    ExecutedRoutedEventArgs e)
{
  this.Close();
}