A Vertical Progress Bar

A development on a progress bar control

This is a pure control using an override of the Draw function to achieve complete control of the presentation. There are three versions horizontal, vertical and angled. The angled is quite complicated to draw, the horizontal very simple so I present the vertical bar as an in between complexity.

A multicolor, vertical progress bar LED

Progress Control

A similarity can be seen with the LEDBar control. There are three colours and thresholds. The range is scaled, normally 0 to 100. The progress bar can be solid or segmented. Thus simulating a series of LED. One of the fancy things is the shading, the default is rather subdued but just as a sombre gre shading is possible so is a patriotic red, white and blue. If the drawing code is studied you will see that instead of shading an image could be progressively drawn.

The control is mainly methods, in fact the only part a real interest is the OnPaint override. The use of a LinearGradientBrush is where the shading magic is carried out. Change the brush to plain boring solid, ripple effect, this is where the suppressed styling flair come into play.

    [Description("Vertical Colour Progress Bar")]
    [ToolboxBitmap(typeof(ProgressBar))]
    [Designer(typeof(VerticalProgressBarDesigner))]
    public partial class VerticalProgressBar : System.Windows.Forms.Control
        {
            public enum FillStyles
            {
                Solid,
                Dashed  //  Same as progress bar
            }

            public enum BorderStyles
            {
                None,
                Outline //  Same as progress bar
            }
            //
            // set default values
            //
            private int BarValue = 0;
            private int BarMinimum = 0;
            private int BarMaximum = 10;
            private int BarStep = 1;
            private int StripMiddleTrip = 0;
            private int StripEndTrip = 0;
            private int NumberOfStrips = 1;
            private FillStyles FillStyle = FillStyles.Solid;
            private BorderStyles BorderStyle = BorderStyles.Outline;

            private Color BarColour = Color.FromArgb(223, 231, 238);
            private Color sepColour = ControlPaint.LightLight(Color.FromArgb(223, 231, 238));
            private Color StripColour = Color.FromArgb(223, 231, 238);
            private Color Strip0Colour = Color.FromArgb(50, 77, 123);
            private Color Strip1Colour = Color.FromArgb(121, 123, 50);
            private Color Strip2Colour = Color.FromArgb(123, 50, 50);
            private Color BorderColour = Color.Black;

            private Blend blender = new Blend(11);

            public VerticalProgressBar()
            {
                InitializeComponent();
                SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw |
                    ControlStyles.DoubleBuffer, true);
                //  Set up Blend profile
                blender.Factors = new float[] { 0.0F, 0.1F, 0.3F, 0.6F, 0.8F, 1.0F, 0.8F, 0.6F, 0.3F, 0.1F, 0.0F };
                blender.Positions = new float[] { 0.0F, 0.1F, 0.2F, 0.3F, 0.4F, 0.5F, 0.6F, 0.7F, 0.8F, 0.9F, 1.0F };
                Size = new Size(15, 120);
            }

            [Description("VerticalProgressBar colour")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public Color VerticalProgressBarColour
            {
                get { return BarColour; }
                set
                {
                    BarColour = value;
                    sepColour = ControlPaint.LightLight(BarColour);
                    this.Invalidate();
                }
            }

            [Description("VerticalProgressBar Seperator Colour")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public Color VerticalProgressSeperatorColour
            {
                get { return sepColour; }
                set
                {
                    sepColour = value;
                    this.Invalidate();
                }
            }

            [Description("VerticalProgressStrip colour")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public Color VerticalProgressBarStrip
            {
                get { return Strip0Colour; }
                set
                {
                    Strip0Colour = value;
                    this.Invalidate();
                }
            }

            [Description("VerticalProgressMiddleStrip colour")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public Color VerticalProgressBarStripMiddle
            {
                get { return Strip1Colour; }
                set
                {
                    Strip1Colour = value;
                    this.Invalidate();
                }
            }

            [Description("VerticalProgressEndStrip colour")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public Color VerticalProgressBarStripEnd
            {
                get { return Strip2Colour; }
                set
                {
                    Strip2Colour = value;
                    this.Invalidate();
                }
            }

            [Description("VerticalProgressCentreStrip colour")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public Color VerticalProgressBarStripCentre
            {
                get { return StripColour; }
                set
                {
                    StripColour = value;
                    this.Invalidate();
                }
            }

            [Description("VerticalProgressBar fill style")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public FillStyles VerticalProgressBarFillStyle
            {
                get { return FillStyle; }
                set
                {
                    FillStyle = value;
                    this.Invalidate();
                }
            }

            [Description("VerticalProgressBar border style")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public BorderStyles VerticalProgressBarBorderStyle
            {
                get { return BorderStyle; }
                set
                {
                    BorderStyle = value;
                    this.Invalidate();
                }
            }

            [Description("The current value for the VerticalColourProgressBar, " +
                 "in the range specified by the Minimum and Maximum properties.")]
            [Category("VerticalProgressBar")]
            // The rest of the Properties windows must be updated when this property is changed.
            [RefreshProperties(RefreshProperties.All)]
            public int VerticalProgressBarValue
            {
                get { return BarValue; }
                set
                {
                    if (value < BarMinimum) BarValue = BarMinimum;
                    if (value > BarMaximum) BarValue = BarMaximum;
                    BarValue = value;
                    this.Invalidate();
                }
            }

            [Description("The trip position of the middle strip ")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public int VerticalProgressBarMiddleTripValue
            {
                get { return StripMiddleTrip; }
                set
                {
                    if (value < BarMinimum) StripMiddleTrip = BarMinimum + 1;
                    if (value > BarMaximum) StripMiddleTrip = BarMaximum - 1;
                    StripMiddleTrip = value;
                    this.Invalidate();
                }
            }

            [Description("The trip position of the end strip ")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public int VerticalProgressBarEndTripValue
            {
                get { return StripEndTrip; }
                set
                {
                    if (value < BarMinimum) StripEndTrip = BarMinimum + 1;
                    if (value > BarMaximum) StripEndTrip = BarMaximum - 1;
                    StripEndTrip = value;
                    this.Invalidate();
                }
            }

            [Description("The number of strips")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public int VerticalProgressBarNumberOfStrips
            {
                get { return NumberOfStrips; }
                set
                {
                    if (value > 3) value = 3;
                    else if (value < 1) value = 1;
                    NumberOfStrips = value;
                }
            }

            [Description("The lower bound of the range this ColourProgressBar is working with.")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public int VerticalProgressBarMinimum
            {
                get { return BarMinimum; }
                set
                {
                    BarMinimum = value;
                    if (BarMinimum > BarMaximum)
                        BarMaximum = BarMinimum;
                    if (BarMinimum > BarValue)
                        BarValue = BarMinimum;
                    this.Invalidate();
                }
            }

            [Description("The uppper bound of the range this ColourProgressbar is working with.")]
            [Category("VerticalProgressBar")]
            [RefreshProperties(RefreshProperties.All)]
            public int VerticalProgressBarMaximum
            {
                get { return BarMaximum; }
                set
                {
                    BarMaximum = value;

                    if (BarMaximum < BarValue)
                        BarValue = BarMaximum;
                    if (BarMaximum < BarMinimum)
                        BarMinimum = BarMaximum;

                    this.Invalidate();
                }
            }

            [Description("The amount to jump the current value of the control by when the Step() method is called.")]
            [Category("VerticalProgressBar")]
            public int VerticalProgressBarStep
            {
                get { return BarStep; }
                set
                {
                    BarStep = value;
                    this.Invalidate();
                }
            }

            [Description("The border color of ColourProgressBar")]
            [Category("VerticalProgressBar")]
            public Color VerticalProgressBarBorderColour
            {
                get { return BorderColour; }
                set
                {
                    BorderColour = value;
                    this.Invalidate();
                }
            }

            //
            // Call the PerformStep() method to increase the value displayed by the amount set in the Step property
            //
            public void VerticalProgressBarPerformStep()
            {
                if (BarValue < BarMaximum)
                    BarValue += BarStep;
                else
                    BarValue = BarMaximum;

                this.Invalidate();
            }

            //
            // Call the PerformStepBack() method to decrease the value displayed by the amount set in the Step property
            //
            public void VerticalProgressBarPerformStepBack()
            {
                if (BarValue > BarMinimum)
                    BarValue -= BarStep;
                else
                    BarValue = BarMinimum;

                this.Invalidate();
            }

            //
            // Call the Increment() method to increase the value displayed by an integer you specify
            // 
            public void VerticalProgressBarIncrement(int value)
            {
                if (BarValue < BarMaximum)
                    BarValue += value;
                else
                    BarValue = BarMaximum;

                this.Invalidate();
            }

            //
            // Call the Decrement() method to decrease the value displayed by an integer you specify
            // 
            public void VerticalProgressBarDecrement(int value)
            {
                if (BarValue > BarMinimum)
                    BarValue -= value;
                else
                    BarValue = BarMinimum;

                this.Invalidate();
            }

            protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
            {
                //
                //  Fill background
                //
                SolidBrush bgBrush = new SolidBrush(BarColour);
                e.Graphics.FillRectangle(bgBrush, this.ClientRectangle);
                bgBrush.Dispose();
                // 
                //  Check for value
                //
                if (BarMaximum == BarMinimum || BarValue == 0)
                {
                    // Draw border only and exit;
                    drawBorder(e.Graphics);
                    return;
                }
                //
                //  The following is the width of the bar. This will vary with each value.
                //  This is further complicated by the use of up to three bars of different colours
                //
                if (NumberOfStrips == 1)
                {
                    int fillHeight = (this.Height * BarValue) / (BarMaximum - BarMinimum);
                    //
                    // GDI+ doesn't like rectangles 0px wide or high
                    //
                    if (fillHeight == 0)
                    {
                        // Draw border only and exit;
                        drawBorder(e.Graphics);
                        return;
                    }
                    //
                    // Rectangles for left and right half of bar
                    //
                    Rectangle rect = new Rectangle(0, (this.Height - fillHeight), this.Width, fillHeight);
                    //
                    // The gradient brush
                    //
                    LinearGradientBrush brush;
                    //
                    // Paint
                    //
                    Single angle = 0.0f;
                    brush = new LinearGradientBrush(new Rectangle(new Point(0, (this.Height - fillHeight)),
                        new Size(this.Width, fillHeight)), Strip0Colour, StripColour, angle);
                    brush.Blend = blender;
                    e.Graphics.FillRectangle(brush, rect);
                    brush.Dispose();
                }
                else if (NumberOfStrips == 2)
                {
                    //  This uses up to StripEndTrip as Strip0Colour and the remainder as Strip2Colour
                    int fillHeight0 = 0;
                    int fillHeight1 = 0;
                    if (BarValue > StripEndTrip)
                    {
                        fillHeight0 = (this.Height * StripEndTrip) / (BarMaximum - BarMinimum);
                        fillHeight1 = (this.Height * (BarValue - StripEndTrip)) / (BarMaximum - BarMinimum);
                    }
                    else
                    {
                        //  BarValue <= trip value
                        fillHeight0 = (this.Height * BarValue) / (BarMaximum - BarMinimum);
                    }
                    //
                    // GDI+ doesn't like rectangles 0px wide or high
                    //
                    if (fillHeight0 == 0)
                    {
                        // Draw border only and exit;
                        drawBorder(e.Graphics);
                        return;
                    }
                    //  Fill in the first part of the strip up to the trip point if required
                    //
                    // Rectangles for left and right half of bar
                    //
                    Rectangle rect0 = new Rectangle(0, (this.Height - fillHeight0), this.Width, fillHeight0);
                    //
                    // The gradient brush
                    //
                    LinearGradientBrush brush;
                    //
                    // Paint
                    //
                    Single angle = 0.0f;
                    brush = new LinearGradientBrush(new Rectangle(new Point(0, (this.Height - fillHeight0)),
                        new Size(this.Width, fillHeight0)), Strip0Colour, StripColour, angle);
                    brush.Blend = blender;
                    e.Graphics.FillRectangle(brush, rect0);
                    brush.Dispose();
                    //  Now if the BarValue>StripTripEnd we paint the second part ie fillWidth1>0
                    if (fillHeight1 > 0)
                    {
                        //
                        // Rectangles for upper and lower half of bar that start from fillWidth0
                        //
                        Rectangle rect1 = new Rectangle(0, (this.Height - fillHeight0 - fillHeight1), this.Width, fillHeight1);
                        //
                        // Paint
                        //
                        brush = new LinearGradientBrush(new Rectangle(new Point(0, (this.Height - fillHeight0 - fillHeight1)),
                            new Size(this.Width, fillHeight1)), Strip1Colour, StripColour, angle);
                        brush.Blend = blender;
                        e.Graphics.FillRectangle(brush, rect1);
                        brush.Dispose();
                    }
                }
                else if (NumberOfStrips == 3)
                {
                    //  This uses up to StripEndTrip as Strip0Colour and the remainder as Strip2Colour
                    int fillHeight0 = 0;
                    int fillHeight1 = 0;
                    int fillHeight2 = 0;
                    if (BarValue > StripEndTrip)
                    {
                        //  Bar goes near to the end
                        fillHeight0 = (this.Height * StripMiddleTrip) / (BarMaximum - BarMinimum);
                        fillHeight1 = (this.Height * (StripEndTrip - StripMiddleTrip)) / (BarMaximum - BarMinimum);
                        fillHeight2 = (this.Height * (BarValue - StripEndTrip)) / (BarMaximum - BarMinimum);
                    }
                    else if (BarValue > StripMiddleTrip)
                    {
                        //  Bar is the middle portion
                        fillHeight0 = (this.Height * StripMiddleTrip) / (BarMaximum - BarMinimum);
                        fillHeight1 = (this.Height * (BarValue - StripMiddleTrip)) / (BarMaximum - BarMinimum);
                    }
                    else
                    {
                        //  BarValue <= middle trip value
                        fillHeight0 = (this.Height * BarValue) / (BarMaximum - BarMinimum);
                    }
                    //
                    // GDI+ doesn't like rectangles 0px wide or high
                    //
                    if (fillHeight0 == 0)
                    {
                        // Draw border only and exit;
                        drawBorder(e.Graphics);
                        return;
                    }
                    //  Fill in the first part of the strip up to the trip point if required
                    //
                    // Rectangles for left and right half of bar
                    //
                    Rectangle rect0 = new Rectangle(0, (this.Height - fillHeight0), this.Width, fillHeight0);
                    //
                    // The gradient brush
                    //
                    LinearGradientBrush brush;
                    //
                    // Paint
                    //
                    Single angle = 0.0f;
                    brush = new LinearGradientBrush(new Rectangle(new Point(0, (this.Height - fillHeight0)),
                        new Size(this.Width, fillHeight0)), Strip0Colour, StripColour, angle);
                    brush.Blend = blender;
                    e.Graphics.FillRectangle(brush, rect0);
                    brush.Dispose();
                    if (fillHeight1 > 0)
                    {
                        //
                        // Rectangles for upper and lower half of bar that start from fillWidth0
                        //
                        Rectangle rect1 = new Rectangle(0, (this.Height - fillHeight0 - fillHeight1), this.Width, fillHeight1);
                        //
                        // Paint
                        //
                        brush = new LinearGradientBrush(new Rectangle(new Point(0, (this.Height - fillHeight0 - fillHeight1)),
                            new Size(this.Width, fillHeight1)), Strip1Colour, StripColour, angle);
                        brush.Blend = blender;
                        e.Graphics.FillRectangle(brush, rect1);
                        brush.Dispose();
                    }
                    if (fillHeight2 > 0)
                    {
                        //
                        // Rectangles for upper and lower half of bar that start from fillWidth0
                        //
                        Rectangle rect2 = new Rectangle(0, (this.Height - fillHeight0 - fillHeight1 - fillHeight2), this.Width, fillHeight2);
                        //
                        // Paint
                        //
                        brush = new LinearGradientBrush(new Rectangle(new Point(0, (this.Height - fillHeight0 - fillHeight1 - fillHeight2)),
                            new Size(this.Width, fillHeight2)), Strip2Colour, StripColour, angle);
                        brush.Blend = blender;
                        e.Graphics.FillRectangle(brush, rect2);
                        brush.Dispose();
                    }
                }
                //
                // Calculate separator's setting
                //
                int fHeight = (this.Height * BarValue) / (BarMaximum - BarMinimum);
                int sepHeight = (int)(this.Width * .67);
                int sepCount = (int)(fHeight / sepHeight);
                //
                // Paint separators
                //
                switch (FillStyle)
                {
                    case FillStyles.Dashed:
                        // Draw each separator line
                        for (int i = 1; i <= sepCount; i++)
                        {
                            e.Graphics.DrawLine(new Pen(sepColour, 1),
                                0, (this.Height - sepHeight * i), this.Width, (this.Height - sepHeight * i));
                        }
                        break;
                    case FillStyles.Solid:
                        // Draw nothing
                        break;
                    default:
                        break;
                }
                //
                // Draw border and exit
                //
                drawBorder(e.Graphics);
            }
            //
            // Draw border
            //
            protected void drawBorder(Graphics g)
            {
                if (BorderStyle == BorderStyles.Outline)
                {
                    Rectangle borderRect = new Rectangle(0, 0,
                        ClientRectangle.Width - 1, ClientRectangle.Height - 1);
                    g.DrawRectangle(new Pen(BorderColour, 1), borderRect);
                }
            }

            public VerticalProgressBar(IContainer container)
            {
                container.Add(this);

                InitializeComponent();
                SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw |
                    ControlStyles.DoubleBuffer, true);
                //  Set up Blend profile
                blender.Factors = new float[] { 0.0F, 0.1F, 0.3F, 0.6F, 0.8F, 1.0F, 0.8F, 0.6F, 0.3F, 0.1F, 0.0F };
                blender.Positions = new float[] { 0.0F, 0.1F, 0.2F, 0.3F, 0.4F, 0.5F, 0.6F, 0.7F, 0.8F, 0.9F, 1.0F };
                Size = new Size(15, 120);
            }

The designer can be copied or the equivalent generated by hand by changing the properties of the designer form. The properties must coincide with that required by the .cs code. Note: only InitializeComponent() and declarations are here, the rest of the designer does not change!

        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }

A simple internal designer class override is used to remove non-required properties.

        internal class VerticalProgressBarDesigner : ControlDesigner
        {
            public VerticalProgressBarDesigner()
            { }

            // clean up some unnecessary properties
            protected override void PostFilterProperties(IDictionary Properties)
            {
                Properties.Remove("AllowDrop");
                Properties.Remove("BackgroundImage");
                    Properties.Remove("ContextMenu");
                    Properties.Remove("FlatStyle");
                    Properties.Remove("Image");
                    Properties.Remove("ImageAlign");
                    Properties.Remove("ImageIndex");
                    Properties.Remove("ImageList");
                    Properties.Remove("Text");
                    Properties.Remove("TextAlign");
                    Properties.Remove("ForeColor");
                    Properties.Remove("BackColor");
                    Properties.Remove("BackgroundImageLayout");
                }
            }