Question

I created a class derived from System.Windows.Forms.ProgressBar. I followed the method advised by MSDN:

  • Add a usercontrol to your project
  • Open the code of the user control
  • Instead of deriving it from System.Windows.Forms.UserControl, derive from ProgressBar.

Furthermore I override OnPaint, so I can paint it myself:

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    // etc, do your own painting.

Yet this function is never called. A breakpoint doesn't break here. The progressbar is painted normally. What am I missing?

Was it helpful?

Solution

The followed procedure is exactly as MSDN proposes. A progressbar has an OnPaint function, so according to MSDN your OnPaint should be called.

The reason that it isn't called is because you have to declare that you will draw your control yourself. In old MFC this was called OwnerDrawn. To tell the system that you want to draw your control yourself you have to change the style of the Control. This is done using Control.SetStyle:

public partial class ColorProgressBar : System.Windows.Forms.ProgressBar
{
    public ColorProgressBar()
    {
        InitializeComponent();
        this.SetStyle(ControlStyles.UserPaint, true);
        // etc, other initializations
    }

This will take care that your OnPaint will be called.

As an example the complete ColorProgressBar. The code of this class can be found elsewhere, but here it is rewritten as a derived class from System.Windows.Forms.ProgressBar. This makes the code much smaller. Besides you have all functionality of a Progressbar.

This progressbar doesn't have a plain color for the bar, but a gradient color between two colors. It can be added using the toolbox like any other control. The properties can be changed like you would change the properties of a Progressbar. At the bottom of the properties window you'll see the extra properties.

To create it:

  • Create your project
  • In Solution Exploser - Add - user control, give it a name, for instance ColorProgressBar
  • Open the code of the ColorProgressBar in the editor
  • Change the base class from UserControl into System.Windows.Forms.ProgressBar
  • Change the class to your needs, see the example below

The most important function is the OnPaint, which will change the appearance of the ProgressBar. The rest is quite simple:

  • Add two properties to describe the gradient colors
  • In the constructor the SetStyle as described above to be sure your OnPaint is called:

    public partial class ColorProgressBar : System.Windows.Forms.ProgressBar { public Color BarColorOutside { get; set; } public Color BarColorCenter { get; set; }

    public ColorProgressBar()
    {
        BarColorOutside = Color.Black;
        BarColorCenter = Color.Yellow;
        InitializeComponent();
        this.SetStyle(ControlStyles.UserPaint, true);
    }
    
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        // your own painting will be added later
    }
    

Now check if this base works:

  • Build
  • Create a form. Add the progressbar to the form
  • Use the properties to give the progressbar a value, and initial colors
  • Debug to check if your onPaint is called.

Now the onPaint. The part of the Progressbar that is filled will be filled with a gradient color. To do that we need to know the fill width and the height of the progress bar. We can make two rectangles: one that will fill the upper half, and one that will fill the bottom half. The filling will be done with a gradient brush: the upper half from barColorOutside to barColorCenter, the bottom half from barColorCenter to barColorOutside. This way the center color will be in the center of the progressbar.

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    // the part that has to be filled with a colored progress:
    int fillWidth = (Width * Value) / (Maximum - Minimum);
    if (fillWidth == 0)
    {    // nothing to fill
        return;
    }

    // the two rectangles:
    Rectangle topRect = new Rectangle(0, 0, fillWidth, Height / 2);
    Rectangle bottomRect = new Rectangle(0, Height / 2, fillWidth, Height);

    // Paint upper half: the gradient fills the complete topRect,
    // from background color to foreground color
    LinearGradientBrush brush = new LinearGradientBrush(topRect, BarColorOutside,  
        BarColorCenter, LinearGradientMode.Vertical);
    e.Graphics.FillRectangle(brush, topRect);
    brush.Dispose();

    // paint lower half: gradient fills the complete bottomRect,
    // from foreground color to background color
    brush = new LinearGradientBrush(bottomRect, BarColorCenter, BarColorOutside, 
        LinearGradientMode.Vertical);
    e.Graphics.FillRectangle(brush, bottomRect);
    brush.Dispose();

    // we have missed one line in the center: draw a line:
    Pen pen = new Pen(BarColorCenter);
    e.Graphics.DrawLine(pen, new Point(0, topRect.Bottom),
        new Point(fillWidth, topRect.Bottom));
    pen.Dispose();

    // if style is blocks, draw vertical lines to simulate blocks
    if (Style == ProgressBarStyle.Blocks)
    {
        int seperatorWidth = (int)(this.Height * 0.67);
        int NrOfSeparators = (int)(fillWidth / seperatorWidth);
        Color sepColor = ControlPaint.LightLight(BarColorCenter);

        for (int i = 1; i <= NrOfSeparators; i++)
        {
            e.Graphics.DrawLine(new Pen(sepColor, 1),
            seperatorWidth * i, 0, seperatorWidth * i, this.Height);
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top