Использование контекстных меню для гиперссылок в потоковом документе - PullRequest
1 голос
/ 01 сентября 2010

Я хочу отобразить несколько действий в ContextMenu, прикрепленном к гиперссылке в FlowDocument.Некоторые из этих действий зависят от значения свойства NavigateUri объекта Hyperlink.Как получить ссылку на гиперссылку, по которой пользователь щелкнул правой кнопкой мыши?

К сожалению, это не так просто, как использование свойства PlacementTarget.Как отмечается в этом (неотвеченном) вопросе на форумах MSDN, PlacementTarget ContextMenu указывает не на элемент Hyperlink, а на весь FlowDocumentScrollViewer: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3ab90017-dea8-497c-a937-87a403cb24e0

Так что мне нужен другой способ выяснить, какая гиперссылкапользователь щелкнул.

Обратите внимание, что мое контекстное меню определено как ресурс в UserControl, содержащий мой FlowDocumentScrollViewer, и прикреплено к каждой гиперссылке с помощью установщика свойства стиля, например:

<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 Ответ

3 голосов
/ 01 сентября 2010

Фреймворк фактически отслеживает это значение в свойстве 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 будет вызванопо гиперссылке, чтобы вы могли взять параметр отправителя и поместить его в свое собственное присоединенное свойство.Примерно так:

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

Обратите внимание, что может быть несколько ContentElements со свойствами ContextMenu, и это фактически установит Владельца для всех из них, даже если это имеет значение только для того, который фактически отображается.

Чтобы получить значение для определенного элемента MenuItem, вам придется пройтись по дереву или использовать привязку с {RelativeSource AncestorType=ContextMenu}.Вы также можете пометить свойство Owner, унаследованное, чтобы оно автоматически распространялось на MenuItems.

Если вы прикрепляете контекстное меню на более высоком уровне, чем Гиперссылка, вы можете использовать OriginalSource вместо отправителя, но это обычно дает вам Run, поэтому вам придется идти вверх по дереву, чтобы найти Hyperlink,

...