ToolStripLayoutStyle.Table 在 ContextMenuStrip 中不工作

ToolStripLayoutStyle.Table not working in ContextMenuStrip

我想显示一个上下文菜单,其中菜单项是在网格中布置的图像。但是,当我在菜单的 ToolStripDropDown 中将 LayoutStyle 设置为 ToolStripLayoutStyle.Table 时,如果创建了新的 ToolStripDropDown 对象,它只会给出菜单项的网格布局。

我的问题是我可以为子菜单创建并分配一个新的 ToolStripDropDown,但不能为 ContextMenuStrip,因为它是 ToolStripDropDown。

下面的代码演示了这个问题。它将显示一个上下文菜单,其中包含颜色样本图像以及两个具有相同图像的子菜单。所有三个菜单都将 LayoutStyle 属性 设置为 ToolStripLayoutStyle.Table,但实际上只有一个会显示为网格。

private void FillDropDown(ToolStripDropDown drop_down)
{
    // Set the drop down to a 2 column table layout
    drop_down.LayoutStyle = ToolStripLayoutStyle.Table;
    TableLayoutSettings table_layout_settings = (TableLayoutSettings)drop_down.LayoutSettings;
    table_layout_settings.ColumnCount = 2;

    // Fill the menu with some colour swatches
    Color[] colours = { Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Blue, Color.Purple };
    foreach (Color colour in colours) {
        ToolStripMenuItem item = new ToolStripMenuItem();
        Bitmap swatch = new Bitmap(64, 64);
        using (Graphics g = Graphics.FromImage(swatch))
        using (SolidBrush b = new SolidBrush(colour)) {
            g.FillRectangle(b, 0, 0, 64, 64);
        }
        item.Image = swatch;
        item.DisplayStyle = ToolStripItemDisplayStyle.Image;
        item.Margin = new Padding(2, 2, 2, 2);
        drop_down.Items.Add(item);
    }
}

private void ShowColorMenu(Point screen_location)
{
    ContextMenuStrip context_menu = new ContextMenuStrip();

    // The root context menu will not layout as a grid
    FillDropDown(context_menu);

    // This sub-menu will not layout as a grid
    ToolStripMenuItem sub_menu = new ToolStripMenuItem("Sub-menu");
    FillDropDown(sub_menu.DropDown);
    context_menu.Items.Add(sub_menu);

    // A sub-menu will layout as a grid if we create a new ToolStripDropDown for it
    ToolStripMenuItem grid_sub_menu = new ToolStripMenuItem("Grid Sub-menu");
    ToolStripDropDown new_drop_down = new ToolStripDropDown();
    FillDropDown(new_drop_down);
    grid_sub_menu.DropDown = new_drop_down;
    context_menu.Items.Add(grid_sub_menu);

    context_menu.Show(screen_location);
}

在我的机器上结果如下:

我想在上下文菜单的根目录中有一个图像网格。理解它为什么会这样也很好。我查看了 .NET 参考源,但这对这次没有帮助。

ContextMenuStrip cannot show its ToolStripMenuItems in a Table layout, because of a limitation in Menu presentation/layout and you also cannot cast ContextMenuStrip to ToolStripDropDown (or the other way around; it's a wrapper around ToolStripDropDownMenu,它不能转换到它的间接祖先:它将保留其特定功能(例如,您可以查看 TextBox 和 ListBox 都为 Control,但这并不意味着,现在,设置 ListBox 的 Text 实际上会在某处显示文本,只是因为 Text 属性 属于控件 class).

但是您可以像使用 ContextMenuStrip 一样直接使用和显示 ToolStripDropDown。完全支持 ToolStripDropDown 的 LayoutSettings can be cast directly to TableLayoutSettings and a LayoutStyle of type ToolStripLayoutStyle.Table


在示例中,包含以 Table 布局排列的 ToolStripMenuItem 的 ToolStripDropDown 对象用作 ContextMenuStrip 以 select 彩色图像应用于 PictureBox 控件,而 Color名称显示在 Label 控件中。

下拉菜单在窗体初始化时创建,在窗体的 ClientArea 中单击鼠标右键显示,在窗体关闭时删除:

Note1:在这里,我使用 Lambda 来订阅 ToolStripDropDown.ItemClicked 事件。但是,对于这种类型的控件,最好改用方法委托。

注2:ToolStripDropDown 处理调用contextColorMenu.Dispose();。如果频繁打开和关闭容器窗体,显式处理 ToolStripMenuItems Images 可能会更好。

using System.Drawing;
using System.Windows.Forms;

public partial class SomeForm : Form
{
    private ToolStripDropDown contextColorMenu = null;

    public SomeForm()
    {
        InitializeComponent();
        contextColorMenu = new ToolStripDropDown();
        contextColorMenu.ItemClicked += (o, a) => {
            // Assign the selected Bitmap to a PitureBox.Image
            picColor.Image = a.ClickedItem.Image;
            // Show the Color description in a Label
            lblColor.Text = ((Color)a.ClickedItem.Tag).ToString();
        };
        FillDropDown(contextColorMenu);
    }
    
    private void ShowColorMenu(Point location) => contextColorMenu.Show(location);

    private void FillDropDown(ToolStripDropDown dropDown)
    {
        dropDown.LayoutStyle = ToolStripLayoutStyle.Table;
        (dropDown.LayoutSettings as TableLayoutSettings).ColumnCount = 2;
        (dropDown.LayoutSettings as TableLayoutSettings).GrowStyle = TableLayoutPanelGrowStyle.AddRows;

        Color[] colors = { Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Blue, Color.Purple };

        foreach (Color color in colors) {
            var swatch = new Bitmap(64, 64);
            using (var g = Graphics.FromImage(swatch)) {
                g.Clear(color);
            }
            var item = new ToolStripMenuItem() {
                DisplayStyle = ToolStripItemDisplayStyle.Image,
                Image = swatch,
                Tag = color,
                Margin = new Padding(2),
                Padding = new Padding(2, 1, 2, 1)  // Fine tune the Items' Cell border
            };
            dropDown.Items.Add(item);
        }
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        if (e.Button == MouseButtons.Right) {
            ShowColorMenu(MousePosition);
        }
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        base.OnFormClosed(e);
        contextColorMenu?.Dispose();
    }
}