2009-06-17 16 views
5

problema interessanti relativi ai comandi che sparano da voci di menu contestuale ...Passando origine del ContextMenu in Command WPF

voglio sparare un comando per inserire una riga sotto il 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 di scelta rapida 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> 

mie idee correnti sono i seguenti:

-Usa click handler invece in modo che possa trovare l'origine nel codice. Il problema è che dovrei quindi gestire l'abilitazione/disabilitazione.

-Handle clic evento e salva l'origine del menu di scelta rapida. Passa queste informazioni salvate nel comando. Ho verificato che gli eventi di clic vengano attivati ​​prima dell'esecuzione del comando.

Qualche idea?

EDIT:

sto usando di CommandSinkBinding per instradare Josh Smith che gestisce nella mia classe ViewModel comando. Quindi il codice che gestisce l'esecuzione del comando non sa nulla della vista.

risposta

5

È necessario utilizzare TranslatePoint per tradurre l'angolo in alto a sinistra (0, 0) di ContextMenu in una coordinata nella griglia di contenimento. Si potrebbe fare in modo legandosi al CommandParameter al ContextMenu e utilizzare un convertitore:

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

Un altro approccio sarebbe un comportamento allegato che si aggiorna automaticamente una proprietà di sola lettura allegato di tipo Point ogni volta che il ContextMenu è aperto. Uso sarebbe simile a questa:

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

Così il TrackOpenLocation proprietà associata fa il lavoro di attaccarsi al ContextMenu e l'aggiornamento di una seconda proprietà associata (OpenLocation) ogni volta che il ContextMenu è aperto. Quindi lo MenuItem può semplicemente collegarsi a OpenLocation per ottenere la posizione in cui è stata aperta l'ultima volta l'ContextMenu.

+0

Sto pensando che intendi "CommandParameter" all'inizio non "ConverterParameter"? –

+0

Ti piacerebbe approfondire l'idea comportamentale allegata? –

+0

Sì, grazie - risolto ed elaborato. –

1

Oltre alla risposta di Kent, pensa a un "metodo standard". F.e. quando un ListBox ha un ContextMenu, non hai bisogno della posizione del menu, perché l'elemento selezionato viene impostato prima che il menu spuntasse. Quindi, se il tuo controllo avrebbe qualcosa che viene "selezionato" con il tasto destro del mouse ...

+0

Hmmm ... Questo ha qualche possibilità. –

+0

Ho finito per utilizzare entrambi i tuoi suggerimenti. Ci sono momenti in cui il clic non è su un controllo figlio, quindi ho bisogno del suggerimento di Kent sulla traduzione Punto con comportamento allegato. Quando è stato selezionato un oggetto, ho usato quell'oggetto. Grazie! –

4

A seguito di risposta di Kent, ho usato il suo suggerimento proprietà associata e finito con questo (usando di Josh Smith example for attached behaviors):

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 quindi da utilizzare nel Xaml, basta:

<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, ho anche bisogno di aggiungere:

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

per l'aria structor of my view, altrimenti il ​​binding per lo CommandParameter non poteva cercare ElementName=menu.

+0

Penso di avere avuto lo stesso problema con lo scope del nome. Grazie per il post. –

+1

Una soluzione alternativa che rimuove la necessità dell'hacking NameScope consiste nell'utilizzare il binding RelativeSource: CommandParameter = "{Binding Path = (Comune: TrackBehavior.OpenLocation), RelativeSource = {RelativeSource AncestorType = ContextMenu}}". Questo ha funzionato bene per me. –

Problemi correlati