2010-08-31 12 views
1

Je souhaite afficher plusieurs actions dans un ContextMenu attaché à un lien hypertexte dans un FlowDocument. Certaines de ces actions dépendent de la valeur de la propriété NavigateUri de l'objet Lien hypertexte. Comment puis-je obtenir une référence à l'hyperlien que l'utilisateur a cliqué avec le bouton droit de la souris?Utilisation de menus contextuels sur des liens hypertexte dans un document de flux

Malheureusement, ce n'est pas aussi simple que d'utiliser la propriété PlacementTarget. Comme cela (sans réponse) question dans les forums MSDN noté, le PlacementTarget du ContextMenu ne pointe pas à l'élément Hyperlink, mais à l'ensemble FlowDocumentScrollViewer: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3ab90017-dea8-497c-a937-87a403cb24e0

Je besoin d'un autre moyen de savoir qui Hyperlink l'utilisateur a cliqué .

Notez que mon menu contextuel est défini comme une ressource dans le UserControl contenant mon FlowDocumentScrollViewer et attaché à chaque Hyperlink à l'aide d'un poseur de propriété de style, comme ceci:

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

Les conseils seraient grandement appréciés!

Répondre

2

La structure suit réellement cette valeur dans la propriété PopupControlService.Owner, mais il s'agit d'une classe interne. Si vous êtes prêt à utiliser des fonctionnalités non documentées, vous pouvez parcourir les propriétés du ContextMenu et tirez-le:

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

L'autre approche est que ContextMenuService.ContextMenuOpeningEvent sera soulevé par le lien hypertexte, afin de pouvoir prendre l'expéditeur paramètre et le mettre dans votre propre propriété jointe. Quelque chose comme ceci:

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

Notez qu'il peut y avoir plusieurs contentelements avec des propriétés ContextMenu et ce sera effectivement mis propriétaire sur tous, même si elle ne les questions pour celui qui est réellement affichée.

Pour obtenir la valeur d'un MenuItem spécifique, vous devrez parcourir l'arborescence ou utiliser une liaison avec {RelativeSource AncestorType=ContextMenu}. Vous pouvez également marquer la propriété Owner héritée pour la propager automatiquement vers MenuItems. Si vous attachez le menu contextuel à un niveau plus élevé que le lien hypertexte, vous pouvez utiliser OriginalSource au lieu de l'expéditeur, mais cela vous donnera normalement un Run donc vous devrez remonter l'arbre pour trouver un lien hypertexte.

+0

Merci, @Quartermeister. Je savais qu'il devait y avoir un moyen de le faire. – dthrasher