Home > Enterprise >  How can I make ContextMenu use my custom MenuItem class?
How can I make ContextMenu use my custom MenuItem class?

Time:10-21

I'm working on a C# desktop app and am looking for a way to programmatically highlight/select a MenuItem, I came across this suggestion: https://stackoverflow.com/a/71115000/4247806

That looks ok, but my menu is populated by binding ItemsSource to a list of ICommand classes, and not explicitly in Xaml markup. So WPF creates the required MenuItem instances behind-the-scenes. How can I make ContextMenu use my custom MenuItem class?

During previous work on a custom DataGrid I stumbled across a GetContainerForItemOverride method that the ContextMenu class has as well. This lead me to this approach:

public class MyContextMenu : ContextMenu
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new MyMenuItem();
    }
}

public class MyMenuItem : MenuItem
{
    public MyMenuItem()
    {
        
    }

    public new bool IsHighlighted
    {
        get => base.IsHighlighted;
        set => base.IsHighlighted = value;
    }
}

However, I would like to exactly mimick/imitate the original behavior, except that I need a MyMenuItem instance. Looking at the implementation of ContextMenu.GetContainerForItemOverride I've found this:

protected override DependencyObject GetContainerForItemOverride()
{
    object currentItem = _currentItem;
    _currentItem = null;
    if (UsesItemContainerTemplate)
    {
        DataTemplate dataTemplate = ItemContainerTemplateSelector.SelectTemplate(currentItem, this);
        if (dataTemplate != null)
        {
            object obj = dataTemplate.LoadContent();
            if (obj is MenuItem || obj is Separator)
            {
                return obj as DependencyObject;
            }

            throw new InvalidOperationException(SR.Get("InvalidItemContainer", GetType().Name, typeof(MenuItem).Name, typeof(Separator).Name, obj));
        }
    }

    return new MenuItem();
}

I can't reproduce this behavior due to the private object _currentItem being used. If I ignore this, in which scenarios could this cause problems?

CodePudding user response:

If you look in the source code, you'll see that the private field _currentItem is only used in two methods so you could easily replicate this code in your custom MyContextMenu and simply change type from MenuItem to MyMenuItem if that's what you want:

private object _currentItem;

protected override bool IsItemItsOwnContainerOverride(object item)
{
    bool ret = (item is MenuItem) || (item is Separator);
    if (!ret)
    {
        _currentItem = item;
    }

    return ret;
}

protected override DependencyObject GetContainerForItemOverride()
{
    object currentItem = _currentItem;
    _currentItem = null;

    if (UsesItemContainerTemplate)
    {
        DataTemplate itemContainerTemplate = ItemContainerTemplateSelector.SelectTemplate(currentItem, this);
        if (itemContainerTemplate != null)
        {
            object itemContainer = itemContainerTemplate.LoadContent();
            if (itemContainer is MenuItem || itemContainer is Separator)
            {
                return itemContainer as DependencyObject;
            }
            else
            {
                throw new InvalidOperationException(SR.Get(SRID.InvalidItemContainer, this.GetType().Name, typeof(MenuItem).Name, typeof(Separator).Name, itemContainer));
            }
        }
    }

    return new MenuItem();
}
  • Related