如何防止InvokeCommandAction将事件传播到父元素?

16

我发现使用与EventTrigger相关联的InvokeCommandAction时,原始事件仍然会一直路由到父元素,直到它被处理。嗯,我想这是预期行为。但我的问题是如何将事件标记为Handled,以便它不会在整个UI树中传播?

实际上,在命令中处理此事件后,所有内容都将在该命令中处理,因此不需要传播。但是在我找到的一个角落情况下,它会导致一些不希望的行为。例如,当用户双击元素(MouseDoubleClick事件)时,我打开一个新窗口。问题是新窗口打开,然后主窗口回到了新窗口前面,因为MouseDoubleClick事件刚好到达UI树中的顶级元素。期望的行为是保持新窗口在前面,但是由于InvokeCommandAction允许事件传播,所以主窗口重新获得了焦点...

我可以使用CallMethodAction资源来解决这个问题,但是由于我处于MVVM场景中,因此不想在我的代码中使用UI事件参数。即使这样会让我隐式地标记事件为已处理并修复问题。

<UserControl x:Class="..."
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseDoubleClick">
            <i:InvokeCommandAction Command="{Binding Path=DisplayReportCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    ...
</UserControl>
3个回答

26

你可以实现自己的EventTrigger,将事件标记为已处理。

public class HandlingEventTrigger : System.Windows.Interactivity.EventTrigger
{
    protected override void OnEvent(System.EventArgs eventArgs)
    {
        var routedEventArgs = eventArgs as RoutedEventArgs;
        if (routedEventArgs != null)
            routedEventArgs.Handled = true;

        base.OnEvent(eventArgs);
    }
}

然后用 <local:HandlingEventTrigger EventName="MouseDoubleClick"> 替换 <i:EventTrigger EventName="MouseDoubleClick">,并添加

xmlns:local="clr-namespace:HandlingEventTrigger's namespace here"

修改您的用户控件属性。


1
这似乎是一个相当不错的想法。我会等待更多的答案。谢谢。 - Ucodia
2
我选择了这个答案,因为在MVVM场景中它是我可以使用的最干净的解决方案。 - Ucodia
1
对于Windows Phone,使用GestureEventArgs而不是RoutedEventArgs。 - Marius Bughiu

1

将附加事件添加到用户控件

              CommandManager.PreviewCanExecute="PreviewCanExecute" 

并在事件处理程序中

               e.ContinueRouting = false;

希望这会有所帮助


0

MouseDoubleClick事件实际上不是一个冒泡路由事件,而是一个直接路由事件。

然而,这个事件会沿着元素树被触发,可以使用Snoop工具进行检查。此外,即使将Handled属性设置为true,这个事件也会沿着元素树发生。

尽管这个路由事件(MouseDoubleClick事件)似乎通过元素树遵循冒泡路线,但它实际上是一个直接路由事件,由每个UIElement沿着元素树被触发。

如果在MouseDoubleClick事件处理程序中将Handled属性设置为true,则随后的MouseDoubleClick事件沿着路线发生时,Handled属性将被设置为false。这是一个高级别的事件,用于控件消费者想要在用户双击控件并在应用程序中处理事件时得到通知。(来自MSDN)

如上所述,你的问题可能并非像你所说的那样是由传播引起的。有一个Window.ShowActivated属性,它确定窗口在首次显示时是否激活。你可以在子窗口(xaml)中设置该属性,但请注意,尽管ShowActivated可以将焦点给予主窗口,但它无法让主窗口在子窗口前视觉上保持在前面。我已尝试寻找解决方案,但直到现在还没有头绪。
<Window ShowActivated="False" ....>
....
</Window>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接