web-dev-qa-db-fra.com

Bouton Windows.Forms avec menu déroulant

Je développe une application C # simple en utilisant Windows.Forms sur .NET. J'ai besoin d'un bouton qui affiche un menu déroulant avec des sous-catégories - un peu comme ToolStripMenu, mais le bouton, vous savez. Je l'ai cherché et n'ai trouvé aucune variante. 

Ma question est la suivante: existe-t-il un moyen de le faire, peut-être une propriété de bouton secret qui permet d’attacher un menu?

Toute aide serait appréciée.

33
tonytony

Vous pouvez afficher le ContextMenuStrip sur l'événement click:

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

Pour déterminer vous-même si vous souhaitez afficher le menu au-dessus ou au-dessous du bouton, vous pouvez essayer d'utiliser ce code, qui mesure le menu et détermine s'il sera ou non partiellement hors écran:

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));
  }    
}
42
LarsTech

Développer @Jaex answer un peu pour permettre une ligne de séparation, un dessin conditionnel de la flèche si rien n'est configuré et un événement de clic séparé pour le corps du bouton principal et la flèche du menu. 

Il est à noter que pour un meilleur alignement, vous pouvez définir le button.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; 

 enter image description here

Voici ma légère amélioration

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);
            }
        }
    }
}
14

facile était nous pouvons le faire. cela peut aider :)

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);
            }
        }
6
Mou

L'option la plus simple consisterait à utiliser le ToolStripDropDownButton dans un ToolStrip non ancré qui affiche uniquement le bouton unique. Vous pouvez ensuite ajouter des sous-éléments, etc. Pour ce faire: - faites glisser une barre d'outils sur votre contrôle/formulaire - utilisez l'aide de présentation pour ajouter un DropDownButton - définissez GripStyle sur Caché. .- régler le Dock sur Aucun

Le résultat est un bouton autonome de style de barre d'outils qui prend en charge le comportement de liste déroulante que vous avez décrit.

5
JoshL

Afficher le menu contextuel sous le bouton lorsque vous cliquez dessus.

2
clearpath

La classe MenuButton de Jaex ci-dessus était parfaite pour moi. J'ai ajouté la logique ci-dessous dans OnMouseDown afin que le menu contextuel ne s'affiche que si je clique sur la flèche. L'événement de clic normal serait déclenché si je cliquais dans la plus grande partie. Autorisé pour une action de clic "Par défaut".

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);
    }
}

Je pensais que cela pourrait être utile à quelqu'un. Merci Jaex

1
Jeff Scott

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

Cela existe donc, mais vous ne chercherez peut-être pas un contrôle payant par une tierce partie.

0
roken

Je me préoccupais également de ce problème et trouvais une solution extrêmement simple (bien qu'un peu sale-hacky): placez une variable ComboBox sous la variable Button, de sorte qu'elle affiche la flèche déroulante située juste à côté du bouton.

Utilisez ensuite SelectedIndexChanged de ComboBox pour modifier le comportement Button ou faites ce que vous voulez qu'il fasse immédiatement.

0
Bart Friederichs