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

时间:2013-05-09 15:16:27

标签: 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: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>

TopMenu.xaml

<UserControl x:Class="SampleWpfApp.TopMenu"
         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" 
         xmlns:local="clr-namespace:SampleWpfApp"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.InputBindings>
    <KeyBinding Gesture="CTRL+N" Command="ApplicationCommands.New" />
    <KeyBinding Gesture="CTRL+F1" Command="{x:Static local:TopMenu.ShowHelp}" />
</UserControl.InputBindings>
<UserControl.CommandBindings>
    <CommandBinding Command="ApplicationCommands.New" Executed="NewExecuted" CanExecute="NewCanExecute"/>
    <CommandBinding x:Name="HelpCmdBinding" CanExecute="AltHelpCanExecute" Executed="AltHelpExecuted" Command="{x:Static local:TopMenu.ShowHelp}" />
</UserControl.CommandBindings>
<DockPanel>
    <Menu DockPanel.Dock="Top">
        <MenuItem Header="_File">
            <MenuItem Command="ApplicationCommands.New" />
            <MenuItem Header="E_xit" InputGestureText="Alt+F4" />
        </MenuItem>
        <MenuItem Header="_Help">
            <MenuItem Header="_View Help" InputGestureText="Ctrl+F1" Command="{x:Static local:TopMenu.ShowHelp}" />
            <MenuItem Header="_About" />
        </MenuItem>
    </Menu>
</DockPanel>

TopMenu.xaml.cs

    public partial class TopMenu : UserControl
{
    public static RoutedCommand ShowHelp = new RoutedCommand("AltHelp", typeof(TopMenu));

    public TopMenu()
    {
        InitializeComponent();
    }


    void NewExecuted(object target, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("The " + ((RoutedCommand)e.Command).Name + " command invoked on " + ((FrameworkElement)target).Name);
    }
    void NewCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    void AltHelpExecuted(object target, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("The " + ((RoutedCommand)e.Command).Name + " command invoked on " + ((FrameworkElement)target).Name);
    }
    void AltHelpCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }


}

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>

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

但是当我单击文本框时,菜单被禁用。在重新启动应用程序并且不单击文本框之前,我无法再次启用菜单。 (尽管使用手势仍会触发消息框)。有人可以帮我识别问题吗?这让我疯狂了一段时间:(

2 个答案:

答案 0 :(得分:2)

向TopMenu用户控件添加FocusManager.IsFocusScope =“True”就可以了。

答案 1 :(得分:0)

首先,您不需要在Window2.xamlTopMenu.xaml中定义InputBindings。 CommandSource(如KeyGesture)已定义并一次性添加到RoutedCommand

RoutedCommand是WPF在四个概念中分离命令机制的方法:

  1. Command是要执行的操作。
  2. CommandSource是调用命令的对象(可以是Control或InputGesture)。
  3. CommandTarget是正在执行命令的对象。
  4. CommandBinding是将命令逻辑映射到命令的对象。
  5. RoutedCommands可以从多个CommandSources触发,每个CommandSource可以定义自己的CommandTarget,树上的每个UIElement都可以实现Executed通过向CanExecute添加CommandBinding来为{}}和RoutedCommand个事件处理程序。

    工作原理:

    1. CommandSource触发RoutedCommand时,PreviewExecuted事件正在element treeWindow隧道向CommandTarget对象挖掘,寻找处理事件的CommandBinding
    2. 如果e.Handled未设置为True,则Executed事件会将element treeCommandTarget冒泡到Window ,寻找适合处理事件的CommandBinding
    3. 这里重要的是如何定义CommandTarget。如果未定义CommandTarget的{​​{1}}属性,则控件的焦点是默认CommandSourceCommandTargetToolbars的神奇之处在于它们设置了Menu属性 他们的孩子对目前有重点的控制。从技术上讲,他们会查看父级,在您的情况下为CommandTarget,并在Window 焦点范围中查找最近关注的控件,即{{1 }}。 WindowTextbox有一个单独的焦点范围。

      那么您的代码会发生什么:当ToolBar具有逻辑焦点时,Menu(作为Textbox)被禁用,但如果您使用定义的MenuItems(Ctrl + N和Ctrl + F1)执行命令。

      您为每个CommandSource定义了两个KeyGesture

      1. CommandSources RoutedCommand设置为TopMenu。 KeyGesture会直接查看目标并找到CommandTarget。那些Command总是被启用。你按下键并执行命令。
      2. 未设置CommandBinding的{​​{1}}。如果CommandSources焦点范围中有一个,则动态设置为焦点控制。然后我们有两个场景:

        • 如果未关注MenuItem,则不会设置CommandTarget。因此,Window会查看Textbox的所有CommandTarget树,并在TopMenu Command中找到element。因此,TopMenu是Window,而CommandBinding已启用。
        • 关注UserControl时,CommandTarget属性设置为此CommandSourceTextboxCommandTarget事件正在TextboxPreviewCanExecute之间CanExecute进行隧道挖掘和冒泡,但他们不会通过TopMenu element tree,因为它不是Window的父级。找不到处理程序,然后Textbox未启用。
      3. 有几种解决方案:

        1. 最合乎逻辑的方式:定义UserControl课程中的TextboxCommandSource,因为它是最顶级的父级,因为您的CommandBindingsevent handlers更像是Window2级命令,它与UserControl Commands类无关。
        2. 手动定义Window的{​​{1}}属性。见下文。
        3. 这就是您所做的,为TopMenu将附加属性TopMenu设置为CommandTarget。因此,当MenuItems查看父焦点范围时,它不会查看FocusManager.IsFocusSope,它会查看TopMenu。
        4. 定义True的{​​{1}}属性:

          Menu

          小评:&#34; ApplicationCommands&#34;是可选的,因为有一个转换器找到了正确的Window

          当您需要多个CommandTarget为同一命令定义不同的逻辑时,了解WPF命令机制是很好的。每个MenuItems都可以按照自己需要的方式在本身上执行<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}}}/>