Click here to Skip to main content
Click here to Skip to main content
Go to top

A simple custom progressbar

, 21 Dec 2009
Rate this:
Please Sign up or sign in to vote.
A simple custom progressbar.

Introduction

In this article, I paint a simple customized progress bar with four sections and different colors depending on the status and % filled. The class is written to fit into my company's software, but it's not that hard to modify it for your own needs. A progress bar is a good way to show data of any type, since you can see the most "important" info from far away without reading the text. Adding different colors further enhances that.

I use the superb extensions that Arun Reginald posted a while ago: http://www.codeproject.com/KB/GDI-plus/ExtendedGraphicsII.aspx, to get the rounded corners.

Background

Back in VB 6 days, there were controls for everything. No matter what you wanted to do, there were always some controls that would do it. My company used a simple progress bar with some text on it to display production data from saw mills. In our new version of the software, we made a more good looking version of that form. We liked it until the day we had to remote control over 56K Wink | ;) . The customers however did not. Too much non-important graphics they said. So we listened, and went back to the idea of progress bars. The one that comes with .NET is good in many ways, but sometimes, you just want more. My friend Google just didn't solve the problem this time, so I had to write my own.

Using the Code

Once imported and compiled, you can use this control like any other in Visual Studio, or you can add it like this:

HarrProgressBar pgb = new HarrProgressBar();
pgb.Padding = new Padding(5);
pgb.LeftText = "1";
pgb.MainText = "47x100x5400 - 20/100";
pgb.FillDegree = 20;
pgb.RightText = "1";
pgb.StatusText = "Raw";
pgb.StatusBarColor = 0;
pgb.Size = s;
pgb.Anchor = AnchorStyles.Left | AnchorStyles.Right;
this.flowLayoutPanel1.Controls.Add(pgb);

This control is based on System.Windows.Forms.Panel, and thus supports all the normal behaviors like Anchor and Docking. I added an internal padding to create a distance between the actual panel area and the drawn area, to be able to switch the background color to get a "selected" look and feel.

The different color codes are hardcoded to keep it simple. To hide sections, just set the sectionsize to 0.

Points of Interest

I haven't really used GDI+ that much, so this was quite fun to do for a change. I soon realized that rounded rectangles are much more better looking. This wasn't that easy though, and I am really happy I found Arun's extensions, which made my life much easier. The tricky part was to calculate the areas to draw:

private Rectangle GetLeftArea()
{
    return new Rectangle(
        Padding.Left,
        Padding.Top, 
        LeftBarSize,
        this.ClientRectangle.Height - Padding.Bottom - Padding.Top);
}

private Rectangle GetStatusArea()
{
    return new Rectangle(
        Padding.Left + LeftBarSize,
        Padding.Top,
        StatusBarSize,
        this.ClientRectangle.Height - Padding.Bottom - Padding.Top);
}

private Rectangle GetMainArea()
{
    return new Rectangle(
        Padding.Left + LeftBarSize + StatusBarSize,
        Padding.Top,
        Convert.ToInt32(((this.ClientRectangle.Width - 
          (Padding.Left + LeftBarSize + StatusBarSize + 
           RightBarSize + Padding.Right)) * FillDegree) / 100),
        this.ClientRectangle.Height - Padding.Bottom - Padding.Top);
}

private Rectangle GetMainAreaBackground()
{
    return new Rectangle(
           Padding.Left + LeftBarSize + StatusBarSize,
           Padding.Top,
           this.ClientRectangle.Width - (Padding.Left + 
             LeftBarSize + StatusBarSize + RightBarSize + Padding.Right),
           this.ClientRectangle.Height - Padding.Bottom - Padding.Top);
}

private Rectangle GetRightArea()
{
    return new Rectangle(
        this.ClientRectangle.Width - (RightBarSize + Padding.Right),
        Padding.Top, 
        RightBarSize,
        this.ClientRectangle.Height - Padding.Bottom - Padding.Top);
}

I had to have two different "Main" sections to be able to draw the text and the grey background. Once I have those rectangles, painting the sections is easy:

