1

我的示例 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;
    }


}

主页.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>

运行应用程序。确保不要单击文本框或文本框的选项卡。单击文件菜单。菜单已启用。检查查看帮助菜单。它也已启用。当您单击时,您会看到消息框。万事皆安。

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

4

2 回答 2

2

将 FocusManager.IsFocusScope="True" 添加到 TopMenu 用户控件就可以了。

于 2013-05-09T15:54:48.640 回答
1

首先,您不需要在Window2.xaml和中定义 InputBindings TopMenu.xaml。A CommandSource(如 a KeyGesture)被定义并添加到 a RoutedCommandonce for all。

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

  1. Command是要执行的动作。
  2. CommandSource是调用命令的对象(可以是 Control 或 InputGesture)。
  3. CommandTarget是正在执行命令的对象。
  4. CommandBinding是将命令逻辑映射到命令的对象。

RoutedCommands可以从多个触发CommandSources,每个都CommandSource可以定义自己的,树上的CommandTarget每个都可以通过向.UIElementExecutedCanExecuteCommandBindingRoutedCommand

这个怎么运作 :

  1. 当 aCommandSource触发 时RoutedCommand,一个事件正在从直到对象PreviewExecuted向下隧道传输,寻找处理该事件的 a。element treeWindowCommandTargetCommandBinding
  2. 如果e.Handled未设置为True,则事件正在从上Executed冒泡到,寻找合适的处理该事件的对象。element treeCommandTargetWindowCommandBinding

这里重要的是如何CommandTarget定义。当CommandTargeta 的属性CommandSource未定义时,具有焦点的 Control 是 default CommandTargetToolbars和的神奇之处Menu在于他们将CommandTarget孩子的属性设置为当前具有焦点的控件。从技术上讲,他们会查看父级(在您的情况下),并在Focus ScopeWindow中找到最近关注的控件,即. the和 the有一个单独的焦点范围。Window TextboxToolBarMenu

那么您的代码发生了什么:当Textbox具有逻辑焦点时,MenuItems(as a CommandSource)被禁用,但如果您使用定义的KeyGesture(Ctrl+N 和 Ctrl+F1),则执行命令。

CommandSources您为每个定义了两个RoutedCommand

  1. aKeyGesture设置CommandTarget为 TopMenu。直接查看Command目标并找到CommandBinding. 那些CommandSources总是启用的。您按下键并执行命令。
  2. aMenuItemCommandTarget未设置。Window如果焦点范围中有一个,它会动态设置为焦点控件。然后我们有两种情况:

    • Textbox不集中时,不CommandTarget设置。所以Command查看所有的elementWindowCommandBinding在 TopMenu 中找到UserControl。所以 TopMenu 是CommandTarget并且CommandSource是启用的。
    • Textbox聚焦时,CommandTarget使用 this 设置属性TextboxPreviewCanExecute和事件通过和之间的CanExecute隧道和冒泡,但它们不通过 TopMenu因为它不是. 未找到处理程序,则未启用。element treeWindowTextboxUserControlTextboxCommandSource

有几种解决方案:

  1. 最合乎逻辑的方式:在类中定义 theCommandBindings和 the ,因为它是最顶层的父级,并且因为您更像是级别命令,所以它与 UserControl类无关。event handlersWindow2CommandsWindowTopMenu
  2. 手动CommandTarget定义MenuItems. 见下文。
  3. 这就是您所做的,将 TopMenu 的附加属性FocusManager.IsFocusSope设置True为。因此,当Menu查看父 Focus Scope 时,它​​不会查看Window,而是查看 TopMenu。

定义 的CommandTarget属性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}}}/>

小备注:“ApplicationCommands”是可选的,因为有一个转换器可以找到正确的Command

当您需要多个UIElement命令为同一命令定义不同的逻辑时,很高兴了解 WPF 命令机制很好。每个人都UIElement可以按照自己需要的方式执行Command

于 2016-11-10T12:27:40.437 回答