Reputation: 54
I'm making a Web Browser, but I don't know how to add these buttons on top of its ContextMenuStrip:
A Sepator make columns for all items, but no for only one line.
Do you have any idea how to make that?
Upvotes: 1
Views: 616
Reputation: 32223
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:
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;
}
Upvotes: 3