WPF防止事件冒泡到控件之外

时间:2012-04-13 09:59:25

标签: wpf events routed-commands

让我们从元素树开始。最顶层的元素是一个WPF窗口,它注册了ApplicationCommands.Find命令。 一些子元素具有KeyBinding,手势键ENTER指向此命令。没关系,如果有人按下ENTER,命令将被执行。 其中我有一个带有弹出窗口的Custom控件,用于搜索和选择一些元素。在这个地方, 我不想允许在自定义控件之外冒泡关键事件。但我设置了e.handled = true KeyDown += new KeyEventHandler (..),它应该是一个冒泡的路由事件,该自定义控件内的所有控件(文本框等)都将忽略任何类型的输入。 但我只是想停止在CustomControl的级别冒泡,说:从叶子到最顶层的控制父级,不再进一步! 这是为什么?我没有过滤PreviewKeyDown,所以事件必须传播到叶子,然后它应该回到父,但它不会。

谢谢

已编辑:示例代码:

<UserControl x:Class="WpfApplication5.TestUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <StackPanel Orientation="Vertical">        
        <TextBox />
        <TextBox />
        <TextBox />
    </StackPanel>
</UserControl>


/* 
 * I want to stop bubbling at this level. Let us say if we click 'enter' on the level of the TextBox leaf, 
 * it should propagate until the TestUserControl is reached and then stop.
 */
public partial class TestUserControl : UserControl
{
    public TestUserControl()
    {
        InitializeComponent();
    }
}


<Window x:Class="WpfApplication5.Window6"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication5"
    Title="Window6" Height="300" Width="300">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Find" Executed="CommandBinding_Executed" />
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Gesture="Enter" Command="ApplicationCommands.Find" />
    </Window.InputBindings>
    <Grid>
        <local:TestUserControl />
    </Grid>
</Window>

public partial class Window6 : Window
{
    public Window6()
    {
        InitializeComponent();
    }

    private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("Executed");
    }
}

1 个答案:

答案 0 :(得分:1)

您希望将事件从自定义控件桥接到窗口,对吧? 然后,您必须从Window中重新启动事件以供系统处理。如果您只将其标记为已处理,则表示不会再进行任何操作。

代码应该是这样的:

private void CustomControl_KeyDown(object sender, KeyEventArgs e)
{
    e.Handled = true;
    var relaunchedEvent = new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key);
    relaunchedEvent.RoutedEvent = Keyboard.KeyDownEvent;
    relaunchedEvent.Source = e.OriginalSource;
    TopMostWindow.RaiseEvent(relaunchedEvent);
} 

- 编辑 -

使用以下xaml测试上面的代码,以查看如何在不执行命令的情况下在窗口中触发keydown事件。

<Window x:Class="WpfApplication5.Window6"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication5"
    Title="Window6" Height="300" Width="300"
    x:Name="TopMostWindow">
    <Grid>
        <Grid KeyDown="CustomControl_KeyDown">
            <local:UserControl1/>
        </Grid>
        <Grid.CommandBindings>
            <CommandBinding Command="ApplicationCommands.Find" Executed="CommandBinding_Executed" />
        </Grid.CommandBindings>
        <Grid.InputBindings>
            <KeyBinding Key="Enter" Command="ApplicationCommands.Find" />
        </Grid.InputBindings>
    </Grid>
</Window>

重要说明:

  • 命令处于更高级别,因此使用冒泡或隧道事件无关紧要,最终命令将被执行,除非事件被标记为已处理。
  • 提升Keydown事件实际上并不意味着按键,因为文本合成不会发生。

我对你的案例的建议是过滤你的用户控件中的keydown事件,如果e.Key=Key.Enter将其标记为已处理,因为Enter对文本框没有太大作用。否则你将不得不模拟按键。