Event when a menu item is highlighted

Has anyone figured out an event that fires whenever a menu item is highlighted?

I would like to display a description of each menu command in the status bar as they are highlighted. I'd like this to happen whether they are highlighted using the mouse or the keyboard.

But after considerable effort, I don't see any event like this. I even tried overriding WndProc to detect raw menu messages but found none are sent. Apparently, WinForms doesn't use the standard Windows menus.

It seems like knowing when a menu item is clicks and when it is selected (highlighted without being clicked) should be the two most important menu events. I don't know why the latter wouldn't be supported.

Anyone been able to figure this out?


Solution 1:

In addition to the mouse events, you can add the keyboard keys part by handling the KeyUp event of the owner menu to get the selected item and display a description in a status-bar label.

public YourForm()
{
    InitializeComponent();

    menuStrip1.ShowItemToolTips = false;
    menuStrip1.KeyUp += OnToolStripKeyUp;

    foreach (var item in GetAllToolStripItems(menuStrip1.Items))
    {
        item.AutoToolTip = false;
        item.MouseEnter += OnToolStripItemMouseEnter;
        item.MouseLeave += OnToolStripItemMouseLeave;

        if (item.GetCurrentParent() is ToolStrip dm)
        {
            dm.ShowItemToolTips = false;
            dm.KeyUp -= OnToolStripKeyUp;
            dm.KeyUp += OnToolStripKeyUp;
        }
    }
}

private void OnToolStripItemMouseEnter(object sender, EventArgs e)
{
    sbrLabel.Text = (sender as ToolStripItem).ToolTipText;
}

private void OnToolStripItemMouseLeave(object sender, EventArgs e)
{
    sbrLabel.Text = "Ready";
}

private void OnToolStripKeyUp(object sender, KeyEventArgs e)
{
    var s = sender as ToolStrip;
    var selItem = s.Items.OfType<ToolStripMenuItem>().FirstOrDefault(x => x.Selected);

    sbrLabel.Text = selItem?.ToolTipText;
}

private IEnumerable<ToolStripItem> GetAllToolStripItems(ToolStripItemCollection tsic)
{
    foreach (var tsi in tsic.Cast<ToolStripItem>())
    {
        yield return tsi;

        if (tsi is ToolStripDropDownItem tsddi && tsddi.HasDropDown)
            foreach (var ddi in GetAllToolStripItems(tsddi.DropDownItems))
                yield return ddi;
    }
}

SO70731362

Solution 2:

With @dr.null's help, I got this working. Here's my version of the code.

private void InitializeMenuStatus(ToolStrip toolStrip)
{
    toolStrip.ShowItemToolTips = false;
    toolStrip.KeyUp += ToolStrip_KeyUp;
    foreach (ToolStripItem toolStripItem in toolStrip.Items)
    {
        toolStripItem.AutoToolTip = false;
        toolStripItem.MouseEnter += ToolStripItem_MouseEnter;
        toolStripItem.MouseLeave += ToolStripItem_MouseLeave;
        if (toolStripItem is ToolStripDropDownItem dropDownItem)
            InitializeMenuStatus(dropDownItem.DropDown);
    }
}

private ToolStripItem? SelectedMenuItem = null;

private void SetSelectedMenuItem(ToolStripItem? item)
{
    if (!ReferenceEquals(item, SelectedMenuItem))
    {
        SelectedMenuItem = item;
        lblStatus.Text = item?.ToolTipText ?? string.Empty;
    }
}

private void ToolStripItem_MouseEnter(object? sender, EventArgs e)
{
    if (sender is ToolStripMenuItem menuItem && menuItem.Selected)
        SetSelectedMenuItem(menuItem);
}

private void ToolStripItem_MouseLeave(object? sender, EventArgs e)
{
    SetSelectedMenuItem(null);
}

private void ToolStrip_KeyUp(object? sender, KeyEventArgs e)
{
    if (sender is ToolStripDropDownMenu dropDownMenu)
    {
        ToolStripMenuItem? menuItem = dropDownMenu.Items.OfType<ToolStripMenuItem>()
            .Where(m => m.Selected)
            .FirstOrDefault();
        SetSelectedMenuItem(menuItem);
    }
}

Why a Selected event was never added to menu items escapes me. I have suggested that it be added. If you agree, please go and show your support for that request.

If anyone's interested, I spent some time fine tuning this code and ended up making a free component, now published as a NuGet package. You can view the code on GitHub