public void paintThis(Graphics _graphics)
{
    // Textformat
    StringFormat f = new StringFormat();
    f.Alignment = StringAlignment.Center;
    f.LineAlignment = StringAlignment.Center;

    // Misc
    _graphics = this.CreateGraphics();
    System.Drawing.Drawing2D.LinearGradientBrush _LeftAndRightBrush = 
      new LinearGradientBrush(GetMainArea(), Color.DimGray, 
      Color.Black, LinearGradientMode.Vertical);
    System.Drawing.Drawing2D.LinearGradientBrush _StatusBrush = 
      new LinearGradientBrush(GetMainArea(), StatusColor1, 
      StatusColor2, LinearGradientMode.Vertical);
    System.Drawing.Drawing2D.LinearGradientBrush _MainBrush = 
      new LinearGradientBrush(GetMainArea(), FirstColor, 
      SecondColor, LinearGradientMode.Vertical);
    
    // Draw left
    if (LeftBarSize > 0)
    {
        _graphics.FillRoundedRectangle(_LeftAndRightBrush, 
          this.GetLeftArea(), this.RoundedCornerAngle, 
          RectangleEdgeFilter.TopLeft | RectangleEdgeFilter.BottomLeft);
        _graphics.DrawString(this.LeftText, this.Font, 
                  Brushes.White, this.GetLeftArea(), f);
    }
    
    // Draw status
    if (StatusBarSize > 0)
    {
        _graphics.FillRoundedRectangle(_StatusBrush, this.GetStatusArea(), 
             this.RoundedCornerAngle, RectangleEdgeFilter.None);
        _graphics.DrawString(this.StatusText, this.Font, 
             Brushes.White, this.GetStatusArea(), f);
    }

    // Draw main background
    _graphics.FillRoundedRectangle(Brushes.DimGray, 
        GetMainAreaBackground(), this.RoundedCornerAngle, 
        RectangleEdgeFilter.None);

    // Draw main
    _graphics.FillRoundedRectangle(_MainBrush, this.GetMainArea(), 
        this.RoundedCornerAngle, RectangleEdgeFilter.None);
    _graphics.DrawString(this.MainText, this.Font, 
        Brushes.White, this.GetMainAreaBackground(), f);

    // Draw right
    if (RightBarSize > 0)
    {
        _graphics.FillRoundedRectangle(_LeftAndRightBrush, 
           this.GetRightArea(), this.RoundedCornerAngle, 
           RectangleEdgeFilter.TopRight | RectangleEdgeFilter.BottomRight);
        _graphics.DrawString(this.RightText, this.Font, 
           Brushes.White, this.GetRightArea(), f);
    }

    // Clean up
    _LeftAndRightBrush.Dispose();
    _MainBrush.Dispose();
    _StatusBrush.Dispose();
}

Drag and Drop

To make this control a little more useful, I added drag and drop to it, controlled by AllowDrag. To enable the control to be "selected", I had to prevent the DoDragDrop() from triggering on MouseDown. This was solved by adding a small check so that you have to drag the control outside a small radius before it triggers. I also have _isDragging to be able to trigger DoDragDrop(); in the OnMouseMove override.

protected override void OnMouseDown(MouseEventArgs e)
{
    this.Focus();
    base.OnMouseDown(e);
    _mX = e.X;
    _mY = e.Y;
    this._isDragging = false;
}

protected override void OnMouseMove(MouseEventArgs e)
{
    if (!_isDragging)
    {
        // This is a check to see if the mouse is moving while pressed.
        // Without this, the DragDrop is fired directly
        // when the control is clicked, now you have to drag a few pixels first.
        if (e.Button == MouseButtons.Left && 
                _DDradius > 0 && this.AllowDrag)
        {
            int num1 = _mX - e.X;
            int num2 = _mY - e.Y;
            if (((num1 * num1) + (num2 * num2)) > _DDradius)
            {
                DoDragDrop(this, DragDropEffects.All);
                _isDragging = true;
                return;
            }
        }
        base.OnMouseMove(e);
    }
}

protected override void OnMouseUp(MouseEventArgs e)
{
    _isDragging = false;
    base.OnMouseUp(e);
}

Selection

My purpose was to use this control in a FlowLayoutPanel and to make it look like a ListView, so I wanted to be able to "select" the items. This is made by changing the background color while focused. It's not totally perfect, but it was good enough for me.

protected override void OnGotFocus(EventArgs e)
{
    this.BackColor = Color.SandyBrown;
    base.OnGotFocus(e);
}

