Pregunta

Problema interesante relacionado con disparar comandos desde elementos del menú contextual ...

Quiero activar un comando para insertar una fila en mi control, InsertRowCmd. Este comando necesita saber dónde insertar la fila.

Podría usar Mouse.GetPosition (), pero eso me daría la posición del mouse actualmente, que estaría sobre el elemento del menú. Quiero obtener el origen del menú contextual en su lugar.

¿Alguien tiene alguna sugerencia sobre cómo pasar el origen del menú contextual como parámetro al comando?

Código de muestra:

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

Mis ideas actuales son las siguientes:

-Utilice el controlador de clic para poder encontrar el origen en el código. El problema es que tendría que manejar la habilitación / deshabilitación.

- Manejar evento de clic y guardar el origen del menú contextual. Pase esta información guardada al comando. He verificado que los eventos de clic se activan antes de ejecutar el comando.

¿Alguna idea?

EDIT:

Estoy usando Josh Smith's CommandSinkBinding para enrutar el manejo de comandos en mi clase ViewModel. Entonces, el código que maneja la ejecución del comando no sabe nada sobre la vista.

¿Fue útil?

Solución

Deberá usar TranslatePoint para traducir la parte superior izquierda (0, 0) del ContextMenu a una coordenada en la cuadrícula que lo contiene. Puede hacerlo vinculando el CommandParameter al ContextMenu y usar un convertidor:

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

Otro enfoque sería un comportamiento adjunto que actualiza automáticamente una propiedad de solo lectura adjunta de tipo Point cada vez que se abre ContextMenu . El uso se vería así:

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

Entonces, la propiedad adjunta TrackOpenLocation hace el trabajo de adjuntar al ContextMenu y actualizar una segunda propiedad adjunta ( OpenLocation ) siempre que el ContextMenu está abierto. Luego, el MenuItem puede unirse a OpenLocation para obtener la ubicación en la que el ContextMenu se abrió por última vez.

Otros consejos

Siguiendo la respuesta de Kent, utilicé su sugerencia de propiedad adjunta y terminé con esto (usando Josh Smith's ejemplo de comportamientos adjuntos ):

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

y luego para usar en Xaml, solo necesitas:

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

Sin embargo, también necesitaba agregar:

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

al constructor de mi vista, de lo contrario, el enlace para el CommandParameter no podría buscar ElementName = menu .

Además de la respuesta de Kent, piense en una " forma estándar " ;. F.e. cuando un ListBox tiene un ContextMenu, no necesita la posición del menú, porque el elemento seleccionado se configura antes de que aparezca el menú. Entonces, si su control tuviera algo que se selecciona " seleccionado " en el botón derecho ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top