在流文档中使用超链接上下文菜单

时间:2010-08-31 20:24:12

标签: wpf contextmenu hyperlink flowdocument

我想在附加到FlowDocument中的超链接的ContextMenu中显示多个操作。其中一些操作取决于Hyperlink对象的NavigateUri属性的值。如何获取用户右键单击的超链接的引用?

不幸的是,它并不像使用PlacementTarget属性那么简单。正如MSDN论坛中的这个(未答复的)问题所指出的,ContextMenu的PlacementTarget并不指向Hyperlink元素,而是指向整个FlowDocumentScrollViewer: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3ab90017-dea8-497c-a937-87a403cb24e0

所以我需要另一种方法来确定用户点击的超链接。

请注意,我的上下文菜单被定义为包含我的FlowDocumentScrollViewer的UserControl中的资源,并使用样式属性设置器附加到每个超链接,如下所示:

<UserControl.Resources>
    <ContextMenu x:Key="contextMenu">
        <MenuItem Name="mnuOpen" Header="_Open Link" Click="mnuOpen_Click" />
        <MenuItem Name="mnuView" Header="_View Properties" Click="mnuView_Click" />
    </ContextMenu>
    <Style TargetType="Hyperlink">
        <Setter Property="ContextMenu" Value="{DynamicResource contextMenu}" />
    </Style>
</UserControl.Resources>

任何提示都将不胜感激!

1 个答案:

答案 0 :(得分:3)

框架实际上在PopupControlService.Owner属性中跟踪该值,但它是一个内部类。如果您愿意使用未记录的功能,您可以迭代ContextMenu上的属性并将其拉出来:

private static object GetOwner(ContextMenu menu)
{
    var prop = menu.GetLocalValueEnumerator();
    while (prop.MoveNext())
    {
        if (prop.Current.Property.Name == "Owner" &&
            prop.Current.Property.OwnerType.Name == "PopupControlService")
        {
            return prop.Current.Value;
        }
    }
    return null;
}

另一种方法是由超级链接引发ContextMenuService.ContextMenuOpeningEvent,因此您可以使用sender参数并将其放入您自己的附加属性中。像这样:

public class ContextMenuOwnerTracker
{
    private static bool isInitialized;
    public static void Initialize()
    {
        if (!isInitialized)
        {
            isInitialized = true;
            EventManager.RegisterClassHandler(typeof(ContentElement), 
                ContextMenuService.ContextMenuOpeningEvent, 
                new ContextMenuEventHandler(OnContextMenuOpening));
            EventManager.RegisterClassHandler(typeof(ContentElement), 
                ContextMenuService.ContextMenuClosingEvent, 
                new ContextMenuEventHandler(OnContextMenuClosing));
        }
    }

    private static void OnContextMenuOpening
        (object sender, ContextMenuEventArgs args)
    {
        var menu = ContextMenuService.GetContextMenu((DependencyObject)sender);
        if (menu != null)
        {
            SetOwner(menu, sender);
        }
    }

    private static void OnContextMenuClosing
        (object sender, ContextMenuEventArgs args)
    {
        var menu = ContextMenuService.GetContextMenu((DependencyObject)sender);
        if (menu != null)
        {
            ClearOwner(menu);
        }
    }

    private static readonly DependencyPropertyKey OwnerKey =
        DependencyProperty.RegisterAttachedReadOnly(
            "Owner",
            typeof(object),
            typeof(ContextMenuOwnerTracker),
            new PropertyMetadata(null));
    public static readonly DependencyProperty OwnerProperty =
        OwnerKey.DependencyProperty;
    public static object GetOwner(ContextMenu element)
    {
        return element.GetValue(OwnerProperty);
    }
    private static void SetOwner(ContextMenu element, object value)
    {
        element.SetValue(OwnerKey, value);
    }
    private static void ClearOwner(ContextMenu element)
    {
        element.ClearValue(OwnerKey);
    }
}

请注意,可能有多个具有ContextMenu属性的ContentElements,这实际上会在所有这些属性上设置所有者,即使它只对实际显示的那个有用。

要获取特定MenuItem的值,您必须向上走树或使用{RelativeSource AncestorType=ContextMenu}绑定。您还可以标记继承的Owner属性,使其自动传播到MenuItems。

如果您将上下文菜单附加到超级链接的更高级别,则可以使用OriginalSource而不是发件人,但这通常会为您提供运行,因此您必须走上树以查找超链接。