Figure 1. Illustration of what this Renderer implementation does, with a comparison screenshot of Visual Studio.
Introduction
I was developing an IDE-like software project and was looking around for a ToolStripRenderer
class written in C# which would aid me in making my MenuStrip
, ContextMenuStrip
, and ToolStrip
controls look more like Visual Studio's variety.
I had no such luck in finding versions written in C# but a Google search did come across this excellent article on VBForums by Nick Thissen. Unfortunately, it was done in VB.NET. For my project, I needed a C# version.
Plus, I was being lazy and didn't want to add the standard MenuStrip
, ContextMenuStrip
, and ToolStrip
controls from the Toolbox and then have to go in by hand into the code and set the Renderer
property on each. As you increase the number of such controls in your project, going into some file somewhere and tweaking code gets cumbersome. Why not save trouble and have the Designer do it, by adding custom controls from a library and just dropping them onto the form, renderer and all?
This is how this article adapts, and hopefully improves upon, Mr. Thissen's article.
Background
This article does the same thing, by translating the VB.NET code provided by Mr. Thissen into the equivalent C#. We will guide you step by step through adding this capability to your own programs.
Using the Code
My first step in hopefully improving upon Mr. Thissen's work was to first start by creating a new Class Library project. I named the Class Library project the VS2008StripRenderingLibrary
and added to it the following classes.
The C# Version of a Module
Mr. Thissen starts his article out having us build a Module
in VB which holds global constants for colors, etc. Since we do not have access to Modules
in C#, the best we can do is a Class full of a bunch of static
members. The first class to add is a class to hold Color
values. The source code is thus:
using System.Drawing;
using System;
namespace VS2008StripRenderingLibrary {
public class clsColor {
public static Color clrHorBG_GrayBlue = Color.FromArgb(255, 233, 236, 250);
public static Color clrHorBG_White = Color.FromArgb(255, 244, 247, 252);
public static Color clrSubmenuBG = Color.FromArgb(255, 240, 240, 240);
public static Color clrImageMarginBlue = Color.FromArgb(255, 212, 216, 230);
public static Color clrImageMarginWhite = Color.FromArgb(255, 244, 247, 252);
public static Color clrImageMarginLine = Color.FromArgb(255, 160, 160, 180);
public static Color clrSelectedBG_Blue = Color.FromArgb(255, 186, 228, 246);
public static Color clrSelectedBG_Header_Blue =
Color.FromArgb(255, 146, 202, 230);
public static Color clrSelectedBG_White = Color.FromArgb(255, 241, 248, 251);
public static Color clrSelectedBG_Border = Color.FromArgb(255, 150, 217, 249);
public static Color clrSelectedBG_Drop_Blue = Color.FromArgb(255, 139, 195, 225);
public static Color clrSelectedBG_Drop_Border = Color.FromArgb(255, 48, 127, 177);
public static Color clrMenuBorder = Color.FromArgb(255, 160, 160, 160);
public static Color clrCheckBG = Color.FromArgb(255, 206, 237, 250);
public static Color clrVerBG_GrayBlue = Color.FromArgb(255, 196, 203, 219);
public static Color clrVerBG_White = Color.FromArgb(255, 250, 250, 253);
public static Color clrVerBG_Shadow = Color.FromArgb(255, 181, 190, 206);
public static Color clrToolstripBtnGrad_Blue = Color.FromArgb(255, 129, 192, 224);
public static Color clrToolstripBtnGrad_White =
Color.FromArgb(255, 237, 248, 253);
public static Color clrToolstripBtn_Border = Color.FromArgb(255, 41, 153, 255);
public static Color clrToolstripBtnGrad_Blue_Pressed =
Color.FromArgb(255, 124, 177, 204);
public static Color clrToolstripBtnGrad_White_Pressed =
Color.FromArgb(255, 228, 245, 252);
public static void DrawRoundedRectangle(Graphics g, int x , int y ,
int width, int height, int m_diameter , Color color ) {
using (Pen pen = new Pen(color)) {
var BaseRect = new RectangleF(x, y, width, height);
var ArcRect = new RectangleF(BaseRect.Location,
new SizeF(m_diameter, m_diameter));
g.DrawArc(pen, ArcRect, 180, 90);
g.DrawLine(pen, x + Convert.ToInt32(m_diameter / 2),
y, x + width - Convert.ToInt32(m_diameter / 2), y);
ArcRect.X = BaseRect.Right - m_diameter;
g.DrawArc(pen, ArcRect, 270, 90);
g.DrawLine(pen, x + width, y + Convert.ToInt32(m_diameter / 2),
x + width, y + height - Convert.ToInt32(m_diameter / 2));
ArcRect.Y = BaseRect.Bottom - m_diameter;
g.DrawArc(pen, ArcRect, 0, 90);
g.DrawLine(pen, x + Convert.ToInt32(m_diameter / 2),
y + height, x + width - Convert.ToInt32(m_diameter / 2), y + height);
ArcRect.X = BaseRect.Left;
g.DrawArc(pen, ArcRect, 90, 90);
g.DrawLine(pen, x, y + Convert.ToInt32(m_diameter / 2),
x, y + height - Convert.ToInt32(m_diameter / 2));
}
}
}
}
Listing 1. The clsolor
class, which stores Color
constants and also provides a static function for rendering rounded rectangles. This is exactly as Nick Thissen's code; I just used Find and Replace to make it into valid C#. Notice also, instead of using a VB Module
, I instead had to make a class with static variables. C# does not support Module
s.
Code to Render Menus
The next class to add was a class I called VS2008MenuRenderer
, which is defined as follows:
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace VS2008StripRenderingLibrary {
public class VS2008MenuRenderer : ToolStripRenderer {
protected override void InitializeItem(ToolStripItem item) {
base.InitializeItem(item);
item.ForeColor = Color.Black;
}
protected override void Initialize(ToolStrip toolStrip) {
base.Initialize(toolStrip);
toolStrip.ForeColor = Color.Black;
}
protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) {
base.OnRenderToolStripBackground(e);
LinearGradientBrush b = new LinearGradientBrush(e.AffectedBounds,
clsColor.clrHorBG_GrayBlue, clsColor.clrHorBG_White,
LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(b, e.AffectedBounds);
}
protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) {
base.OnRenderImageMargin(e);
LinearGradientBrush b = new LinearGradientBrush
(e.AffectedBounds, clsColor.clrImageMarginWhite,
clsColor.clrImageMarginBlue, LinearGradientMode.Horizontal);
var DarkLine = new SolidBrush(clsColor.clrImageMarginLine);
var WhiteLine = new SolidBrush(Color.White);
var rect = new Rectangle(e.AffectedBounds.Width,
2, 1, e.AffectedBounds.Height);
var rect2 = new Rectangle(e.AffectedBounds.Width + 1,
2, 1, e.AffectedBounds.Height);
var SubmenuBGbrush = new SolidBrush(clsColor.clrSubmenuBG);
var rect3 = new Rectangle(0, 0, e.ToolStrip.Width, e.ToolStrip.Height);
var borderPen = new Pen(clsColor.clrMenuBorder);
var rect4 = new Rectangle
(0, 1, e.ToolStrip.Width - 1, e.ToolStrip.Height - 2);
e.Graphics.FillRectangle(SubmenuBGbrush, rect3);
e.Graphics.FillRectangle(b, e.AffectedBounds);
e.Graphics.FillRectangle(DarkLine, rect);
e.Graphics.FillRectangle(WhiteLine, rect2);
e.Graphics.DrawRectangle(borderPen, rect4);
}
protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) {
base.OnRenderItemCheck(e);
if (e.Item.Selected) {
var rect = new Rectangle(3, 1, 20, 20);
var rect2 = new Rectangle(4, 2, 18, 18);
SolidBrush b = new SolidBrush(clsColor.clrToolstripBtn_Border);
SolidBrush b2 = new SolidBrush(clsColor.clrCheckBG);
e.Graphics.FillRectangle(b, rect);
e.Graphics.FillRectangle(b2, rect2);
e.Graphics.DrawImage(e.Image, new Point(5, 3));
} else {
var rect = new Rectangle(3, 1, 20, 20);
var rect2 = new Rectangle(4, 2, 18, 18);
SolidBrush b = new SolidBrush(clsColor.clrSelectedBG_Drop_Border);
SolidBrush b2 = new SolidBrush(clsColor.clrCheckBG);
e.Graphics.FillRectangle(b, rect);
e.Graphics.FillRectangle(b2, rect2);
e.Graphics.DrawImage(e.Image, new Point(5, 3));
}
}
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) {
base.OnRenderSeparator(e);
var DarkLine = new SolidBrush(clsColor.clrImageMarginLine);
var WhiteLine = new SolidBrush(Color.White);
var rect = new Rectangle(32, 3, e.Item.Width - 32, 1);
e.Graphics.FillRectangle(DarkLine, rect);
e.Graphics.FillRectangle(WhiteLine, rect);
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) {
e.ArrowColor = Color.Black;
base.OnRenderArrow(e);
}
protected override void OnRenderMenuItemBackground
(ToolStripItemRenderEventArgs e) {
base.OnRenderMenuItemBackground(e);
if (e.Item.Enabled) {
if (e.Item.IsOnDropDown == false && e.Item.Selected) {
var rect = new Rectangle(3, 2, e.Item.Width - 6, e.Item.Height - 4);
using (var b = new LinearGradientBrush
(rect, clsColor.clrSelectedBG_White,
clsColor.clrSelectedBG_Header_Blue,
LinearGradientMode.Vertical)) {
using (var b2 = new SolidBrush(clsColor.clrCheckBG)) {
e.Graphics.FillRectangle(b, rect);
clsColor.DrawRoundedRectangle(e.Graphics, rect.Left - 1,
rect.Top - 1, rect.Width, rect.Height + 1, 4,
clsColor.clrToolstripBtn_Border);
clsColor.DrawRoundedRectangle(e.Graphics, rect.Left - 2,
rect.Top - 2, rect.Width + 2, rect.Height + 3, 4,
Color.White);
e.Item.ForeColor = Color.Black;
}
}
} else if (e.Item.IsOnDropDown && e.Item.Selected) {
var rect = new Rectangle(4, 2, e.Item.Width - 6, e.Item.Height - 4);
using (var b = new LinearGradientBrush
(rect, clsColor.clrSelectedBG_White,
clsColor.clrSelectedBG_Blue, LinearGradientMode.Vertical)) {
using (var b2 = new SolidBrush(clsColor.clrSelectedBG_Border)) {
e.Graphics.FillRectangle(b, rect);
clsColor.DrawRoundedRectangle(e.Graphics,
rect.Left - 1, rect.Top - 1, rect.Width,
rect.Height + 1, 6, clsColor.clrSelectedBG_Border);
e.Item.ForeColor = Color.Black;
}
}
}
if ((e.Item as ToolStripMenuItem).DropDown.Visible &&
e.Item.IsOnDropDown == false) {
var rect = new Rectangle(3, 2, e.Item.Width - 6, e.Item.Height - 4);
using (var b = new LinearGradientBrush
(rect, Color.White, clsColor.clrSelectedBG_Drop_Blue,
LinearGradientMode.Vertical)) {
using (var b2 = new SolidBrush
(clsColor.clrSelectedBG_Drop_Border)) {
e.Graphics.FillRectangle(b, rect);
clsColor.DrawRoundedRectangle(
e.Graphics, rect.Left - 1, rect.Top - 1,
rect.Width, rect.Height + 1,
4, clsColor.clrSelectedBG_Drop_Border);
clsColor.DrawRoundedRectangle(
e.Graphics, rect.Left - 2, rect.Top - 2,
rect.Width + 2, rect.Height + 3, 4,
Color.White);
e.Item.ForeColor = Color.Black;
}
}
}
}
}
}
}
Listing 2. Code for the VS2008MenuStripRenderer
class, which is in charge of making MenuStrips
look like Visual Studio 2008.
Code to Render a ToolStrip
The code for the VS2008ToolStripRenderer
class is shown below. Just like the VS2008MenuStripRenderer
code, this code is also adapted straight from what Nick Thissen has written, except that I used Find and Replace to turn his VB.NET code into valid C# code.
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace VS2008StripRenderingLibrary {
public class VS2008ToolStripRenderer : ToolStripProfessionalRenderer {
protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) {
base.OnRenderToolStripBackground(e);
using (var b = new LinearGradientBrush(e.AffectedBounds,
clsColor.clrVerBG_White, clsColor.clrVerBG_GrayBlue,
LinearGradientMode.Vertical)) {
using (var shadow = new SolidBrush(clsColor.clrVerBG_Shadow)) {
var rect = new Rectangle
(0, e.ToolStrip.Height - 2, e.ToolStrip.Width, 1);
e.Graphics.FillRectangle(b, e.AffectedBounds);
e.Graphics.FillRectangle(shadow, rect);
}
}
}
protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) {
base.OnRenderButtonBackground(e);
var rectBorder = new Rectangle(0, 0, e.Item.Width - 1, e.Item.Height - 1);
var rect = new Rectangle(1, 1, e.Item.Width - 2, e.Item.Height - 2);
if (e.Item.Selected == true || (e.Item as ToolStripButton).Checked) {
using (var b = new LinearGradientBrush
(rect, clsColor.clrToolstripBtnGrad_White,
clsColor.clrToolstripBtnGrad_Blue, LinearGradientMode.Vertical)) {
using (var b2 = new SolidBrush(clsColor.clrToolstripBtn_Border)) {
e.Graphics.FillRectangle(b2, rectBorder);
e.Graphics.FillRectangle(b, rect);
}
}
}
if (e.Item.Pressed) {
using (var b = new LinearGradientBrush
(rect, clsColor.clrToolstripBtnGrad_White_Pressed,
clsColor.clrToolstripBtnGrad_Blue_Pressed,
LinearGradientMode.Vertical)) {
using (var b2 = new SolidBrush(clsColor.clrToolstripBtn_Border)) {
e.Graphics.FillRectangle(b2, rectBorder);
e.Graphics.FillRectangle(b, rect);
}
}
}
}
}
}
Listing 3. Code for the VS2008ToolStripRenderer
class, which is again just adapted straight from Nick Thissen's VB.NET code, translating it into valid C#.
Controls
Now for the controls you can use to just drop onto your form in place of ToolStrip
, MenuStrip
, ToolStripContainer
, or ContextMenuStrip
.
The controls are too easy to write. All we do is add yet more classes to our Class Library project, derived from the control classes listed above. Each new class only implements the constructor, setting each control's Renderer
property to a new object of either VS2008MenuRenderer
or VS2008ToolStripRenderer
.
The VS2008MenuStrip and VS2008ContextMenuStrip Controls
Code for the VS2008MenuStrip
control is as follows:
using System.Windows.Forms;
namespace VS2008StripRenderingLibrary {
public class VS2008MenuStrip : MenuStrip {
public VS2008MenuStrip() {
this.Renderer = new VS2008MenuRenderer();
}
}
public class VS2008ContextMenuStrip : ContextMenuStrip {
public VS2008ContextMenuStrip() {
this.Renderer = new VS2008MenuRenderer();
}
}
}
Listing 4. Code for the VS2008MenuStrip
and VS2008ContextMenuStrip
controls.
The VS2008ToolStrip Control
Code for the VS2008ToolStrip
control is as given below:
using System.Windows.Forms;
namespace VS2008StripRenderingLibrary {
public class VS2008ToolStrip : ToolStrip {
public VS2008ToolStrip() {
this.Renderer = new VS2008ToolStripRenderer();
}
}
}
Listing 5. Code for the VS2008ToolStrip
control.
Finally, we will want a ToolStripContainer
control to use for when we want to use a ToolStripContainer
and want to have the rendering be consistent.
The VS2008ToolStripContainer Control
The VS2008ToolStripContainer
control is written as given below:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace VS2008StripRenderingLibrary {
public class VS2008ToolStripContainer : ToolStripContainer {
public VS2008ToolStripContainer() {
this.TopToolStripPanel.Paint +=
new PaintEventHandler(TopToolStripPanel_Paint);
this.TopToolStripPanel.SizeChanged +=
new EventHandler(TopToolStripPanel_SizeChanged);
}
void TopToolStripPanel_SizeChanged(object sender, EventArgs e) {
this.Invalidate();
}
void TopToolStripPanel_Paint(object sender, PaintEventArgs e) {
Graphics g = e.Graphics;
var rect = new Rectangle(0, 0, this.Width, this.FindForm().Height);
using (LinearGradientBrush b = new LinearGradientBrush(
rect, clsColor.clrHorBG_GrayBlue,
clsColor.clrHorBG_White, LinearGradientMode.Horizontal)) {
g.FillRectangle(b, rect);
}
}
}
}
Listing 6. Code for the VS2008ToolStripContainer
control.
Points of Interest
Adding The Controls to A New Project
The benefits of using the VS2008StripRenderingLibrary
can be seen in the demo project included with this article. You can save yourself a lot of coding by letting the Designer do the work for you.
The figure below illustrates creating a project with a new, blank Form and a reference to the VS2008StripRenderingLibrary
library:
Figure 2. Adding controls to a new form already rendered like Visual Studio!
History
- 1st April, 2010: Article written and posted