20

I have ListBox (with SelectionMode=Extended) that has multiple items and I want to to add context menu feature. The problem is how to dynamically create context menu based on some conditions. Eg. I'd like to show regular context menu if only one item is selected but to show other context menu (eg. with some new items added) when more than one item is selected. In addition, I'd like to create third kind of context menu if among the selected item is at least one that has some property set. Etc... there can be multiple conditions like these.

Basically I need to dynamically generate context menu right after user has right-click the mouse button but just before the menu is actually shown. Is this possible?

Zev Spitz
  • 13,950
  • 6
  • 64
  • 136
matori82
  • 3,669
  • 9
  • 42
  • 64

3 Answers3

14

I am aware that this is an old question. It seems that there is a very simple answer which solves the OP's original problem in a MVVM scenario, because the ContextMenu class supports binding via the ItemsSource property.

Hope it helps someone encountering this.

XAML

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

In the ViewModel, you can modify the "ItemList" property dynamically according to the current application state.

nepdev
  • 937
  • 1
  • 11
  • 19
9

I've found the answer to my question and it's ContextMenuOpening Event. Basically I need to handle this event and do menu adjustments according to the current application state. More details here: https://msdn.microsoft.com/en-us/library/Bb613568(v=vs.100).aspx

Boumbles
  • 2,473
  • 3
  • 24
  • 42
matori82
  • 3,669
  • 9
  • 42
  • 64
5

If you have a set of predefined context menus that you want to use based on specific scenarios you can always create your context menus as resources.

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

And then create data triggers on your ListBox to set the ContextMenu to use, rather than what I have done below I would suggest binding to properties on your view model or code behind for this as it might get very messy in xaml. The implementation here checks to see if only one item is selected and in that case switches to Menu1

<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>

If the selection for which context menu to show is only of concern to the view I would suggest handling it in code behind.

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

While if the ViewModel does have some interest in which menu to show (doesn't sound like it but it's hard to tell without knowing the full context) you could expose some properties that let you decide in the ViewModel which ContextMenu to show. Although rather than individual Boolean properties you'd most likely want to create a class that makes sure that only one of the Booleans is true at any given time.

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
}
Peter Karlsson
  • 1,170
  • 7
  • 15
  • Very helpful thanks, but doesn't exactly solves my problem. I need to create context menu in code and on-the-fly. That's because some conditions that affect the context menu can only be checked/calculated from code. – matori82 Feb 09 '13 at 02:08