Question

Problème intéressant lié au lancement de commandes à partir d'éléments de menu contextuel ...

Je souhaite 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 au-dessus de l'élément de menu. Je souhaite plutôt obtenir l'origine du menu contextuel.

Quelqu'un a-t-il des suggestions sur la manière de transmettre 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:

-Utilisez plutôt le gestionnaire de clic afin que je puisse trouver l'origine dans le code. Le problème est que je devrais alors gérer l’activation / la désactivation.

-Handle click event et enregistrer l'origine du menu contextuel. Transmettez ces informations enregistrées 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 "noreferrer"> CommandSinkBinding pour acheminer la gestion de la commande dans ma classe ViewModel. Le code qui gère l’exécution de la commande ne connaît donc rien de la vue.

Était-ce utile?

La solution

Vous devrez utiliser TranslatePoint pour traduire le haut-gauche (0, 0) du ContextMenu en une coordonnée de la grille la contenant. Vous pouvez le faire en liant le CommandParameter au ContextMenu et en utilisant un convertisseur:

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

Une autre approche serait un comportement attaché qui met à jour automatiquement une propriété attachée en lecture seule de type Point à chaque ouverture du ContextMenu . L’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 effectue le travail de rattachement au ContextMenu et de mise à jour d'une deuxième propriété attachée ( OpenLocation ) chaque fois que le ContextMenu est ouvert. Ensuite, MenuItem peut simplement être lié à OpenLocation pour obtenir l'emplacement où le ContextMenu a été ouvert pour la dernière fois.

Autres conseils

Suite à la réponse de Kent, j’ai utilisé la suggestion de propriété ci-jointe et je me suis retrouvé avec ceci (en utilisant celui de Josh Smith exemple de comportements attachés ):

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

et ensuite à utiliser dans le Xaml, il vous suffit de:

<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, je devais également ajouter:

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

au constructeur de ma vue, sinon la liaison pour le CommandParameter ne pourrait pas rechercher NomElément = menu .

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top