2009-06-17 7 views
5

problème intéressant lié aux commandes de tir à partir des éléments de menu contextuel ...Passage origine de ContextMenu dans WPF Commande

Je veux déclencher une commande pour insérer une ligne dans mon contrôle, InsertRowCmd. Cette commande doit savoir où insérer la ligne.

Je pourrais utiliser Mouse.GetPosition(), mais cela me donnerait la position de la souris actuellement, ce qui serait sur l'élément de menu. Je veux obtenir l'origine du menu contextuel à la place.

Est-ce que quelqu'un a des suggestions sur la façon de passer l'origine du menu contextuel en tant que paramètre à la commande?

Exemple de code:

<UserControl x:Name="MyControl"> 
<!--...--> 
     <ContextMenu x:Name="menu"> 
      <MenuItem Header="Insert Row" Command="{x:Static customCommands:MyCommands.InsertRowCmd}" CommandParameter="?"/> 
     </ContextMenu> 
</UserControl> 

Mes idées actuelles sont les suivantes:

-Utiliser cliquez sur Gestionnaire à la place afin que je puisse trouver l'origine dans le code. Le problème est que je devrais alors gérer l'activation/désactivation.

-Heatle cliquez sur l'événement et enregistrez l'origine du menu contextuel. Transmettez cette information enregistrée dans la commande. J'ai vérifié que les événements de clic se déclenchent avant l'exécution de la commande.

Des idées?

EDIT:

J'utilise de CommandSinkBinding pour acheminer Josh Smith la commande de manipulation dans ma classe ViewModel. Ainsi, le code qui gère l'exécution de la commande ne sait rien de la vue.

Répondre

5

Vous devez utiliser TranslatePoint pour convertir le coin supérieur gauche (0, 0) du ContextMenu en coordonnées dans la grille qui le contient. Vous pouvez le faire en liant le CommandParameter au ContextMenu et utiliser un convertisseur:

CommandParameter="{Binding IsOpen, ElementName=_menu, Converter={StaticResource PointConverter}}" 

Une autre approche serait un comportement qui met à jour automatiquement joint une propriété de type readonly attaché Point chaque fois que le ContextMenu est ouvert. Utilisation ressemblerait à quelque chose comme ceci:

<ContextMenu x:Name="_menu" local:TrackBehavior.TrackOpenLocation="True"> 
    <MenuItem Command="..." CommandParameter="{Binding Path=(local:TrackBehavior.OpenLocation), ElementName=_menu}"/> 
</ContextMenu> 

Ainsi, la propriété attachée TrackOpenLocation fait le travail de fixation à la ContextMenu et la mise à jour d'une seconde propriété attachée (OpenLocation) chaque fois que le ContextMenu est ouvert. Ensuite, le MenuItem peut simplement lier à OpenLocation pour obtenir l'emplacement auquel le ContextMenu a été ouvert pour la dernière fois.

+0

Je pense que vous voulez dire "CommandParameter" au début pas "ConverterParameter"? –

+0

Souhaitez-vous élaborer sur l'idée de comportement ci-joint? –

+0

Oui, merci - fixe et élaboré. –

1

En plus de la réponse de Kent, pensez à une «façon standard». F.e. Quand un ListBox a un ContextMenu, vous n'avez pas besoin de la position du menu, parce que l'élément sélectionné est défini avant que le menu apparaisse. Donc, si votre contrôle aurait quelque chose qui est "sélectionné" sur le clic droit ...

+0

Hmmm ... Cela a une certaine possibilité. –

+0

J'ai fini par utiliser vos deux suggestions. Il y a des moments où le clic n'est pas sur un contrôle enfant, donc j'ai besoin de la suggestion de Kent sur la traduction de Point avec le comportement attaché. Quand un article a été sélectionné, j'ai utilisé cet article à la place. Merci! –

4

À la suite de la réponse de Kent, j'ai utilisé sa suggestion de propriété ci-joint et a fini avec ce (à l'aide example for attached behaviors Josh Smith):

public static class TrackBehavior 
{ 
public static readonly DependencyProperty TrackOpenLocationProperty = DependencyProperty.RegisterAttached("TrackOpenLocation", typeof(bool), typeof(TrackBehavior), new UIPropertyMetadata(false, OnTrackOpenLocationChanged)); 

public static bool GetTrackOpenLocation(ContextMenu item) 
{ 
    return (bool)item.GetValue(TrackOpenLocationProperty); 
} 

public static void SetTrackOpenLocation(ContextMenu item, bool value) 
{ 
    item.SetValue(TrackOpenLocationProperty, value); 
} 

public static readonly DependencyProperty OpenLocationProperty = DependencyProperty.RegisterAttached("OpenLocation", typeof(Point), typeof(TrackBehavior), new UIPropertyMetadata(new Point())); 

public static Point GetOpenLocation(ContextMenu item) 
{ 
    return (Point)item.GetValue(OpenLocationProperty); 
} 

public static void SetOpenLocation(ContextMenu item, Point value) 
{ 
    item.SetValue(OpenLocationProperty, value); 
} 

static void OnTrackOpenLocationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
{ 
    var menu = dependencyObject as ContextMenu; 
    if (menu == null) 
    { 
    return; 
    } 

    if (!(e.NewValue is bool)) 
    { 
    return; 
    } 

    if ((bool)e.NewValue) 
    { 
    menu.Opened += menu_Opened; 

    } 
    else 
    { 
    menu.Opened -= menu_Opened; 
    } 
} 

static void menu_Opened(object sender, RoutedEventArgs e) 
{ 
    if (!ReferenceEquals(sender, e.OriginalSource)) 
    { 
    return; 
    } 

    var menu = e.OriginalSource as ContextMenu; 
    if (menu != null) 
    { 
    SetOpenLocation(menu, Mouse.GetPosition(menu.PlacementTarget)); 
    } 
} 
} 

puis à utiliser dans le XAML, vous avez juste besoin:

<ContextMenu x:Name="menu" Common:TrackBehavior.TrackOpenLocation="True"> 
<MenuItem Command="{Binding SomeCommand}" CommandParameter="{Binding Path=(Common:TrackBehavior.OpenLocation), ElementName=menu}" Header="Menu Text"/> 
</ContextMenu> 

Cependant, j'ai aussi besoin d'ajouter:

NameScope.SetNameScope(menu, NameScope.GetNameScope(this)); 

à la con structurer de ma vue, sinon la liaison pour le CommandParameter n'a pas pu rechercher ElementName=menu.

+0

Je pense avoir eu le même problème avec la portée du nom. Merci pour le post. –

+1

Une autre solution qui supprime le besoin du hack NameScope consiste à utiliser la liaison RelativeSource: CommandParameter = "{Binding Path = (Common: TrackBehavior.OpenLocation), RelativeSource = {RelativeSource AncestorType = ContextMenu}}". Cela a bien fonctionné pour moi. –

Questions connexes