How to add Controls, as a toolbar, to a ContextMenuStrip?

451 views Asked by At

I'm making a Web Browser, but I don't know how to add these buttons on top of its ContextMenuStrip:

enter image description here

A Sepator make columns for all items, but no for only one line.
Do you have any idea how to make that?

1

There are 1 answers

0
Jimi On

Here's a custom Component, inheriting ToolStripControlHost (as the .Net's ToolStripTextBox, ToolStripComboBox etc.) that hosts a UserControl instead of a standard Control.

▶ Building a UserControl, you can add whatever other Controls to its surface and manage their actions as usual, except the UserControl's Events are exposed through the ToolStripControlHost derived object, so implementers don't need to know anything about the underlying hosted Control.

The hosted Control's events that you want to expose are subscribed to overriding OnSubscribeControlEvents() (and of course unsubscribed to overriding OnUnsubscribeControlEvents), then raising a related or composed event that returns values as needed.

▶ This is shown in the ToolStripControlHost class, see the public event EventHandler<UserActionArgs> ButtonAction event. This event returns a custom EventArgs object which includes properties that allow to determine the action performed by the Button clicked.

When needed, the hosted Control is exposed through the ToolStripControlHost.Control property.


You can initialize an existing ContextMenuStrip (contextMenuStrip1, here) in the constructor of the Form that uses it:

public partial class Form1 : Form
{
    private ToolStripUserControl toolStripUC = new ToolStripUserControl();

    public Form1()
    {
        InitializeComponent();

        // Assigns an existing ContextMenuStrip, to the Form or any other Control
        this.ContextMenuStrip = contextMenuStrip1;

        // Inserts the ToolStripUserControl and a Separator
        contextMenuStrip1.Items.Insert(0, new ToolStripSeparator());
        contextMenuStrip1.Items.Insert(0, toolStripUC);

        // Subscribes to the ButtonAction event
        toolStripUC.ButtonAction += (s, e) 
            => { this.Text = $"Your Action is {e.UserAction}, triggered by {e.TriggerControl.Name}"; };
        contextMenuStrip1.Closed += (s, e) => this.Text = "Right-Click on me";
    }
}

ToolStripControlHost derived class:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

[ToolboxItem(false)]
public class ToolStripUserControl : ToolStripControlHost
{
    // Pass the UserControl, MenuStripNavigationBar, to the base class.
    public ToolStripUserControl() : base(new MenuStripNavigationBar()) { }

    public MenuStripNavigationBar NavigatorBar => Control as MenuStripNavigationBar;

    // Exposes the ButtonActions Dictionary, to redefine the Buttons' actions
    public Dictionary<Control, ButtonAction> ButtonActions {
        get => NavigatorBar.ButtonActions;
        set => NavigatorBar.ButtonActions = value;
    }

    // Subscribe to the events you want to expose...
    protected override void OnSubscribeControlEvents(Control ctl)
    {
        base.OnSubscribeControlEvents(ctl);
        var navigatorBar = (MenuStripNavigationBar)ctl;
        navigatorBar.UserAction += OnButtonAction;
    }

    // ...and then unsubscribe. This is called when the Form is destroyed
    protected override void OnUnsubscribeControlEvents(Control ctl)
    {
        base.OnUnsubscribeControlEvents(ctl);
        var navigatorBar = (MenuStripNavigationBar)ctl;
        navigatorBar.UserAction -= OnButtonAction;
    }

    // Exposes a public custom event
    public event EventHandler<UserActionArgs> ButtonAction;

    // Raises the event when an UserAction is triggered
    private void OnButtonAction(object sender, EventArgs e)
    {
        var ctl = sender as Control;
        var userAction = new UserActionArgs(ButtonActions[ctl], ctl);
        ButtonAction?.Invoke(this, userAction);
    }
}

Custom EventArgs object:

public class UserActionArgs : EventArgs
{
    public UserActionArgs(ButtonAction action, Control control)
    {
        UserAction = action;
        TriggerControl = control;
    }
    public ButtonAction UserAction { get; }
    public Control TriggerControl { get; }
}

Actions enumerator:

public enum ButtonAction
{
    MoveBack,
    MoveForward,
    PlayMusic,
    PlayAnimation
}

This is how it works:

ToolStripControlHost UserControl

This is the full UserControl, to ease testing:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

public partial class MenuStripNavigationBar : UserControl
{
    public event EventHandler UserAction;

