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

发布于 2024-09-16 22:54:27 字数 1116 浏览 7 评论 0原文

我想在附加到 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>

任何提示将不胜感激!

I want to display several actions in a ContextMenu attached to a Hyperlink in a FlowDocument. Some of these actions depend on the value of the Hyperlink object's NavigateUri property. How can I get a reference to the Hyperlink that the user right-clicked?

Unfortunately, it's not as simple as using the PlacementTarget property. As this (unanswered) question in the MSDN forums noted, the PlacementTarget of the ContextMenu does not point to the Hyperlink element, but to the entire FlowDocumentScrollViewer:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3ab90017-dea8-497c-a937-87a403cb24e0

So I need another way to figure out which Hyperlink the user clicked.

Note that my context menu is defined as a resource in the UserControl containing my FlowDocumentScrollViewer, and attached to each Hyperlink using a style property setter, like so:

<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>

Any hints would be greatly appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

等数载,海棠开 2024-09-23 22:54:27

该框架实际上跟踪 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 属性的 ContentElement,这实际上会在所有内容上设置 Owner,即使它只对实际显示的那个重要。

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

如果您将上下文菜单附加到比超链接更高的级别,则可以使用 OriginalSource 而不是发件人,但这通常会给您一个“运行”,因此您必须沿着树向上查找超链接。

The framework actually tracks that value in the PopupControlService.Owner property, but it's an internal class. If you're willing to use undocumented features, you could iterate over the properties on the ContextMenu and pull it out:

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;
}

The other approach is that ContextMenuService.ContextMenuOpeningEvent will be raised by the Hyperlink, so you could take the sender parameter and put it in your own attached property. Something like this:

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);
    }
}

Note that there may be multiple ContentElements with ContextMenu properties and this will actually set Owner on all of them, even though it only matters for the one that is actually displayed.

To get the value for a specific MenuItem, you will have to walk up the tree or use a binding with {RelativeSource AncestorType=ContextMenu}. You could also mark the Owner property inherited to have it propagate automatically to the MenuItems.

If you attach the context menu at a higher level than the Hyperlink, you can use OriginalSource instead of sender, but that will normally give you a Run so you'll have to walk up the tree to find a Hyperlink.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文