Domanda

Problema interessante relativo all'attivazione di comandi dalle voci del menu contestuale ...

Voglio lanciare un comando per inserire una riga nel mio controllo, InsertRowCmd. Questo comando deve sapere dove inserire la riga.

Potrei usare Mouse.GetPosition (), ma questo mi porterebbe la posizione del mouse attualmente, che sarebbe sopra la voce di menu. Voglio invece ottenere l'origine del menu di scelta rapida.

Qualcuno ha qualche suggerimento su come passare l'origine del menu contestuale come parametro al comando?

Codice di esempio:

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

Le mie idee attuali sono le seguenti:

-Utilizza invece il gestore di clic in modo da poter trovare l'origine nel codice. Il problema è che dovrei quindi gestire l'abilitazione / disabilitazione.

- Maneggia l'evento click e salva l'origine del menu contestuale. Passa queste informazioni salvate nel comando. Ho verificato che gli eventi clic si attivano prima dell'esecuzione del comando.

Qualche idea?

EDIT:

Sto usando CommandSinkBinding per indirizzare la gestione dei comandi nella mia classe ViewModel. Quindi il codice che gestisce l'esecuzione del comando non sa nulla della vista.

È stato utile?

Soluzione

Dovrai utilizzare TranslatePoint per tradurre in alto a sinistra (0, 0) del ContextMenu in una coordinata nella griglia di contenimento. Puoi farlo legando il CommandParameter al ContextMenu e usare un convertitore:

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

Un altro approccio sarebbe un comportamento associato che aggiorna automaticamente una proprietà di sola lettura collegata di tipo Point ogni volta che si apre ContextMenu . L'uso sarebbe simile a questo:

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

Quindi la proprietà TrackOpenLocation svolge il compito di collegarsi al ContextMenu e di aggiornare una seconda proprietà collegata ( OpenLocation ) ogni volta che il ContextMenu è aperto. Quindi MenuItem può semplicemente essere associato a OpenLocation per ottenere la posizione in cui è stato aperto il ContextMenu .

Altri suggerimenti

Seguendo la risposta di Kent, ho usato il suo suggerimento di proprietà allegato e ho finito con questo (usando esempio di comportamenti collegati ):

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

e poi per usarlo in Xaml, devi solo:

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

Tuttavia, avevo anche bisogno di aggiungere:

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

al costruttore della mia vista, altrimenti l'associazione per CommandParameter non è riuscita a cercare ElementName = menu .

Oltre alla risposta di Kent, pensa a un "modo standard". F.e. quando un ListBox ha un menu contestuale, non è necessaria la posizione del menu, poiché l'elemento selezionato viene impostato prima che il menu venga visualizzato. Quindi, se il tuo controllo avrebbe qualcosa che viene "selezionato" con il tasto destro ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top