    public MenuStripNavigationBar()
    {
        InitializeComponent();
        ButtonActions = new Dictionary<Control, ButtonAction>();

        buttonArrowLeft.Text = "⏪";
        ButtonActions.Add(buttonArrowLeft, ButtonAction.MoveBack);
        buttonArrowLeft.Click += ButtonsActionEvent;

        buttonArrowRight.Text = "⏩";
        ButtonActions.Add(buttonArrowRight, ButtonAction.MoveBack);
        buttonArrowRight.Click += ButtonsActionEvent;

        buttonMusicKey.Text = "♬";
        ButtonActions.Add(buttonMusicKey, ButtonAction.PlayMusic);
        buttonMusicKey.Click += ButtonsActionEvent;

        buttonAction.Text = "⛄";
        ButtonActions.Add(buttonAction, ButtonAction.PlayAnimation);
        buttonAction.Click += ButtonsActionEvent;
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public Dictionary<Control, ButtonAction> ButtonActions { get; set; }

    private void ButtonsActionEvent(object sender, EventArgs e) 
        => UserAction?.Invoke(sender, e);


    [ToolboxItem(false)]
    internal class ButtonPanel : Panel
    {
        private Color borderActiveColor = Color.LightSteelBlue;
        private Color borderInactiveColor = Color.Gray;
        private Color borderColor = Color.Transparent;

        public ButtonPanel()
        {
            SetStyle(ControlStyles.Selectable | ControlStyles.SupportsTransparentBackColor | 
                          ControlStyles.UserMouse | ControlStyles.StandardClick, true);
            BackColor = Color.Transparent;
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            OnEnter(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            OnLeave(e);
        }

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            bool hovered = ClientRectangle.Contains(PointToClient(MousePosition));
            borderColor = hovered ? Enabled ? borderActiveColor : borderInactiveColor : Color.Transparent;
            Invalidate();
        }

        protected override void OnLeave(EventArgs e)
        {
            if (ClientRectangle.Contains(PointToClient(MousePosition))) return;
            borderColor = Color.Transparent;
            base.OnLeave(e);
            Invalidate();
        }

        TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | 
            TextFormatFlags.NoPadding | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.SingleLine;

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            var textRect = Rectangle.Inflate(ClientRectangle, 0, -1);
            TextRenderer.DrawText(e.Graphics, Text, Font, textRect, ForeColor, Color.Empty, flags);
            ControlPaint.DrawBorder(e.Graphics, ClientRectangle, borderColor, ButtonBorderStyle.Solid);
        }
    }
}

Designer file:

partial class MenuStripNavigationBar
{
    private System.ComponentModel.IContainer components = null;

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null)) {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        this.buttonAction = new MenuStripNavigationBar.ButtonPanel();
        this.buttonArrowLeft = new MenuStripNavigationBar.ButtonPanel();
        this.buttonArrowRight = new MenuStripNavigationBar.ButtonPanel();
        this.buttonMusicKey = new MenuStripNavigationBar.ButtonPanel();
        this.SuspendLayout();
        // 
        // buttonAction
        // 
        this.buttonAction.BackColor = System.Drawing.Color.Transparent;
        this.buttonAction.Font = new System.Drawing.Font("Segoe UI Symbol", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.buttonAction.Location = new System.Drawing.Point(166, 2);
        this.buttonAction.Name = "buttonAction";
        this.buttonAction.Size = new System.Drawing.Size(42, 26);
        this.buttonAction.TabIndex = 0;
        // 
        // buttonArrowLeft
        // 
        this.buttonArrowLeft.BackColor = System.Drawing.Color.Transparent;
        this.buttonArrowLeft.Font = new System.Drawing.Font("Segoe UI Symbol", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.buttonArrowLeft.Location = new System.Drawing.Point(3, 2);
        this.buttonArrowLeft.Name = "buttonArrowLeft";
        this.buttonArrowLeft.Size = new System.Drawing.Size(42, 26);
        this.buttonArrowLeft.TabIndex = 0;
        // 
        // buttonArrowRight
        // 
        this.buttonArrowRight.BackColor = System.Drawing.Color.Transparent;
        this.buttonArrowRight.Font = new System.Drawing.Font("Segoe UI Symbol", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.buttonArrowRight.Location = new System.Drawing.Point(58, 2);
        this.buttonArrowRight.Name = "buttonArrowRight";
        this.buttonArrowRight.Size = new System.Drawing.Size(42, 26);
        this.buttonArrowRight.TabIndex = 0;
        // 
        // buttonMusicKey
        // 
        this.buttonMusicKey.BackColor = System.Drawing.Color.Transparent;
        this.buttonMusicKey.Font = new System.Drawing.Font("Segoe UI Symbol", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.buttonMusicKey.Location = new System.Drawing.Point(112, 2);
        this.buttonMusicKey.Name = "buttonMusicKey";
        this.buttonMusicKey.Size = new System.Drawing.Size(42, 26);
        this.buttonMusicKey.TabIndex = 0;
        // 
        // MenuStripNavigationBar
        // 
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
        this.BackColor = System.Drawing.Color.Transparent;
        this.Controls.Add(this.buttonMusicKey);
        this.Controls.Add(this.buttonArrowRight);
        this.Controls.Add(this.buttonArrowLeft);
        this.Controls.Add(this.buttonAction);
        this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.MaximumSize = new System.Drawing.Size(212, 30);
        this.MinimumSize = new System.Drawing.Size(212, 30);
        this.Name = "MenuStripNavigationBar";
        this.Size = new System.Drawing.Size(212, 30);
        this.ResumeLayout(false);
    }

    private ButtonPanel buttonAction;
    private ButtonPanel buttonArrowLeft;
    private ButtonPanel buttonArrowRight;
    private ButtonPanel buttonMusicKey;
}