protected override void OnLostFocus(EventArgs e)
{
    this.BackColor = Color.Transparent;
    base.OnLostFocus(e);
}

protected override void OnClick(EventArgs e)
{
    this.Focus();
    base.OnClick(e);
}

Final Thoughts

This little Christmas workshop actually turned out pretty good. I could probably add more functions and better looking graphics and animation, but since my customers don't want it or need it, I think I will leave it like this for the moment.

Check out my other article about the FlowLayoutPanel to see how I plan to use it.

History

  • 2009-12-21 - v1.0 - Article added.

License

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

Share

About the Author

P.Sandgren
Architect Consid
Sweden Sweden
I have been working with .NET development since 2003, working with backend systems, and both web and desktop applications. I have experience of integrating software with ERP-systems, connecting production environment, and publishing information to various platforms. With this comes very extensive knowledge about windows OS administration, both server and client. I have worked with almost every OS from Microsoft, and a few linux dists in mixed environment, along with several mobile and "Thin client"-solutions. I have experience in configuring and managing webservers, ftpservers, domains and mailservers in a mixed OS environment.
 
I'm always looking forward to learning and mastering new technologies.
 
Specialties: .NET development, PLC integration, ERP integration
Development -> C#, ASP.NET MVC, HTML, WCF, WinForms, Silverlight, PHP, VB6
Database -> MSSQL, MySQL, MsAccess
ERP -> Jeeves, JSB, Jeeves eSales
Automation integration -> TwinCAT, Siemens S7
Automation HMI -> Iconics, Beijer, Indusoft
Microsoft products -> Sharepoint, Microsoft Office
OS -> Windows Guru (Server & Client), Linux (Debian/Ubuntu)
Follow on   LinkedIn

Comments and Discussions

 
QuestionOnly MainArea Pinmembereferreyra13-Nov-13 1:35 
AnswerRe: Only MainArea PinprofessionalP.Sandgren5-Apr-14 23:15 
QuestionLatest version and error Pinmembereferreyra13-Nov-13 1:11 
QuestionHow to customize the color PinmemberTridip Bhattacharjee21-Mar-13 4:56 
AnswerRe: How to customize the color PinmemberP.Sandgren21-Mar-13 6:16 
GeneralRe: How to customize the color PinmemberTridip Bhattacharjee21-Mar-13 20:51 
GeneralProgress Bar versus TimeLine Control PinmemberTV Mogul23-Dec-09 4:48 
GeneralIt's Almost Cool--but it has one big shortcoming! PinmemberTV Mogul22-Dec-09 3:44 
GeneralRe: It's Almost Cool--but it has one big shortcoming! PinmemberP.Sandgren22-Dec-09 6:07 
GeneralRe: It's Almost Cool--but it has one big shortcoming! PinmemberTV Mogul22-Dec-09 6:49 
GeneralRe: It's Almost Cool--but it has one big shortcoming! PinmemberP.Sandgren22-Dec-09 7:51 
GeneralWOW!!!! I am VERY Impressed! PinmemberTV Mogul22-Dec-09 8:32 
GeneralRe: It's Almost Cool--but it has one big shortcoming! PinmemberTV Mogul22-Dec-09 8:40 
GeneralRe: It's Almost Cool--but it has one big shortcoming! PinmemberP.Sandgren22-Dec-09 11:44 
Hello,
 
Well, the problem isn't really to print it out, but I don't think the time should be a part of the progressbar, rather the flowlayoutpanel. I got it all in my mind and will write a small sample tomorrow. I made this control to fit our application, so I'd rather branch it to a separate class to make it more suitable. Also, I will take a closer look to the article's you posted to make it better, and perhaps post it seperatly here on codeproject with a few samples.
 
Pär Sandgren
par.sandgren@gmail.com
Developer and Sysadmin
www.cgv.se

GeneralRe: It's Almost Cool--but it has one big shortcoming! PinmemberTV Mogul22-Dec-09 12:55 
GeneralRe: It's Almost Cool--but it has one big shortcoming! PinmemberTV Mogul22-Dec-09 7:06 
GeneralNice control PinmemberNagy Vilmos21-Dec-09 4:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140905.1 | Last Updated 21 Dec 2009
Article Copyright 2009 by P.Sandgren
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid