Reputation: 15
For the app I'm making, I made a custom ProgressBar based on the one made by William Daniel: How to change the color of progressbar in C# .NET 3.5?
After doing some testing with lower percentage values I noticed this weird flicker:
I tried setting DoubleBuffered
to true but they still show up.
Does anyone know why these weird lines show up?
Upvotes: 1
Views: 964
Reputation: 32248
That's not flickering, that's a form of tearing. The Custom Control is not always drawn completely; it can happen if you have a close loop that doesn't allow the Form to repaint itself and its child controls correctly.
Possibly, make the procedure that fills the ProgressBar asynchronous.
Some suggested modifications to make it smoother:
Use floating point values for your calculations (don't cast to int
), and use a RectagleF to define the bounds of your drawing.
Remove the Bitmap and set ControlStyles.OptimizedDoubleBuffer:
If true, the control is first drawn to a buffer rather than directly to the screen, which can reduce flicker. If you set this property to true, you should also set the
AllPaintingInWmPaint
to true.
Then of course set ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint
:
AllPaintingInWmPaint
: If true, the control ignores the window messageWM_ERASEBKGND
to reduce flicker. This style should only be applied if the UserPaint bit is set to true.
UserPaint
: If true, the control paints itself rather than the operating system doing so. If false, the Paint event is not raised. This style only applies to classes derived from Control.
ControlStyles.Opaque
removes the background and lets ProgressBarRenderer do its job, to fill the base graphics of the ProgressBar.
Calculate the current width of ProgressBar's colored parts based on the current Value
set:
float width = (this.Width - 3) * ((float)this.Value / this.Maximum)
If width > 0
, then paint the ProgressBar colors using the Forecolor and BackColor properties. Of course you could use a different set of properties to define these colors.
Add some anti-alias to the drawing, setting the Graphics.SmoothingMode, to make the color transition generated by the LinearGradientBrush smoother. This is more useful when you set LinearGradientMode.Horizontal.
Resulting in:
public class ProgressBarCustom : ProgressBar
{
public ProgressBarCustom()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.Opaque, true);
}
protected override void OnPaint(PaintEventArgs e)
{
ProgressBarRenderer.DrawHorizontalBar(e.Graphics, ClientRectangle);
float width = (Width - 3) * ((float)Value / Maximum);
if (width > 0) {
var rect = new RectangleF(1, 1, width, Height - 3);
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
using (var brush = new LinearGradientBrush(rect, BackColor, ForeColor, LinearGradientMode.Horizontal)){
e.Graphics.FillRectangle(brush, rect);
}
}
}
}
As a small improvement, you could draw a line 9 pixels in height, with a semi-transparent color, to the top side of the ProgressBar, to simulate the original reflection.
Change the code to add Graphics.DrawLine()
:
// [...]
using (var brush = new LinearGradientBrush(rect, BackColor, ForeColor, LinearGradientMode.Horizontal))
using (var pen = new Pen(Color.FromArgb(40, 240,240,240), 9)) {
e.Graphics.FillRectangle(brush, rect);
e.Graphics.DrawLine(pen, 1.0f, 1.0f, width, 1.0f);
}
Upvotes: 2