Custom VisualStudio 2008-style MenuStrip and ToolStrip Renderer in C#





5.00/5 (6 votes)
An adaptation of Nick Thissen's article on VBForums translated to C# and bottled up into a Class Library you can just drop into your projects.

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)) {
//Dim g As Graphics
var BaseRect = new RectangleF(x, y, width, height);
var ArcRect = new RectangleF(BaseRect.Location,
new SizeF(m_diameter, m_diameter));
//top left Arc
g.DrawArc(pen, ArcRect, 180, 90);
g.DrawLine(pen, x + Convert.ToInt32(m_diameter / 2),
y, x + width - Convert.ToInt32(m_diameter / 2), y);
// top right arc
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));
// bottom right arc
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);
// bottom left arc
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 {
// Make sure the textcolor is black
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;
}
// Render horizontal background gradient
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);
}
// Render image margin and gray ItemBackground
protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) {
base.OnRenderImageMargin(e);
// Draw ImageMargin background gradient
LinearGradientBrush b = new LinearGradientBrush
(e.AffectedBounds, clsColor.clrImageMarginWhite,
clsColor.clrImageMarginBlue, LinearGradientMode.Horizontal);
// Shadow at the right of image margin
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);
// Gray background
var SubmenuBGbrush = new SolidBrush(clsColor.clrSubmenuBG);
var rect3 = new Rectangle(0, 0, e.ToolStrip.Width, e.ToolStrip.Height);
// Border
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);
}
// Render checkmark
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));
}
}
// Render separator
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);
}
// Render arrow
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) {
e.ArrowColor = Color.Black;
base.OnRenderArrow(e);
}
// Render MenuItem background: lightblue if selected, darkblue if dropped down
protected override void OnRenderMenuItemBackground
(ToolStripItemRenderEventArgs e) {
base.OnRenderMenuItemBackground(e);
if (e.Item.Enabled) {
if (e.Item.IsOnDropDown == false && e.Item.Selected) {
// If item is MenuHeader and selected: draw darkblue color
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) {
// If item is NOT menuheader (but subitem);
// and selected: draw lightblue border
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 item is MenuHeader and menu is dropped down;
// selection rectangle is now darker
if ((e.Item as ToolStripMenuItem).DropDown.Visible &&
e.Item.IsOnDropDown == false) {
// (e.Item as ToolStripMenuItem).OwnerItem == null
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 {
// Render custom background gradient
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);
}
}
}
// Render button selected and pressed state
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:

History
- 1st April, 2010: Article written and posted