C# 为什么ContextMenuOpening的源对于Canvas和UserControl的行为不同?

C# 为什么ContextMenuOpening的源对于Canvas和UserControl的行为不同?,c#,wpf,events,contextmenu,C#,Wpf,Events,Contextmenu,我有一个简单的窗口: <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:self="clr-namespace:WpfApplication1"

我有一个简单的窗口:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:self="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="435" Width="613">
    <StackPanel>
        <Canvas Name="canvas">
            <self:Red />
        </Canvas>
        <UserControl Name="uc">
            <self:Blue />
        </UserControl>
    </StackPanel>
</Window>
我创建了一些上下文菜单:

public MainWindow()
{
    InitializeComponent();

    canvas.ContextMenu = new ContextMenu();
    canvas.ContextMenuOpening += (sender, e) =>
    {
        System.Diagnostics.Debug.WriteLine(e.Source.GetType());
    };

    uc.ContextMenu = new ContextMenu();
    uc.ContextMenuOpening += (sender, e) =>
    {
        System.Diagnostics.Debug.WriteLine(e.Source.GetType());
    };
}
如果我打开
画布上的上下文菜单
源代码
红色
,但是如果我在
用户控件
上打开它,
源代码
用户控件

知道为什么吗?
我是在以下网站上找到的:

ContextMenu本身是FrameworkElement派生的类,但此事件不会从作为源打开的上下文菜单引发。事件从作为属性“拥有”上下文菜单的元素引发


如果我理解正确,在第一种情况下,
源代码应为
Canvas
,但事实并非如此。

此行为在该属性的MSDN文档中有相当好的介绍:

根据不同的元素和内容模型进行的源代码调整因类别而异。每个调整事件源的类都会尝试预测对于大多数输入场景和该类要用于的场景,哪个源最有用,然后将该源设置为源。如果此源与您对事件的处理无关,请尝试检查OriginalSource,查看它是否报告了更合适的其他源

这正是UserControl类所做的,它在其方法中修补源属性


因此,正如引用的文本所暗示的,您可能正在寻找OriginalSource属性以使代码具有类似的行为,在这两种情况下,您都会得到一个对矩形的引用。

因此,每当UserControl处理RoutedEvent时,UserControl将从此成为源(如果没有其他元素也调整源)?我不愿给出无保留的“是”,WPF中的事件路由非常复杂。这是预期的默认行为,此虚拟方法是从FrameworkElement.BuildRouteCreHelper()调用的。
public MainWindow()
{
    InitializeComponent();

    canvas.ContextMenu = new ContextMenu();
    canvas.ContextMenuOpening += (sender, e) =>
    {
        System.Diagnostics.Debug.WriteLine(e.Source.GetType());
    };

    uc.ContextMenu = new ContextMenu();
    uc.ContextMenuOpening += (sender, e) =>
    {
        System.Diagnostics.Debug.WriteLine(e.Source.GetType());
    };
}