Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Hi!
I have just finished creating my first ever custom ToolStripRenderer, and I managed to create one that looks very similar to the VisualStudio 2008 IDE.
Here is a screenshot, with a shot of the VS2008 IDE menu for comparison:
There are a few very tiny differences:
1. The arrow after a nested submenu is still white, should be black. This will be fixed in the next update!
2. The menus overall seem smaller. The blue selection rectangles for example and the image margins. I don't know if I can change this since that is simply the size of a MenuStrip. If anyone has any ideas, let me know!
To make this work, all you need is the following code! No custom controls, just the usual MenuStrip and ToolStrip (not MainMenu and ToolBar!) controls.
Step 1
If you haven't already got a MenuStrip and/or ToolStrip control, add them now. You can rightclick them and choose "Insert Standard Items" to insert the standard items if you wish.
If you want your ToolStrip in a ToolStripContainer, add that too and add your MenuStrip and ToolStrip controls to the ToolStripContainer instead of the form.
Step 2
Add a new 'Module' and call it "clsColors.vb". This module hosts all the different color constants and a 'DrawRoundedRectangle' function (not mine). Use the following code for this module:
Code:
Module clsColors
Public clrHorBG_GrayBlue As Color = Color.FromArgb(255, 233, 236, 250)
Public clrHorBG_White As Color = Color.FromArgb(255, 244, 247, 252)
Public clrSubmenuBG As Color = Color.FromArgb(255, 240, 240, 240)
Public clrImageMarginBlue As Color = Color.FromArgb(255, 212, 216, 230)
Public clrImageMarginWhite As Color = Color.FromArgb(255, 244, 247, 252)
Public clrImageMarginLine As Color = Color.FromArgb(255, 160, 160, 180)
Public clrSelectedBG_Blue As Color = Color.FromArgb(255, 186, 228, 246)
Public clrSelectedBG_Header_Blue As Color = Color.FromArgb(255, 146, 202, 230)
Public clrSelectedBG_White As Color = Color.FromArgb(255, 241, 248, 251)
Public clrSelectedBG_Border As Color = Color.FromArgb(255, 150, 217, 249)
Public clrSelectedBG_Drop_Blue As Color = Color.FromArgb(255, 139, 195, 225)
Public clrSelectedBG_Drop_Border As Color = Color.FromArgb(255, 48, 127, 177)
Public clrMenuBorder As Color = Color.FromArgb(255, 160, 160, 160)
Public clrCheckBG As Color = Color.FromArgb(255, 206, 237, 250)
Public clrVerBG_GrayBlue As Color = Color.FromArgb(255, 196, 203, 219)
Public clrVerBG_White As Color = Color.FromArgb(255, 250, 250, 253)
Public clrVerBG_Shadow As Color = Color.FromArgb(255, 181, 190, 206)
Public clrToolstripBtnGrad_Blue As Color = Color.FromArgb(255, 129, 192, 224)
Public clrToolstripBtnGrad_White As Color = Color.FromArgb(255, 237, 248, 253)
Public clrToolstripBtn_Border As Color = Color.FromArgb(255, 41, 153, 255)
Public clrToolstripBtnGrad_Blue_Pressed As Color = Color.FromArgb(255, 124, 177, 204)
Public clrToolstripBtnGrad_White_Pressed As Color = Color.FromArgb(255, 228, 245, 252)
Public Sub DrawRoundedRectangle(ByVal objGraphics As Graphics, _
ByVal m_intxAxis As Integer, _
ByVal m_intyAxis As Integer, _
ByVal m_intWidth As Integer, _
ByVal m_intHeight As Integer, _
ByVal m_diameter As Integer, ByVal color As Color)
Dim pen As New Pen(color)
'Dim g As Graphics
Dim BaseRect As New RectangleF(m_intxAxis, m_intyAxis, m_intWidth, m_intHeight)
Dim ArcRect As New RectangleF(BaseRect.Location, New SizeF(m_diameter, m_diameter))
'top left Arc
objGraphics.DrawArc(pen, ArcRect, 180, 90)
objGraphics.DrawLine(pen, m_intxAxis + CInt(m_diameter / 2), m_intyAxis, m_intxAxis + m_intWidth - CInt(m_diameter / 2), m_intyAxis)
' top right arc
ArcRect.X = BaseRect.Right - m_diameter
objGraphics.DrawArc(pen, ArcRect, 270, 90)
objGraphics.DrawLine(pen, m_intxAxis + m_intWidth, m_intyAxis + CInt(m_diameter / 2), m_intxAxis + m_intWidth, m_intyAxis + m_intHeight - CInt(m_diameter / 2))
' bottom right arc
ArcRect.Y = BaseRect.Bottom - m_diameter
objGraphics.DrawArc(pen, ArcRect, 0, 90)
objGraphics.DrawLine(pen, m_intxAxis + CInt(m_diameter / 2), m_intyAxis + m_intHeight, m_intxAxis + m_intWidth - CInt(m_diameter / 2), m_intyAxis + m_intHeight)
' bottom left arc
ArcRect.X = BaseRect.Left
objGraphics.DrawArc(pen, ArcRect, 90, 90)
objGraphics.DrawLine(pen, m_intxAxis, m_intyAxis + CInt(m_diameter / 2), m_intxAxis, m_intyAxis + m_intHeight - CInt(m_diameter / 2))
End Sub
End Module
(Continued next post...)
Last edited by NickThissen; Sep 19th, 2008 at 03:55 PM.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Step 3
Add a new 'Class' and call it "clsMenuRenderer". This will be the MenuStripRenderer. Put the following code in the new class:
Code:
Public Class clsMenuRenderer
Inherits System.Windows.Forms.ToolStripRenderer
'// Make sure the textcolor is black
Protected Overrides Sub InitializeItem(ByVal item As System.Windows.Forms.ToolStripItem)
MyBase.InitializeItem(item)
item.ForeColor = Color.Black
End Sub
Protected Overrides Sub Initialize(ByVal toolStrip As System.Windows.Forms.ToolStrip)
MyBase.Initialize(toolStrip)
toolStrip.ForeColor = Color.Black
End Sub
'// Render horizontal bakcground gradient
Protected Overrides Sub OnRenderToolStripBackground(ByVal e As ToolStripRenderEventArgs)
MyBase.OnRenderToolStripBackground(e)
Dim b As New Drawing2D.LinearGradientBrush(e.AffectedBounds, clrHorBG_GrayBlue, clrHorBG_White, _
Drawing2D.LinearGradientMode.Horizontal)
e.Graphics.FillRectangle(b, e.AffectedBounds)
End Sub
'// Render Image Margin and gray itembackground
Protected Overrides Sub OnRenderImageMargin(ByVal e As System.Windows.Forms.ToolStripRenderEventArgs)
MyBase.OnRenderImageMargin(e)
'// Draw ImageMargin background gradient
Dim b As New Drawing2D.LinearGradientBrush(e.AffectedBounds, clrImageMarginWhite, clrImageMarginBlue, _
Drawing2D.LinearGradientMode.Horizontal)
'// Shadow at the right of image margin
Dim DarkLine As New Drawing.SolidBrush(clrImageMarginLine)
Dim WhiteLine As New Drawing.SolidBrush(Color.White)
Dim rect As New Rectangle(e.AffectedBounds.Width, 2, 1, e.AffectedBounds.Height)
Dim rect2 As New Rectangle(e.AffectedBounds.Width + 1, 2, 1, e.AffectedBounds.Height)
'// Gray background
Dim SubmenuBGbrush As New Drawing.SolidBrush(clrSubmenuBG)
Dim rect3 As New Rectangle(0, 0, e.ToolStrip.Width, e.ToolStrip.Height)
'// Border
Dim borderPen As New Pen(clrMenuBorder)
Dim rect4 As 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)
End Sub
'// Render Checkmark
Protected Overrides Sub OnRenderItemCheck(ByVal e As System.Windows.Forms.ToolStripItemImageRenderEventArgs)
MyBase.OnRenderItemCheck(e)
If e.Item.Selected Then
Dim rect As New Rectangle(3, 1, 20, 20)
Dim rect2 As New Rectangle(4, 2, 18, 18)
Dim b As New Drawing.SolidBrush(clrToolstripBtn_Border)
Dim b2 As New Drawing.SolidBrush(clrCheckBG)
e.Graphics.FillRectangle(b, rect)
e.Graphics.FillRectangle(b2, rect2)
e.Graphics.DrawImage(e.Image, New Point(5, 3))
Else
Dim rect As New Rectangle(3, 1, 20, 20)
Dim rect2 As New Rectangle(4, 2, 18, 18)
Dim b As New Drawing.SolidBrush(clrSelectedBG_Drop_Border)
Dim b2 As New Drawing.SolidBrush(clrCheckBG)
e.Graphics.FillRectangle(b, rect)
e.Graphics.FillRectangle(b2, rect2)
e.Graphics.DrawImage(e.Image, New Point(5, 3))
End If
End Sub
'// Render separator
Protected Overrides Sub OnRenderSeparator(ByVal e As System.Windows.Forms.ToolStripSeparatorRenderEventArgs)
MyBase.OnRenderSeparator(e)
Dim DarkLine As New Drawing.SolidBrush(clrImageMarginLine)
Dim WhiteLine As New Drawing.SolidBrush(Color.White)
Dim rect As New Rectangle(32, 3, e.Item.Width - 32, 1)
Dim rect2 As New Rectangle(32, 4, e.Item.Width - 32, 1)
e.Graphics.FillRectangle(DarkLine, rect)
e.Graphics.FillRectangle(WhiteLine, rect2)
End Sub
'// Render arrow
Protected Overrides Sub OnRenderArrow(ByVal e As System.Windows.Forms.ToolStripArrowRenderEventArgs)
e.ArrowColor = Color.Black
MyBase.OnRenderArrow(e)
End Sub
'// Render Menuitem background: lightblue if selected, darkblue if dropped down
Protected Overrides Sub OnRenderMenuItemBackground(ByVal e As System.Windows.Forms.ToolStripItemRenderEventArgs)
MyBase.OnRenderMenuItemBackground(e)
If e.Item.Enabled Then
If e.Item.IsOnDropDown = False AndAlso e.Item.Selected Then
'// If item is MenuHeader and selected: draw darkblue border
Dim rect As New Rectangle(3, 2, e.Item.Width - 6, e.Item.Height - 4)
Dim b As New Drawing2D.LinearGradientBrush(rect, clrSelectedBG_White, clrSelectedBG_Header_Blue, Drawing2D.LinearGradientMode.Vertical)
Dim b2 As New Drawing.SolidBrush(clrToolstripBtn_Border)
e.Graphics.FillRectangle(b, rect)
clsColors.DrawRoundedRectangle(e.Graphics, rect.Left - 1, rect.Top - 1, rect.Width, rect.Height + 1, 4, clrToolstripBtn_Border)
clsColors.DrawRoundedRectangle(e.Graphics, rect.Left - 2, rect.Top - 2, rect.Width + 2, rect.Height + 3, 4, Color.White)
e.Item.ForeColor = Color.Black
ElseIf e.Item.IsOnDropDown AndAlso e.Item.Selected Then
'// If item is NOT menuheader (but subitem) and selected: draw lightblue border
Dim rect As New Rectangle(4, 2, e.Item.Width - 6, e.Item.Height - 4)
Dim b As New Drawing2D.LinearGradientBrush(rect, clrSelectedBG_White, clrSelectedBG_Blue, Drawing2D.LinearGradientMode.Vertical)
Dim b2 As New Drawing.SolidBrush(clrSelectedBG_Border)
e.Graphics.FillRectangle(b, rect)
clsColors.DrawRoundedRectangle(e.Graphics, rect.Left - 1, rect.Top - 1, rect.Width, rect.Height + 1, 6, clrSelectedBG_Border)
e.Item.ForeColor = Color.Black
End If
'// If item is MenuHeader and menu is dropped down: selection rectangle is now darker
If CType(e.Item, ToolStripMenuItem).DropDown.Visible AndAlso e.Item.IsOnDropDown = False Then 'CType(e.Item, ToolStripMenuItem).OwnerItem Is Nothing Then
Dim rect As New Rectangle(3, 2, e.Item.Width - 6, e.Item.Height - 4)
Dim b As New Drawing2D.LinearGradientBrush(rect, Color.White, clrSelectedBG_Drop_Blue, Drawing2D.LinearGradientMode.Vertical)
Dim b2 As New Drawing.SolidBrush(clrSelectedBG_Drop_Border)
e.Graphics.FillRectangle(b, rect)
clsColors.DrawRoundedRectangle(e.Graphics, rect.Left - 1, rect.Top - 1, rect.Width, rect.Height + 1, 4, clrSelectedBG_Drop_Border)
clsColors.DrawRoundedRectangle(e.Graphics, rect.Left - 2, rect.Top - 2, rect.Width + 2, rect.Height + 3, 4, Color.White)
e.Item.ForeColor = Color.Black
End If
End If
End Sub
End Class
Step 4
Add another new 'Class' and call it "clsToolstripRenderer". This will be the ToolStrip Renderer. Put the following code in the new class:
Code:
Public Class clsToolstripRenderer
Inherits System.Windows.Forms.ToolStripProfessionalRenderer
'// Render container background gradient
Protected Overrides Sub OnRenderToolStripBackground(ByVal e As ToolStripRenderEventArgs)
MyBase.OnRenderToolStripBackground(e)
Dim b As New Drawing2D.LinearGradientBrush(e.AffectedBounds, clrVerBG_White, clrVerBG_GrayBlue, _
Drawing2D.LinearGradientMode.Vertical)
Dim shadow As New Drawing.SolidBrush(clrVerBG_Shadow)
Dim rect As New Rectangle(0, e.ToolStrip.Height - 2, e.ToolStrip.Width, 1)
e.Graphics.FillRectangle(b, e.AffectedBounds)
e.Graphics.FillRectangle(shadow, rect)
End Sub
'// Render button selected and pressed state
Protected Overrides Sub OnRenderButtonBackground(ByVal e As System.Windows.Forms.ToolStripItemRenderEventArgs)
MyBase.OnRenderButtonBackground(e)
If e.Item.Selected Or CType(e.Item, ToolStripButton).Checked Then
Dim rectBorder As New Rectangle(0, 0, e.Item.Width - 1, e.Item.Height - 1)
Dim rect As New Rectangle(1, 1, e.Item.Width - 2, e.Item.Height - 2)
Dim b As New Drawing2D.LinearGradientBrush(rect, clrToolstripBtnGrad_White, clrToolstripBtnGrad_Blue, _
Drawing2D.LinearGradientMode.Vertical)
Dim b2 As New Drawing.SolidBrush(clrToolstripBtn_Border)
e.Graphics.FillRectangle(b2, rectBorder)
e.Graphics.FillRectangle(b, rect)
End If
If e.Item.Pressed Then
Dim rectBorder As New Rectangle(0, 0, e.Item.Width - 1, e.Item.Height - 1)
Dim rect As New Rectangle(1, 1, e.Item.Width - 2, e.Item.Height - 2)
Dim b As New Drawing2D.LinearGradientBrush(rect, clrToolstripBtnGrad_White_Pressed, clrToolstripBtnGrad_Blue_Pressed, _
Drawing2D.LinearGradientMode.Vertical)
Dim b2 As New Drawing.SolidBrush(clrToolstripBtn_Border)
e.Graphics.FillRectangle(b2, rectBorder)
e.Graphics.FillRectangle(b, rect)
End If
End Sub
End Class
Continued next post...
Last edited by NickThissen; Sep 19th, 2008 at 03:55 PM.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Step 5
Put the following code in your Form_Load event. It sets the MenuStrip and ToolStrip renderer to the custom renderer:
Code:
MenuStrip1.Renderer = New clsMenuRenderer
ToolStrip1.Renderer = New clsToolstriprenderer
If you added these controls in a ToolStripContainer, use the following code to make the background of the ToolStripContainer the same as the MenuStrip background:
Code:
Private Sub ToolStripContainer1_TopToolStripPanel_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles ToolStripContainer1.TopToolStripPanel.Paint
Dim g As Graphics = e.Graphics
Dim rect As New Rectangle(0, 0, ToolStripContainer1.Width, Me.Height)
Dim b As New Drawing2D.LinearGradientBrush(rect, clrHorBG_GrayBlue, clrHorBG_White, Drawing2D.LinearGradientMode.Horizontal)
g.FillRectangle(b, rect)
End Sub
Private Sub ToolStripContainer1_TopToolStripPanel_SizeChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripContainer1.TopToolStripPanel.SizeChanged
ToolStripContainer1.Invalidate()
End Sub
That's it!
If you have any questions / suggestions, feel free to let me know!
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Cheers, glad to could have helped you, it was fun to do this!
By the way, I have updated my code above (the clsMenuRenderer, clsToolstripRenderer and clsColors files aswell) to include the following:
- Black arrow instead of white on MenuStrips
- Checked buttons on a ToolStrip show a blue background instead of only a single blue 1 px border
Also, if you need to use a ToolStripDropDown button to show a Dropdown menu, I would advise you to add a new 'ToolStripDropDownMenu' control and assign that control to the ToolStripDropDownButton's "DropDown" property. Then in the Form_Load event, set the ToolStripDropDownMenu renderer to 'New clsMenuRenderer' just like you did with the standard MenuStrip.
If you don't do this and leave the default toolstripdropdown for a dropdownbutton it will look completely wrong.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Do you mean how to insert the separators and shortcuts into the MenuStrip or do you mean how I managed to draw them the way my custom renderer does?
If it's the first case:
You insert a separator by either rightclicking on the "Type here" box and choosing "Insert - Separator", or by simply typing a "-" as the text.
For the shortcuts, there are three important properties in any 'ToolStripMenuItem' control:
The ShowShortcutKeys property which decides if the shortcut keys are displayed or not (default is True so there should be no problem).
The ShortcutKeys property which allows you to select the desired shortcut. This will display the shortcut if ShowShortcutKeys is True and also actually behave like a shortcut, meaning if you press that shortcut on your keyboard, the corresponding ToolStripMenuItem will get clicked.
Lastly there is the ShortcutKeyDisplayString property which allows you to make the MenuItem show a different shortcut than it actually is. An example where you would need this is for example if you want a MenuItem to insert a Tab into a textbox. Since you cannot set "Tab" as a shortcut key (because pressing the Tab key already adds a tab automatically if the textbox is selected), you can still show the user that pressing Tab is the same as pressing the MenuItem by setting the ShortcutKeyDisplayString to "Tab".
Anyway, you can also rightclick the MenuStrip (and ToolStrip for that matter) and choose "Insert Standard Items" to insert the standard "File - New, Open, Save ...", "Edit - Cut, Copy etc.." MenuItems.
----
If you didn't mean that after all but meant how I actually drew it and I just typed all that for nothing (), just have a look at the code.
Most (if not all) of the drawing is done in the "Protected Overrides Sub OnRender..." subs. Any actions in these subs override the standard rendering actions. The text, shape and everything is already in place if you don't touch that, I merely 'paste' a new layer of colours and shapes on top of the old one.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Arh doh i actually already knew that thing with the seperator, i just forget fast when i don't really work with it But thanks for clarifying.
And thanks for the shortcut help, exactly what i needed.
So, what about other form controls? Like a ProgressBar or some likely? How can you alter that?
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Hmm I have no idea actually... I suppose you could always override its Paint event but the MenuStrips and ToolStrips have a handy little "Renderer" property. You can then create a custom renderer class inheriting from the default ToolStripRenderer and adept that to your needs. As far as I know there is no such thing for other controls.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
What do you mean, go on the whole form?
And I might do a windows 7 theme if I find some time and motivation to do it.. I've already started a vista theme that I never finished so I doubt it
Why not give it a shot yourself? Use my code as a guide. It's not very hard, just a little time consuming.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
HI
I got an error
Value of type can not be converted to system.window.form toolstriprender.
I use ur instructions but use only menustrip no tool bar i want to use.
help me plz
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
HI
I got an error
Value of type can not be converted to system.window.form toolstriprender.
I use ur instructions but use only menustrip no tool bar i want to use.
help me plz
MenuStrip1.Renderer = New clsMenuRenderer
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Originally Posted by coolcurrent4u
@NickThissen
how do i add a transparent color and use it, i need the toolbar background to be transparent.
thanks
I don't think you can, really. You might be able to set a transparent color via code but even then I think it won't show the background through of whatever is behind it. I've never tried it though.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Hey Nick,
Just started playing around in VB.Net and VS2010 so I don't understand all the code yet.
My question is how do you get the shaded strip on the File Menu?
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
I think you control the size via the size of the icons actually. I've done this several times but I can remember I'm always struggling with it, trying various things until it just suddenly becomes bigger :P I think there is a property that controls the size of the images, which ultimately also determines the size of the toolstrip itself.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Hey nick.
Sorry to rebump this thread, but I wanted to ask: I love your works. How would I be able Or you already made one?) Windows 7 Menustrip render?
Or maybe MAC OS X. Linux.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
I have not done a Windows 7 theme in winforms. I have got a pretty decent one in WPF though. If you can use WPF then I could try to tidy it up a bit and post it.
Re: Custom VisualStudio2008 style MenuStrip and ToolStrip Renderer
Originally Posted by NickThissen
I have not done a Windows 7 theme in winforms. I have got a pretty decent one in WPF though. If you can use WPF then I could try to tidy it up a bit and post it.
Ah... well I don't use WPF (I don't really know how to, heh, I'm kind of new). Well, I really wanted Windows 7 or Mac OS X/Linux theme.
Errors when trying to replicate using community 2015
As stated in the Title, I am running this in Community 2015. My question is can this be done in 2015, and if it can, what did I do wrong?
Code:
' Render container background gradient: Overload resolution failed because no accessible 'New' accepts this number of arguments.
Protected Overrides Sub OnRenderToolStripBackground(e As ToolStripRenderEventArgs)
MyBase.OnRenderToolStripBackground(e)
Dim b As New Drawing2D.LinearGradientBrush(e.AffectedBounds, clrVerBG_White, clrVerBG_GrayBlue,
Drawing2D.LinearGradientMode.Vertical)
Dim shadow As New Drawing.SolidBrush(clrVerBG_Shadow)
Dim rect As New Rectangle(0, e.ToolStrip.Height - 2, e.ToolStrip.Width - 1) ' Error on this line
e.Graphics.FillRectangle(b, e.AffectedBounds)
e.Graphics.FillRectangle(shadow, rect)
End Sub
Code:
' OnRenderImageMargin: Error on last FillRectangle. says it failed because Value of type Pen cannot be converted to Brush
e.Graphics.FillRectangle(SubmenuBGbrush, rect3)
e.Graphics.FillRectangle(b, e.AffectedBounds)
e.Graphics.FillRectangle(DarkLine, rect)
e.Graphics.FillRectangle(WhiteLine, rect2)
e.Graphics.FillRectangle(borderPen, rect4) ' Error on this line