Windows.Forms button with drop-down menu

2019-01-11 01:05发布

I'm developing simple C# application using Windows.Forms on .NET. I need some button that will show a drop-down menu with subcategories - much like ToolStripMenu, but the button, you know. I searched for it and could not found any variants.

My question is: is there any way to do this, maybe some secret button property that allows attach menu to it?

Any help will be appreciated.

9条回答
SAY GOODBYE
2楼-- · 2019-01-11 01:11

You can show the ContextMenuStrip on the click event:

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

To make your own determination whether to show the menu above or below the button, you can try using this code, which measures the menu and determines whether or not it would be partially offscreen:

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));
  }    
}
查看更多
不美不萌又怎样
3楼-- · 2019-01-11 01:11

Button have down arrow right side of it and you can set menu of it from designer:

ss

With ShowMenuUnderCursor:

ss

MenuButton class:

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.ButtonShadow;
            Point[] arrows = new Point[] { new Point(arrowX, arrowY), new Point(arrowX + 7, arrowY), new Point(arrowX + 3, arrowY + 4) };
            pevent.Graphics.FillPolygon(brush, arrows);
        }
    }
}
查看更多
太酷不给撩
4楼-- · 2019-01-11 01:11

Jaex's MenuButton class above was perfect for me. I did add the logic below into the OnMouseDown so that the context menu would only show up if I clicked on the arrow. The normal click event would get triggered if I clicked in the larger portion. Allowed for a "Default" click action.

if (Menu != null && mevent.Button == MouseButtons.Left)
{
    if (mevent.Location.X >= this.Width - 14)
    {
        System.Drawing.Point menuLocation;

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

        Menu.Show(this, menuLocation);
    }
}

Thought this might be useful to someone. Thanks Jaex

查看更多
家丑人穷心不美
5楼-- · 2019-01-11 01:15

Expanding @Jaex answer a little bit to allow for a separator line, conditional drawing of the arrow if nothing is configured and a separate click event for the main button body and the menu arrow.

It should be noted that for better alignment you can set the button.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;

enter image description here

Here is my slight improvement

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);
            }
        }
    }
}
查看更多
闹够了就滚
6楼-- · 2019-01-11 01:20

Infragistics has the WinDropDownButton: http://help.infragistics.com/Help/NetAdvantage/WinForms/2012.1/CLR2.0/html/WinDropDownButton_About_WinDropDownButton.html

So it certainly exists, however you may not be looking for a paid third-party control.

查看更多
【Aperson】
7楼-- · 2019-01-11 01:28

I was fiddling with this issue as well and found an extremely simple solution (albeit a little dirty-hacky): place a ComboBox under the Button, such that it shows the dropdown arrow right next to the button.

Then use SelectedIndexChanged of the ComboBox to change the Button behaviour, or do what you want it to do immediately.

查看更多
登录 后发表回答