在菜单中隐藏多余的ToolStripSeparator

时间:2018-11-16 15:44:37

标签: c# winforms menu

我有一个ContextMenuStrip控件,其中包含多个菜单项A,B,C,D,E,并在项目C前后添加了分隔符。

enter image description here

在运行时,我根据某些条件在Opening事件中动态决定是否显示菜单项C。

private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
    toolStripMenuItemC.Visible = SomeCondition;
}

当在运行时显示菜单并且C项被隐藏时,两个分隔符都是可见的,看起来很丑。

enter image description here

是否有一些内置机制可以自动将多个连续的菜单分隔符组合为一个?例如,Delphi中的VCL框架为此具有TPopupMenu.AutoLineReduction属性。


当然可以为菜单编写特定的逻辑,以查看哪些菜单项可见,然后确定要显示的分隔符。但是,项目越多,分隔符越多,此代码将变得越复杂。每次在菜单中添加,删除或移动菜单项时,都必须更新代码。

我正在寻找一种在不知道菜单中特定项目的情况下可以在任何菜单上使用的通用方法。我更喜欢开箱即用的WinForms中已经包含的某种方式,但是您也可以使用自己的工具功能来清除菜单中的分隔符。

2 个答案:

答案 0 :(得分:1)

由于我尚未在WinForms中找到内置的方法来完成此操作,因此我编写了以下实用程序函数:

public static class ToolStripExtensions
{
    /// <summary>
    /// Automatically show/hide separator in toolstrips (Menus, toolbars, etc).
    /// This will hide / show separators based on the other toolstripitems in the collections.
    /// A separator will be hidden if it would be the first visible entry in the list.
    /// A separator will be hidden if it would be the last visible entry in the list.
    /// A separator will be hidden if it would appear right after another separator.
    /// All other separatos will be shown.
    /// </summary>
    /// <param name="items">A collection of ToolStripItems</param>
    /// <param name="includeSubmenus">If true, also cleanup separators in submenus</param>
    public static void CleanUpSeparators(this ToolStripItemCollection items, bool includeSubmenus = true)
    {
        // Will be true when we have last seen a visible item 
        // which is not a separator 
        bool canInsertSeparator = false;

        List<ToolStripSeparator> keepers = new List<ToolStripSeparator>();
        List<ToolStripSeparator> gonners = new List<ToolStripSeparator>();

        ToolStripSeparator lastSeparator = null;

        // Decide which separators should stay and which should go
        for (int i = 0; i < items.Count; i++)
        {
            ToolStripItem item = items[i];

            if (item is ToolStripSeparator)
            {
                if (canInsertSeparator)
                {
                    keepers.Add(item as ToolStripSeparator);
                    lastSeparator = item as ToolStripSeparator;
                    canInsertSeparator = false;
                }
                else
                {
                    gonners.Add(item as ToolStripSeparator);
                }
            }
            else
            {
                // After seeing at least one visible item, we can add a new separator again
                if (item.Available)
                {
                    canInsertSeparator = true;
                }
            }

            // Recursion
            if (includeSubmenus && item is ToolStripDropDownItem)
            {
                (item as ToolStripDropDownItem).DropDownItems.CleanUpSeparators(true);
            }
        }

        if (!canInsertSeparator && lastSeparator != null)
        {
            // The last separator has no following visible other entries, 
            // so we don't want it
            gonners.Add(lastSeparator);
        }

        // Show and hide the separators
        // First show, then hide, because it is possible
        // a separator at the end of the menu is in both lists
        // and it should be hidden
        foreach (var separator in keepers)
        {
            separator.Visible = true;
        }
        foreach (var separator in gonners)
        {
            separator.Visible = false;
        }
    }
}

它会这样使用:

private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
    toolStripMenuItemC.Visible = SomeCondition;

    contextMenuStrip1.Items.CleanUpSeparators();
}

答案 1 :(得分:0)

尝试一下,它以不同的方式工作,根据要隐藏的ToolStripMenuItem的相对位置显示或隐藏ToolStripSeparatorToolStripMenuItem

在以下情况下显示/隐藏ToolStripSeparator

  1. 菜单是最后一个菜单,前一项是ToolStripSeparator
  2. 菜单是第一个菜单,随后的菜单是ToolStripSeparator
  3. 菜单之前是ToolStripSeparator

方法调用:ShowHideMenuItem([ParentMenu], [ToolStripMenuItem], [true|false])

ShowHideMenuItem(ToolStripMenuItem1, ToolStripMenuItem5, true);

private void ShowHideMenuItem(ToolStripMenuItem ParentMenu, ToolStripMenuItem MenuItem, bool ShowOrHide)
{
    if (!ParentMenu.HasDropDownItems) return;
    int itemIndex = ParentMenu.DropDownItems.IndexOf(MenuItem);

    if (ParentMenu.DropDownItems.Count > 1)
    {
        if (itemIndex == 0 &&
            ParentMenu.DropDownItems[itemIndex + 1].GetType() == typeof(ToolStripSeparator)) {
            ParentMenu.DropDownItems[itemIndex + 1].Visible = ShowOrHide;
        }
        else if (itemIndex == ParentMenu.DropDownItems.Count - 1 &&
                    ParentMenu.DropDownItems[itemIndex - 1].GetType() == typeof(ToolStripSeparator)) {
            ParentMenu.DropDownItems[itemIndex - 1].Visible = ShowOrHide;
        }
        else if (ParentMenu.DropDownItems[itemIndex + 1].GetType() == typeof(ToolStripSeparator) &&
                    ParentMenu.DropDownItems[itemIndex - 1].GetType() == typeof(ToolStripSeparator)) {
            ParentMenu.DropDownItems[itemIndex + 1].Visible = ShowOrHide;
            ParentMenu.DropDownItems[itemIndex - 1].Visible = ShowOrHide;
        }
    }
    MenuItem.Visible = ShowOrHide;
}