2013-02-09 4 views
9

Ich habe ListBox (mit SelectionMode = Erweitert), die mehrere Elemente hat, und ich möchte Kontextmenüfunktion hinzufügen. Das Problem besteht darin, wie das Kontextmenü basierend auf bestimmten Bedingungen dynamisch erstellt wird. Z.B. Ich möchte das normale Kontextmenü anzeigen, wenn nur ein Element ausgewählt ist, aber um ein anderes Kontextmenü anzuzeigen (z. B. wenn einige neue Elemente hinzugefügt wurden), wenn mehr als ein Element ausgewählt ist. Darüber hinaus möchte ich eine dritte Art von Kontextmenü erstellen, wenn unter dem ausgewählten Element mindestens eines mit einer bestimmten Eigenschaft festgelegt ist. Etc ... kann es mehrere Bedingungen wie diese geben.WPF: Wie man ContextMenu dynamisch generiert

Grundsätzlich muss ich das Kontextmenü dynamisch generieren, nachdem der Benutzer die rechte Maustaste gedrückt hat, aber kurz bevor das Menü tatsächlich angezeigt wird. Ist das möglich?

Antwort

6

Ich habe die Antwort auf meine Frage gefunden und es ist ContextMenuOpening Event. Grundsätzlich muss ich dieses Ereignis behandeln und Menüeinstellungen entsprechend dem aktuellen Anwendungsstatus vornehmen. Mehr Details hier: https://msdn.microsoft.com/en-us/library/Bb613568(v=vs.100).aspx

+1

der Link ist jetzt ungültig – aaron

+0

Aktualisierte Dokumentation: https://msdn.microsoft.com/en-us/library/bb613568.aspx –

+1

Nur für den Fall, dass das Dokument in irgendeiner Weise wieder entfernt wird, lautet der Titel "How to: Handle das ContextMenuOpening-Ereignis ", so dass Sie danach suchen können. – Rolfi

3

Wenn Sie über eine Reihe von vordefinierten Kontextmenüs verfügen, die Sie basierend auf bestimmten Szenarien verwenden möchten, können Sie Ihre Kontextmenüs immer als Ressourcen erstellen.

<Window.Resources> 
    <ContextMenu x:Key="Menu1"> 
     <MenuItem>Item1</MenuItem> 
    </ContextMenu> 
    <ContextMenu x:Key="Menu2"> 
     <MenuItem>Item1</MenuItem> 
     <MenuItem>Item2</MenuItem> 
    </ContextMenu> 
</Window.Resources> 

Und dann schaffen Daten lösen auf Ihrem ListBox die ContextMenu einstellen zu verwenden, und nicht als das, was ich getan habe unter ich dafür zu Eigenschaften auf Ihrer Ansicht nach Modell oder Code hinter Bindung würde vorschlagen, da es sehr chaotisch könnte in Xaml. Die Implementierung überprüft hier, um zu sehen, wenn nur ein Element ausgewählt ist und in diesem Fall schaltet

<ListBox x:Name="mylist" SelectionMode="Multiple" ContextMenu="{StaticResource Menu2}" > 
    <ListBox.Style> 
     <Style TargetType="{x:Type ListBox}"> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding Path=SelectedItems.Count, RelativeSource={RelativeSource Self}}" Value="1" > 
        <Setter Property="ContextMenu" Value="{StaticResource ResourceKey=Menu1}" /> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </ListBox.Style> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <TextBlock Text="{Binding Path=DisplayName}" /> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

menu1 Wenn die Auswahl für das Menü welchem ​​Kontext zeigen nur ein Anliegen der Ansicht ist, würde ich es im Code vorschlagen Handhabung hinter.

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     // Hook up any events that might influence which menu to show 
     mylist.SelectionChanged += listSelectionChanged; 
     InitializeComponent(); 
    } 

    private void listSelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     var listBox = sender as ListBox; 
     if (listBox == null) 
      return; // Or throw something, hard 

     ContextMenu menuToUse; 
     // Logic for selecting which menu to use goes here 

     listBox.ContextMenu = menuToUse; 
    } 
} 

Während, wenn das Ansichtsmodell eines gewisses Interesse hat, in dem Menü zu zeigen (klingt nicht wie es, aber es ist schwer, ohne zu sagen, den vollen Kontext zu wissen) Sie einige Eigenschaften aussetzen könnten, die Sie im Ansichtsmodell entscheiden lassen welche ContextMenu zu zeigen. Obwohl Sie eher als einzelne boolesche Eigenschaften eine Klasse erstellen möchten, die sicherstellt, dass immer nur einer der Booleschen Werte wahr ist.

public class MyViewModel : INotifyPropertyChanged 
{ 

    public MyViewModel() 
    { 
     SelectedItems = new ObservableCollection<string>(); 
     SelectedItems.CollectionChanged += SelectedItemsChanged; 
    } 

    private void SelectedItemsChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     // Logic to see which ShowMenuX property to set to true goes here 
    } 

    public ObservableCollection<string> SelectedItems { get; set; } 

    private bool _showMenu1 = false; 
    public bool ShowMenu1 
    { 
     get { return _showMenu1; } 
     set 
     { 
      _showMenu1 = value; 
      RaisePropertyChanged("ShowMenu1"); 
     } 
    } 

    // INotifyPropertyChanged implementation goes here 
} 
+0

Sehr hilfreich danke, löst aber nicht genau mein Problem. Ich muss Kontextmenü in Code und on-the-fly erstellen. Das liegt daran, dass einige Bedingungen, die das Kontextmenü betreffen, nur aus dem Code überprüft/berechnet werden können. – matori82

10

Ich bin mir bewusst, dass dies eine alte Frage ist. Es scheint, dass es eine sehr einfache Antwort gibt, die das ursprüngliche Problem des OP in einem MVVM-Szenario löst, da die ContextMenu-Klasse das Binden über die ItemsSource-Eigenschaft unterstützt.

Ich hoffe, es hilft jemandem dabei zu begegnen. nach dem aktuellen Anwendungszustand

XAML

 <ContextMenu ItemsSource="{Binding Path=ItemList, UpdateSourceTrigger=PropertyChanged}"> 
     </ContextMenu> 

Im Ansichtsmodell, können Sie die "ItemList" Eigenschaft dynamisch ändern.

+6

Wie behandeln Sie Binding-Befehle auf den Menüpunkten? – Julien

+1

@Julien, siehe [diese] (http://stackoverflow.com/a/22849063/1997232) antwort. – Sinatr