带有下拉菜单的Windows.Forms按钮

ton*_*ony 37 c# winforms drop-down-menu

我正在使用.NET上的Windows.Forms开发简单的C#应用​​程序.我需要一些按钮,它会显示一个包含子类别的下拉菜单 - 很像ToolStripMenu,但是按钮,你知道.我搜索它并找不到任何变种.

我的问题是:有没有办法做到这一点,也许一些秘密按钮属性,允许附加菜单吗?

任何帮助将不胜感激.

Jae*_*aex 58

按钮右下方有箭头,你可以从设计师设置它的菜单:

SS

使用ShowMenuUnderCursor:

SS

MenuButton类:

public class MenuButton : Button
{
    [DefaultValue(null)]
    public ContextMenuStrip Menu { get; set; }

    [DefaultValue(false)]
    public bool ShowMenuUnderCursor { get; set; }

    protected override void OnMouseDown(MouseEventArgs mevent)
    {
        base.OnMouseDown(mevent);

        if (Menu != null && mevent.Button == MouseButtons.Left)
        {
            Point menuLocation;

            if (ShowMenuUnderCursor)
            {
                menuLocation = mevent.Location;
            }
            else
            {
                menuLocation = new Point(0, Height);
            }

            Menu.Show(this, menuLocation);
        }
    }

    protected override void OnPaint(PaintEventArgs pevent)
    {
        base.OnPaint(pevent);

        if (Menu != null)
        {
            int arrowX = ClientRectangle.Width - 14;
            int arrowY = ClientRectangle.Height / 2 - 1;

            Brush brush = Enabled ? SystemBrushes.ControlText : SystemBrushes.ControlDark;
            Point[] arrows = new Point[] { new Point(arrowX, arrowY), new Point(arrowX + 7, arrowY), new Point(arrowX + 3, arrowY + 4) };
            pevent.Graphics.FillPolygon(brush, arrows);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的答复!发布问题时,我真的想到了带箭头的按钮,使用 Graphics API 绘制箭头是个好主意。 (2认同)
  • 太棒了!非常感谢你的回答 (2认同)
  • 为什么`Menu`的值总是`null`,不管`ContextMenuStrip`属性值如何设置? (2认同)
  • @Alex 将一个新的 ContextMenuStrip 对象添加到 `Menu` 属性,而不是 `ContextMenuStrip` 属性。`myButton.Menu = New ContextMenuStrip` .. 你可能在做 `myButton.ContextMenuStrip = New ContextMenuStrip` (2认同)

Lar*_*ech 45

您可以在click事件上显示ContextMenuStrip:

private void button1_Click(object sender, EventArgs e) {
  contextMenuStrip1.Show(button1, new Point(0, button1.Height));
}
Run Code Online (Sandbox Code Playgroud)

要自行确定是否在按钮上方或下方显示菜单,您可以尝试使用此代码来测量菜单并确定它是否部分在屏幕外:

private void button1_Click(object sender, EventArgs e) {
  Point screenPoint = button1.PointToScreen(new Point(button1.Left, button1.Bottom));
  if (screenPoint.Y + contextMenuStrip1.Size.Height > Screen.PrimaryScreen.WorkingArea.Height) {
    contextMenuStrip1.Show(button1, new Point(0, -contextMenuStrip1.Size.Height));
  } else {
    contextMenuStrip1.Show(button1, new Point(0, button1.Height));
  }    
}
Run Code Online (Sandbox Code Playgroud)


Sve*_*son 16

扩展@Jaex回答一点,以允许分隔线,如果没有配置任何箭头的条件绘图和主按钮主体和菜单箭头的单独点击事件.

应该注意的是,为了更好地对齐,你可以设置 button.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;

在此输入图像描述

这是我的微小改进

public class SplitButton : Button
{
    [DefaultValue(null), Browsable(true),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public ContextMenuStrip Menu { get; set; }

    [DefaultValue(20), Browsable(true),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public int SplitWidth { get; set; }

    public SplitButton() 
    {
        SplitWidth = 20;
    }

    protected override void OnMouseDown(MouseEventArgs mevent)
    {
        var splitRect = new Rectangle(this.Width - this.SplitWidth, 0, this.SplitWidth, this.Height);

        // Figure out if the button click was on the button itself or the menu split
        if (Menu != null && 
            mevent.Button == MouseButtons.Left &&
            splitRect.Contains(mevent.Location) )
        {
            Menu.Show(this, 0, this.Height);    // Shows menu under button
            //Menu.Show(this, mevent.Location); // Shows menu at click location
        }
        else
        {
            base.OnMouseDown(mevent);
        }
    }

    protected override void OnPaint(PaintEventArgs pevent)
    {
        base.OnPaint(pevent);

        if (this.Menu != null && this.SplitWidth > 0)
        { 
            // Draw the arrow glyph on the right side of the button
            int arrowX = ClientRectangle.Width - 14;
            int arrowY = ClientRectangle.Height / 2 - 1;

            var arrowBrush = Enabled ? SystemBrushes.ControlText : SystemBrushes.ButtonShadow;
            var arrows = new[] { new Point(arrowX, arrowY), new Point(arrowX + 7, arrowY), new Point(arrowX + 3, arrowY + 4) };
            pevent.Graphics.FillPolygon(arrowBrush, arrows);

            // Draw a dashed separator on the left of the arrow
            int lineX = ClientRectangle.Width - this.SplitWidth;
            int lineYFrom = arrowY - 4;
            int lineYTo = arrowY + 8;
            using( var separatorPen = new Pen(Brushes.DarkGray){DashStyle = DashStyle.Dot})
            {
                pevent.Graphics.DrawLine(separatorPen, lineX, lineYFrom, lineX, lineYTo);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • `var splitRect = new Rectangle(this.Width - this.SplitWidth,this.Location.Y,this.SplitWidth,this.Height);`第二个参数应改为**0** (4认同)
  • 杰基是对的。如果该参数不为 0,则下拉“单击区域”不会覆盖按钮的所有高度。 (2认同)
  • 我已编辑代码来修复@Jacky识别的错误. (2认同)
  • 这是非常好的@Sverrir我在我的项目中寻找这种类型的控制 (2认同)
  • 很棒的代码。我建议将从末尾开始的第 7 行更新为 `using (var seperatorPen = new Pen(Brushes.DarkGray) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dot })` 这样就不需要包含该库。 (2认同)

Mou*_*Mou 6

我们能做到这一点很容易.这可能有帮助:)

ContextMenuStrip contextMenuStrip1 = new ContextMenuStrip();

        private void button1_Click(object sender, EventArgs e)
        {
            contextMenuStrip1.Items.Clear();
            contextMenuStrip1.Items.Add("item1");
            contextMenuStrip1.Items.Add("item2");

            contextMenuStrip1.Show(button1, new Point(0, button1.Height));
        }

        private void contextMenuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem.Text == "item1")
            {
                MessageBox.Show(e.ClickedItem.Text);
            }
        }
Run Code Online (Sandbox Code Playgroud)


Jos*_*shL 5

最简单的选择是在仅展示单个按钮的未停靠的ToolStrip中使用ToolStripDropDownButton.然后你可以添加子项,等等.为此: - 将一个Toolstrip拖到你的控件/窗体上 - 使用布局助手添加一个DropDownButton - 将GripStyle设置为隐藏 - 将Dock设置为None

结果是一个独立的工具栏样式按钮,它支持您描述的下拉行为.