Click here to Skip to main content
15,886,797 members
Articles / Programming Languages / C#

WindowsVistaRenderer: A New Button Generation

Rate me:
Please Sign up or sign in to vote.
4.86/5 (141 votes)
23 Nov 2007CPOL3 min read 283.7K   8.2K   322  
ToolStripRenderer that renders Vista like buttons
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Renderers
{
    /// <summary>
    /// Renders toolstrip items using Windows Vista look and feel
    /// </summary>
    /// <remarks>
    /// 2007 Jos� Manuel Men�ndez Poo
    /// Visit my blog for upgrades and other renderers - www.menendezpoo.com
    /// </remarks>
    public class WindowsVistaRenderer
        : ToolStripRenderer
    {
        #region Static

        /// <summary>
        /// Creates the glow of the buttons
        /// </summary>
        /// <param name="rectangle"></param>
        /// <returns></returns>
        private static GraphicsPath CreateBottomRadialPath(Rectangle rectangle)
        {
            GraphicsPath path = new GraphicsPath();
            RectangleF rect = rectangle;
            rect.X -= rect.Width * .35f;
            rect.Y -= rect.Height * .15f;
            rect.Width *= 1.7f;
            rect.Height *= 2.3f;
            path.AddEllipse(rect);
            path.CloseFigure();
            return path;
        }

        /// <summary>
        /// Creates the chevron for the overflow button
        /// </summary>
        /// <param name="overflowButtonSize"></param>
        /// <returns></returns>
        private static GraphicsPath CreateOverflowChevron(Size overflowButtonSize)
        {
            Rectangle r = new Rectangle(Point.Empty, overflowButtonSize);
            GraphicsPath path = new GraphicsPath();

            int segmentWidth = 3;
            int segmentHeight = 3;
            int segmentSeparation = 5;
            int chevronWidth = segmentWidth + segmentSeparation;
            int chevronHeight = segmentHeight * 2;
            int chevronLeft = (r.Width - chevronWidth) / 2;
            int chevronTop = (r.Height - chevronHeight) / 2;

            // Segment \
            path.AddLine(
                new Point(chevronLeft, chevronTop),
                new Point(chevronLeft + segmentWidth, chevronTop + segmentHeight));

            // Segment /
            path.AddLine(
                new Point(chevronLeft + segmentWidth, chevronTop + segmentHeight),
                new Point(chevronLeft, chevronTop + segmentHeight * 2));

            path.StartFigure();

            // Segment \
            path.AddLine(
                new Point(segmentSeparation + chevronLeft, chevronTop),
                new Point(segmentSeparation + chevronLeft + segmentWidth, chevronTop + segmentHeight));

            // Segment /
            path.AddLine(
                new Point(segmentSeparation + chevronLeft + segmentWidth, chevronTop + segmentHeight),
                new Point(segmentSeparation + chevronLeft, chevronTop + segmentHeight * 2));


            return path;
        }

        #endregion

        #region Fields

        private WindowsVistaColorTable _colorTable;
        private bool _glossyEffect;
        private bool _bgglow;
        private int _toolstripRadius;
        private int _buttonRadius;

        #endregion

        #region Ctor

        public WindowsVistaRenderer()
        {
            ColorTable = new WindowsVistaColorTable();

            GlossyEffect = true;
            BackgroundGlow = true;
            ToolStripRadius = 2;
            ButtonRadius = 2;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the buttons rectangle radius
        /// </summary>
        public int ButtonRadius
        {
            get { return _buttonRadius; }
            set { _buttonRadius = value; }
        }

        /// <summary>
        /// Gets or sets the radius of the rectangle of the hole ToolStrip
        /// </summary>
        public int ToolStripRadius
        {
            get { return _toolstripRadius; }
            set { _toolstripRadius = value; }
        }

        /// <summary>
        /// Gets ors sets if background glow should be rendered
        /// </summary>
        public bool BackgroundGlow
        {
            get { return _bgglow; }
            set { _bgglow = value; }
        }

        /// <summary>
        /// Gets or sets if glossy effect should be rendered
        /// </summary>
        public bool GlossyEffect
        {
            get { return _glossyEffect; }
            set { _glossyEffect = value; }
        }

        /// <summary>
        /// Gets or sets the color table of the renderer
        /// </summary>
        public WindowsVistaColorTable ColorTable
        {
            get { return _colorTable; }
            set { _colorTable = value; }
        }


        #endregion

        #region Methods

        /// <summary>
        /// Initializes properties for ToolStripMenuItem objects
        /// </summary>
        /// <param name="item"></param>
        protected virtual void InitializeToolStripMenuItem(ToolStripMenuItem item)
        {
            item.AutoSize = false;
            item.Height = 26;
            item.TextAlign = ContentAlignment.MiddleLeft;

            foreach (ToolStripItem subitem in item.DropDownItems)
            {
                if (subitem is ToolStripMenuItem)
                {
                    InitializeToolStripMenuItem(subitem as ToolStripMenuItem);
                }
            }
        }

        /// <summary>
        /// Gets a rounded rectangle representing the hole area of the toolstrip
        /// </summary>
        /// <param name="toolStrip"></param>
        /// <returns></returns>
        private GraphicsPath GetToolStripRectangle(ToolStrip toolStrip)
        {
            return GraphicsTools.CreateRoundRectangle(
                new Rectangle(0, 0, toolStrip.Width - 1, toolStrip.Height - 1), ToolStripRadius);
        }

        /// <summary>
        /// Draws the glossy effect on the toolbar
        /// </summary>
        /// <param name="g"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        private void DrawGlossyEffect(Graphics g, ToolStrip t)
        {
            DrawGlossyEffect(g, t, 0);
        }

        /// <summary>
        /// Draws the glossy effect on the toolbar
        /// </summary>
        /// <param name="g"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        private void DrawGlossyEffect(Graphics g, ToolStrip t, int offset)
        {
            Rectangle glossyRect = new Rectangle(0, offset,
                    t.Width - 1,
                    (t.Height - 1) / 2);

            using (LinearGradientBrush b = new LinearGradientBrush(
                glossyRect.Location, new PointF(0, glossyRect.Bottom),
                ColorTable.GlossyEffectNorth,
                ColorTable.GlossyEffectSouth))
            {
                using (GraphicsPath border =
                    GraphicsTools.CreateTopRoundRectangle(glossyRect, ToolStripRadius))
                {
                    g.FillPath(b, border);
                }
            }
        }

        /// <summary>
        /// Renders the background of a button
        /// </summary>
        /// <param name="e"></param>
        private void DrawVistaButtonBackground(ToolStripItemRenderEventArgs e)
        {
            bool chk = false;

            if (e.Item is ToolStripButton)
            {
                chk = (e.Item as ToolStripButton).Checked;
            }

            DrawVistaButtonBackground(e.Graphics,
                new Rectangle(Point.Empty, e.Item.Size),
                e.Item.Selected,
                e.Item.Pressed,
                chk);
        }

        /// <summary>
        /// Renders the background of a button on the specified rectangle using the specified device
        /// </summary>
        /// <param name="e"></param>
        private void DrawVistaButtonBackground(Graphics g, Rectangle r, bool selected, bool pressed, bool checkd)
        {
            g.SmoothingMode = SmoothingMode.AntiAlias;

            Rectangle outerBorder = new Rectangle(r.Left, r.Top, r.Width - 1, r.Height - 1);
            Rectangle border = outerBorder; border.Inflate(-1, -1);
            Rectangle innerBorder = border; innerBorder.Inflate(-1, -1);
            Rectangle glossy = outerBorder; glossy.Height /= 2;
            Rectangle fill = innerBorder; fill.Height /= 2;
            Rectangle glow = Rectangle.FromLTRB(outerBorder.Left,
                outerBorder.Top + Convert.ToInt32(Convert.ToSingle(outerBorder.Height) * .5f),
                outerBorder.Right, outerBorder.Bottom);

            if (selected || pressed || checkd)
            {
                #region Layers

                //Outer border
                using (GraphicsPath path =
                    GraphicsTools.CreateRoundRectangle(outerBorder, ButtonRadius))
                {
                    using (Pen p = new Pen(ColorTable.ButtonOuterBorder))
                    {
                        g.DrawPath(p, path);
                    }
                }

                //Checked fill
                if (checkd)
                {
                    using (GraphicsPath path = GraphicsTools.CreateRoundRectangle(innerBorder, 2))
                    {
                        using (Brush b = new SolidBrush(selected ? ColorTable.CheckedButtonFillHot : ColorTable.CheckedButtonFill))
                        {
                            g.FillPath(b, path);
                        }
                    }
                }

                //Glossy effefct
                using (GraphicsPath path = GraphicsTools.CreateTopRoundRectangle(glossy, ButtonRadius))
                {
                    using (Brush b = new LinearGradientBrush(
                        new Point(0, glossy.Top),
                        new Point(0, glossy.Bottom),
                        ColorTable.GlossyEffectNorth,
                        ColorTable.GlossyEffectSouth))
                    {
                        g.FillPath(b, path);
                    }
                }

                //Border
                using (GraphicsPath path =
                    GraphicsTools.CreateRoundRectangle(border, ButtonRadius))
                {
                    using (Pen p = new Pen(ColorTable.ButtonBorder))
                    {
                        g.DrawPath(p, path);
                    }
                }

                Color fillNorth = pressed ? ColorTable.ButtonFillNorthPressed : ColorTable.ButtonFillNorth;
                Color fillSouth = pressed ? ColorTable.ButtonFillSouthPressed : ColorTable.ButtonFillSouth;

                //Fill
                using (GraphicsPath path = GraphicsTools.CreateTopRoundRectangle(fill, ButtonRadius))
                {
                    using (Brush b = new LinearGradientBrush(
                        new Point(0, fill.Top),
                        new Point(0, fill.Bottom),
                        fillNorth, fillSouth))
                    {
                        g.FillPath(b, path);
                    }
                }

                Color innerBorderColor = pressed || checkd ? ColorTable.ButtonInnerBorderPressed : ColorTable.ButtonInnerBorder;

                //Inner border
                using (GraphicsPath path =
                    GraphicsTools.CreateRoundRectangle(innerBorder, ButtonRadius))
                {
                    using (Pen p = new Pen(innerBorderColor))
                    {
                        g.DrawPath(p, path);
                    }
                }

                //Glow
                using (GraphicsPath clip = GraphicsTools.CreateRoundRectangle(glow, 2))
                {
                    g.SetClip(clip, CombineMode.Intersect);

                    Color glowColor = ColorTable.Glow;

                    if (checkd)
                    {
                        if (selected)
                        {
                            glowColor = ColorTable.CheckedGlowHot;
                        }
                        else
                        {
                            glowColor = ColorTable.CheckedGlow;
                        }
                    }

                    using (GraphicsPath brad = CreateBottomRadialPath(glow))
                    {
                        using (PathGradientBrush pgr = new PathGradientBrush(brad))
                        {
                            unchecked
                            {
                                int opacity = 255;
                                RectangleF bounds = brad.GetBounds();
                                pgr.CenterPoint = new PointF((bounds.Left + bounds.Right) / 2f, (bounds.Top + bounds.Bottom) / 2f);
                                pgr.CenterColor = Color.FromArgb(opacity, glowColor);
                                pgr.SurroundColors = new Color[] { Color.FromArgb(0, glowColor) };
                            }
                            g.FillPath(pgr, brad);
                        }
                    }
                    g.ResetClip();
                }

                


                #endregion
            }
        }

        /// <summary>
        /// Draws the background of a menu, vista style
        /// </summary>
        /// <param name="e"></param>
        private void DrawVistaMenuBackground(ToolStripItemRenderEventArgs e)
        {
            DrawVistaMenuBackground(e.Graphics,
                new Rectangle(Point.Empty, e.Item.Size),
                e.Item.Selected);
        }

        /// <summary>
        /// Draws the background of a menu, vista style
        /// </summary>
        /// <param name="g"></param>
        /// <param name="r"></param>
        /// <param name="highlighted"></param>
        private void DrawVistaMenuBackground(Graphics g, Rectangle r, bool highlighted)
        {
            //g.Clear(ColorTable.MenuBackground);

            int margin = 2;

            #region IconSeparator

            int left = 22;

            using (Pen p = new Pen(ColorTable.MenuDark))
            {
                g.DrawLine(p,
                            new Point(r.Left + left, r.Top),
                            new Point(r.Left + left, r.Height - margin));
            }


            using (Pen p = new Pen(ColorTable.MenuLight))
            {
                g.DrawLine(p,
                            new Point(r.Left + left + 1, r.Top),
                            new Point(r.Left + left + 1, r.Height - margin));
            }

            #endregion

            if (highlighted)
            {
                #region Draw Rectangle

                using (GraphicsPath path = GraphicsTools.CreateRoundRectangle(
                    new Rectangle(r.X + margin, r.Y + margin, r.Width - margin * 2, r.Height - margin * 2), 3))
                {

                    using (Brush b = new LinearGradientBrush(
                        new Point(0, 2), new Point(0, r.Height - 2),
                        ColorTable.MenuHighlightNorth,
                        ColorTable.MenuHighlightSouth))
                    {
                        g.FillPath(b, path);
                    }

                    using (Pen p = new Pen(ColorTable.MenuHighlight))
                    {
                        g.DrawPath(p, path);
                    }
                }

                #endregion
            }

        }

        /// <summary>
        /// Draws the border of the vista menu window
        /// </summary>
        /// <param name="g"></param>
        /// <param name="r"></param>
        private void DrawVistaMenuBorder(Graphics g, Rectangle r)
        {
            using (Pen p = new Pen(ColorTable.BackgroundBorder))
            {
                g.DrawRectangle(p,
                    new Rectangle(r.Left, r.Top, r.Width - 1, r.Height - 1));
            }
        }

        #endregion

        protected override void Initialize(ToolStrip toolStrip)
        {
            base.Initialize(toolStrip);

            toolStrip.AutoSize = false;
            toolStrip.Height = 35;
            toolStrip.ForeColor = ColorTable.Text;
            toolStrip.GripStyle = ToolStripGripStyle.Hidden;
        }

        protected override void InitializeItem(ToolStripItem item)
        {
            base.InitializeItem(item);

            //Don't Affect ForeColor of TextBoxes and ComboBoxes
            if ( !((item is ToolStripTextBox) || (item is ToolStripComboBox)) )
            {
                item.ForeColor = ColorTable.Text;
            }
            
            item.Padding = new Padding(5);

            if (item is ToolStripSplitButton)
            {
                ToolStripSplitButton btn = item as ToolStripSplitButton;
                btn.DropDownButtonWidth = 18;

                foreach (ToolStripItem subitem in btn.DropDownItems)
                {
                    if (subitem is ToolStripMenuItem)
                    {
                        InitializeToolStripMenuItem(subitem as ToolStripMenuItem);
                    }
                }
            }
            
            if (item is ToolStripDropDownButton)
            {
                ToolStripDropDownButton btn = item as ToolStripDropDownButton;
                btn.ShowDropDownArrow = false;

                foreach (ToolStripItem subitem in btn.DropDownItems)
                {
                    if (subitem is ToolStripMenuItem)
                    {
                        InitializeToolStripMenuItem(subitem as ToolStripMenuItem);
                    }
                }
            }
        }

        protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
        {
            
            if (e.ToolStrip is ToolStripDropDownMenu)
            {
                #region Draw Rectangled Border

                DrawVistaMenuBorder(e.Graphics,
                    new Rectangle(Point.Empty, e.ToolStrip.Size));

                #endregion
            }
            else
            {
                #region Draw Rounded Border
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

                using (GraphicsPath path = GetToolStripRectangle(e.ToolStrip))
                {
                    using (Pen p = new Pen(ColorTable.BackgroundBorder))
                    {
                        e.Graphics.DrawPath(p, path);
                    }
                } 
                #endregion
            }
            
            
        }

        protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e)
        {
            if (e.ToolStrip is ToolStripDropDownMenu)
            {
                return;
            }

            #region Background

            using (LinearGradientBrush b = new LinearGradientBrush(
                Point.Empty, new PointF(0,e.ToolStrip.Height), 
                ColorTable.BackgroundNorth,
                ColorTable.BackgroundSouth))
            {
                using (GraphicsPath border = GetToolStripRectangle(e.ToolStrip))
                {
                    e.Graphics.FillPath(b, border);
                }
            }

            #endregion

            if (GlossyEffect)
            {
                #region Glossy Effect

                DrawGlossyEffect(e.Graphics, e.ToolStrip, 1);

                #endregion 
            }

            if (BackgroundGlow)
            {
                #region BackroundGlow

                int glowSize = Convert.ToInt32(Convert.ToSingle(e.ToolStrip.Height) * 0.15f);
                Rectangle glow = new Rectangle(0,
                    e.ToolStrip.Height - glowSize - 1,
                    e.ToolStrip.Width - 1,
                    glowSize);
                
                using (LinearGradientBrush b = new LinearGradientBrush(
                    new Point(0, glow.Top -1 ), new PointF(0, glow.Bottom),
                    Color.FromArgb(0, ColorTable.BackgroundGlow),
                    ColorTable.BackgroundGlow))
                {
                    using (GraphicsPath border =
                        GraphicsTools.CreateBottomRoundRectangle(glow, ToolStripRadius))
                    {
                        e.Graphics.FillPath(b, border);
                    }
                }


                #endregion
            }
            
        }

        protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
        {

            if (e.Item is ToolStripButton)
            {
                e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            }

            if (e.Item is ToolStripMenuItem)
            {
                Rectangle r = new Rectangle(e.TextRectangle.Location, new Size(e.TextRectangle.Width, 24));
                e.TextRectangle = r;
                e.TextColor = ColorTable.MenuText;
            }

            base.OnRenderItemText(e);
        }

        protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e)
        {
            DrawVistaButtonBackground(e);
        }

        protected override void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e)
        {
            DrawVistaButtonBackground(e);

            ToolStripDropDownButton item = e.Item as ToolStripDropDownButton; if (item == null) return;

            Rectangle arrowBounds = new Rectangle(item.Width - 18, 0, 18, item.Height);
            
            DrawArrow(new ToolStripArrowRenderEventArgs(
                e.Graphics, e.Item,
                arrowBounds,
                ColorTable.DropDownArrow, ArrowDirection.Down));
        }

        protected override void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e)
        {
            DrawVistaButtonBackground(e);
            
            ToolStripSplitButton item = e.Item as ToolStripSplitButton; if(item==null) return;

            Rectangle arrowBounds = item.DropDownButtonBounds;
            Rectangle buttonBounds = new Rectangle(item.ButtonBounds.Location, new Size(item.ButtonBounds.Width + 2, item.ButtonBounds.Height));
            Rectangle dropDownBounds = item.DropDownButtonBounds;

            DrawVistaButtonBackground(e.Graphics, buttonBounds, item.ButtonSelected,
                item.ButtonPressed, false);

            DrawVistaButtonBackground(e.Graphics, dropDownBounds, item.DropDownButtonSelected,
                item.DropDownButtonPressed, false);

            DrawArrow(new ToolStripArrowRenderEventArgs(
                e.Graphics, e.Item,
                arrowBounds,
                ColorTable.DropDownArrow, ArrowDirection.Down));
        }

        protected override void OnRenderItemImage(ToolStripItemImageRenderEventArgs e)
        {
            if (!e.Item.Enabled)
            {
                base.OnRenderItemImage(e);
            }
            else
            {
                if (e.Image != null)
                {
                    e.Graphics.DrawImage(e.Image, e.ImageRectangle);
                }
            }
            
        }

        protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)
        {

            DrawVistaMenuBackground(e.Graphics,
                new Rectangle(Point.Empty, e.Item.Size),
                e.Item.Selected);

        }

        protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
        {

            

            if (e.Item.IsOnDropDown)
            {
                int left = 20;
                int right = e.Item.Width - 3;
                int top = e.Item.Height / 2; top--;

                //e.Graphics.Clear(ColorTable.MenuBackground);

                using (Pen p = new Pen(ColorTable.MenuDark))
                {
                    e.Graphics.DrawLine(p,
                        new Point(left, top),
                        new Point(right, top));
                }

                using (Pen p = new Pen(ColorTable.MenuLight))
                {
                    e.Graphics.DrawLine(p,
                        new Point(left, top + 1),
                        new Point(right, top + 1));
                }
            }
            else
            {
                int top = 3;
                int left = e.Item.Width / 2; left--;
                int height = e.Item.Height - top * 2;
                RectangleF separator = new RectangleF(left, top, 0.5f, height);

                using (Brush b = new LinearGradientBrush(
                    separator.Location,
                    new Point(Convert.ToInt32(separator.Left), Convert.ToInt32(separator.Bottom)),
                    ColorTable.SeparatorNorth, ColorTable.SeparatorSouth))
                {
                    e.Graphics.FillRectangle(b, separator);
                }
            }
        }

        protected override void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e)
        {
            DrawVistaButtonBackground(e);

            //Chevron is obtained from the character: � (Alt+0187)
            using (Brush b = new SolidBrush(e.Item.ForeColor))
            {
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Center;
                sf.LineAlignment = StringAlignment.Center;

                Font f = new Font(e.Item.Font.FontFamily, 15);

                e.Graphics.DrawString("�", f, b, new RectangleF(Point.Empty, e.Item.Size), sf); 
            }

        }

        protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
        {
            if (e.Item is ToolStripMenuItem && e.Item.Selected)
            {
                e.ArrowColor = ColorTable.MenuText;
            }

            base.OnRenderArrow(e);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Product Manager
United States United States
- I've been programming Windows and Web apps since 1997.
- My greatest concern nowadays is product, user interface, and usability.
- TypeScript / React expert

@geeksplainer

Comments and Discussions