当焦点在文本框上时,WPF应用程序菜单被禁用,并且在应用程序重新启动之前不会工作

当焦点在文本框上时,WPF应用程序菜单被禁用,并且在应用程序重新启动之前不会工作,wpf,commandbinding,Wpf,Commandbinding,我的示例WPF应用程序中的AppMenus有问题 Window2.xaml: <Window x:Class="SampleWpfApp.Window2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SampleWpf

我的示例WPF应用程序中的AppMenus有问题

Window2.xaml:

<Window x:Class="SampleWpfApp.Window2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SampleWpfApp"
    Name="RootWindow"
    Title="Window2" Height="600" Width="800">
<Window.InputBindings>
    <KeyBinding Gesture="CTRL+N" Command="ApplicationCommands.New" CommandTarget="{Binding ElementName=TopMenu}" />
    <KeyBinding Gesture="CTRL+F1" Command="{x:Static local:TopMenu.ShowHelp}" CommandTarget="{Binding ElementName=TopMenu}" />
</Window.InputBindings>    
<DockPanel>
    <local:TopMenu DockPanel.Dock="Top" x:Name="TopMenu" />
    <ContentControl>
        <local:Home x:Name="MainContent" />
    </ContentControl>
</DockPanel>
Home.xaml

<UserControl x:Class="SampleWpfApp.Home"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" Margin="10,37,0,0"/>
    <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" Margin="10,86,0,0"/>
    <Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10,127,0,0"/>
</Grid>

运行应用程序。确保不要单击文本框或文本框的选项卡。单击“文件”菜单。菜单已启用。检查查看帮助菜单。它也已启用。当您单击时,您将看到消息框。一切都很好


但当我点击文本框时,菜单被禁用。在重新启动应用程序并且不单击文本框之前,我无法再次启用菜单。(不过,使用手势仍会触发messagebox)。有人能帮我确定这个问题吗?这让我疯狂了一段时间:(

将FocusManager.IsFocusScope=“True”添加到TopMenu用户控件中就成功了。

首先,您不需要在
Window2.xaml
TopMenu.xaml
.A
CommandSource
中定义输入绑定(就像
按键手势一样)一次定义并添加到
RoutedCommand

RoutedCommand
是WPF将命令机制分为四个概念的方法:

  • 命令
    是要执行的操作
  • CommandSource
    是调用命令的对象(可以是控件或输入手势)
  • CommandTarget
    是在其上执行命令的对象
  • CommandBinding
    是将命令逻辑映射到命令的对象
  • RoutedCommands
    可以从多个
    CommandSources
    触发,每个
    CommandSource
    可以定义自己的
    CommandTarget
    ,树上的每个
    UIElement
    可以通过向
    RoutedCommand

    工作原理:

  • CommandSource
    触发
    RoutedCommand
    时,一个
    PreviewExecuted
    事件从
    窗口
    沿
    元素树向下挖掘,直到
    CommandTarget
    对象,寻找处理该事件的
    CommandBinding
  • 如果
    e.Handled
    未设置为
    True
    ,则执行的
    事件将
    元素树
    CommandTarget
    冒泡到
    窗口
    ,寻找处理该事件的合适
    CommandBinding
  • 这里重要的是如何定义
    CommandTarget
    。当未定义
    CommandSource
    CommandTarget
    属性时,具有焦点的控件是默认的
    CommandTarget
    工具栏和
    菜单的神奇之处在于它们设置了
    CommandTarget
    性质 他们的子控件指向当前具有焦点的控件。从技术上讲,他们查看父控件,即您案例中的
    窗口,并在
    窗口
    焦点范围中找到最近聚焦的控件,即
    文本框
    工具栏
    菜单
    具有单独的焦点范围

    那么您的代码发生了什么:当
    文本框
    具有逻辑焦点时,
    菜单项
    (作为
    命令源
    )将被禁用,但如果您使用定义的
    键手势
    (Ctrl+N和Ctrl+F1),命令将被执行

    您已经为每个
    RoutedCommand
    定义了两个
    CommandSources

  • CommandTarget
    设置为TopMenu的
    Key手势
    命令
    直接查看目标并找到
    CommandBinding
    。这些
    CommandSources
    始终处于启用状态。您按下键,命令就会执行
  • 未设置带有
    CommandTarget
    MenuItem
    。如果
    窗口中有聚焦控件,则会将其动态设置为聚焦控件。然后我们有两种情况:

    • 文本框
      未聚焦时,不会设置
      命令目标
      。因此
      命令
      会查看
      窗口
      的所有
      元素
      树,并在TopMenu
      用户控件
      中找到
      命令绑定
      。因此TopMenu是
      命令目标
      命令数据源
      已启用
    • 当聚焦
      Textbox
      时,
      CommandTarget
      属性使用该
      Textbox
      进行设置。
      PreviewCanExecute
      CanExecute
      事件在
      窗口
      文本框
      之间通过
      元素树
      进行隧道和冒泡,但它们不会通过TopMenu
      UserControl
      ,因为它不是
      Textbox
      的父项。如果未找到任何处理程序,则不会启用
      命令源
  • 有几种解决方案:

  • 最合乎逻辑的方法是:在
    Window2
    类中定义
    命令绑定
    事件处理程序
    ,因为它是最顶层的父类,并且因为您的
    命令
    更像
    窗口
    级别的命令,所以它与UserControl
    TopMenu
    类无关
  • 手动定义
    MenuItems
    CommandTarget
    属性。请参见下文
  • 这就是您所做的,将TopMenu的附加属性
    FocusManager.IsFocusScope
    设置为
    True
    。因此当
    菜单查看父焦点范围时,它不会查看
    窗口,而是查看TopMenu
  • 定义
    MenuItems
    CommandTarget
    属性:

    <MenuItem Command="New" CommandTarget={Binding RelativeSource=
        {RelativeSource AncestorType={x:Type local:TopMenu}}}/>
    ...
    <MenuItem Header="_View Help" InputGestureText="Ctrl+F1"
        Command="{x:Static local:TopMenu.ShowHelp}"
        CommandTarget={Binding RelativeSource={RelativeSource
        AncestorType={x:Type local:TopMenu}}}/>
    
    
    ...
    
    小注释:“ApplicationCommand”是可选的,因为有一个转换器可以找到
    <UserControl x:Class="SampleWpfApp.Home"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" Margin="10,37,0,0"/>
        <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" Margin="10,86,0,0"/>
        <Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10,127,0,0"/>
    </Grid>
    
    <MenuItem Command="New" CommandTarget={Binding RelativeSource=
        {RelativeSource AncestorType={x:Type local:TopMenu}}}/>
    ...
    <MenuItem Header="_View Help" InputGestureText="Ctrl+F1"
        Command="{x:Static local:TopMenu.ShowHelp}"
        CommandTarget={Binding RelativeSource={RelativeSource
        AncestorType={x:Type local:TopMenu}}}/>