WPF ContextMenu with bound items: Items.Count == 0 in ContextMenuOpening event

Posted by OregonGhost on Stack Overflow See other posts from Stack Overflow or by OregonGhost
Published on 2010-03-12T10:11:57Z Indexed on 2010/03/12 11:27 UTC
Read the original article Hit count: 957

Filed under:
|
|
|

I have a ContextMenu with the ItemsSource bound to the selected item of a list view, like this:

<ContextMenu ItemsSource="{Binding Path=PlacementTarget.SelectedItem,
    RelativeSource={RelativeSource Self}, Converter={StaticResource possibleConverter}}"/>

The possibleConverter enumerates all possible values for a property of the the selected item, which are shown in the context menu. In the Opened event of the context menu, I select the current value like this:

var cm = e.OriginalSource as ContextMenu;
if (cm != null) {
    var lv = cm.PlacementTarget as ListView;
    var field = lv.SelectedItem as Field;
    var item = cm.ItemContainerGenerator.ContainerFromItem(cm.Items.OfType<object>().Where(o => o.ToString().Equals(field.StringValue)).FirstOrDefault()) as MenuItem;
    if (item != null) {
        item.IsChecked = true;
    }
}

Not particularly elegant, but it works. With the debugger I verified that the ContextMenu.Items.Count property has a non-zero value when expected (i.e. cm.Items.Count is non-zero in the if).

So far, so good. There are, however, items in the listview where the context menu will have no items. In this case, an empty menu is shown. I tried to suppress this in the ContextMenuOpening event in the list view, like this:

var lv = sender as ListView;
if (lv != null) {
    var cm = lv.ContextMenu;
    if ((cm != null) && (cm.Items.Count > 0)) {
        // Here we want to check the current item, which is currently done in the Opened event.
    } else {
        e.Handled = true;
    }
}

Seems like it should work. However, cm.Items.Count is always zero. This is true even if ListView.SelectedItem did not change: For an item with menu entries, the menu is shown correctly after the first click, so the data binding has already happend. It is shown correct the second time as well, but in any case, Items.Count is zero in the ContextMenuOpening event.

What am I missing? How can I suppress empty context menus? Why is the count zero in the ContextMenuOpening handler, which is in Windows Forms (ContextMenuStrip.Opening) the canonical point where to do these things?

EDIT: Upon further investigating, it turns out that in the ContextMenuOpening handler, any binding to the listview fails, which is why ItemsSource is null. I tried to bind via ElementName, via a FindAncestor relationship, all to no avail. The PlacementTarget is null during that event. An ugly hack worked though: In the ContextMenuOpening event, I assign the list view to the ContextMenu.Tag property, while the ItemsSource binding now binds to Tag.SelectedItem. This updates the binding, so Items.Count is what it should be. It's still strange. How can you do meaningful things in ContextMenuOpening other than replacing the menu or something, if the binding fails because somehow the context menu is out of context during the event? Was it only tested with static pre-defined menu items?

© Stack Overflow or respective owner

Related posts about wpf

Related posts about listview