How to fix the flickering in User controls
It is not the kind of flicker that double-buffering can solve. Nor BeginUpdate or SuspendLayout. You've got too many controls, the BackgroundImage can make it a lot worse.
It starts when the UserControl paints itself. It draws the BackgroundImage, leaving holes where the child control windows go. Each child control then gets a message to paint itself, they'll fill in the hole with their window content. When you have a lot of controls, those holes are visible to the user for a while. They are normally white, contrasting badly with the BackgroundImage when it is dark. Or they can be black if the form has its Opacity or TransparencyKey property set, contrasting badly with just about anything.
This is a pretty fundamental limitation of Windows Forms, it is stuck with the way Windows renders windows. Fixed by WPF btw, it doesn't use windows for child controls. What you'd want is double-buffering the entire form, including the child controls. That's possible, check my code in this thread for the solution. It has side-effects though, and doesn't actually increase painting speed. The code is simple, paste this in your form (not the user control):
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
There are many things you can do to improve painting speed, to the point that the flicker isn't noticeable anymore. Start by tackling the BackgroundImage. They can be really expensive when the source image is large and needs to be shrunk to fit the control. Change the BackgroundImageLayout property to "Tile". If that gives a noticeable speed-up, go back to your painting program and resize the image to be a better match with the typical control size. Or write code in the UC's OnResize() method to create a properly sized copy of the image so that it doesn't have to be resized every time the control repaints. Use the Format32bppPArgb pixel format for that copy, it renders about 10 times faster than any other pixel format.
Next thing you can do is prevent the holes from being so noticeable and contrasting badly with the image. You can turn off the WS_CLIPCHILDREN style flag for the UC, the flag that prevents the UC from painting in the area where the child controls go. Paste this code in the UserControl's code:
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
The child controls will now paint themselves on top of the background image. You might still see them painting themselves one by one, but the ugly intermediate white or black hole won't be visible.
Last but not least, reducing the number of child controls is always a good approach to solve slow painting problems. Override the UC's OnPaint() event and draw what is now shown in a child. Particular Label and PictureBox are very wasteful. Convenient for point and click but their light-weight alternative (drawing a string or an image) takes only a single line of code in your OnPaint() method.
How to resolve unwanted flickering of Controls when the Form has a background Image?
Nothing to do with CPU limitations. This is related to the rendering of the Background of a Form and the content of its child Controls.
Read a description here: How to fix the flickering in User Controls.
(but WS_EX_COMPOSITED
won't help you here).
Since you have some Controls that a combined functionality, you can build a UserControl to group the single Controls into a specialized entity that provide the functionality and contains all the logic required to perform this Task and notify when a choice has been made (all input values are validated and the submit Button is cliecked).
To handle the transparency of this UserControl, you have to tweak its standard behavior a little bit, since just setting BackColor = Color.Transparent
is not enough when you have a Form with a background Image: the Transparency would be simulated considering the Form's Background
Color, not the content of the background Image (an Image is not a Color).
You can make your UserControl actually transparent, preventing its background from being painted.
- Use SetStyle() to set ControlStyles.Opaque, so the background is not painted.
- Set
ControlStyles.OptimizedDoubleBuffer
toFalse
: this UserControl cannot useDoubleBuffering
of course, otherwise we're back at square one (the Parent Control's background is used to build the BufferedGraphcs object) - Override CreateParams to add
WS_EX_TRANSPARENT
to the extended styles of the UserControl Window, so the System won't interfere, letting other windows behind the UserControl draw their content first (but we won't draw ours after). - The
WS_CLIPCHILDREN
Style needs to be removed (since the base class,Control
, adds it) otherwise the UserControls's child Controls may disappear when the Form is resized.
When the Add Task
Button is clicked, the UserControl can raise a public event, passing in a custom EventArgs object - after validation - the values entered.
The Form can subscribe to this event and read the custom EventArgs Properties when the event is raised.
- Since your Form has a BackgroudImage, set
DoubleBuffered = True
.
This is how it looks like:
The Image shown here has a size of 3840x2560
(it's freely downloadable from the Web).
Try to resize the Form without double-buffering it :)
A PasteBin of the complete UserControl, in case it's needed:
Transparent UserControl
Assuming the UserControl (AddNewTask
) added to a Form is named AddNewTask1
, you can add an event handler to its AddTaskClicked
event, using the Designer or in code, in the Form Constructor:
Public Class SomeForm
Public Sub New()
InitializeComponent()
AddHandler AddNewTask1.AddTaskClicked, AddressOf OnTaskAdded
End Sub
Private Sub OnTaskAdded(sender As Object, e As AddNewTask.AddTaskEventArgs)
Dim values As String = $"{e.TaskName}: Hours: {e.TaskHours}, Minutes: {e.TaskMinutes}"
End Sub
End Sub
The AddNewTask
UserControl:
Public Class AddNewTask
Private Const WS_EX_TRANSPARENT As Integer = &H20
Private Const WS_CLIPCHILDREN As Integer = &H2000000
Public Event AddTaskClicked As EventHandler(Of AddTaskEventArgs)
Public Sub New()
SetStyle(ControlStyles.Opaque Or ControlStyles.ResizeRedraw, True)
SetStyle(ControlStyles.OptimizedDoubleBuffer, False)
InitializeComponent()
End Sub
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.Style = cp.Style And Not WS_CLIPCHILDREN
cp.ExStyle = cp.ExStyle Or WS_EX_TRANSPARENT
Return cp
End Get
End Property
Private Sub btnAddTask_Click(sender As Object, e As EventArgs) Handles btnAddTask.Click
Dim hours As UInteger
Dim sHours = If(String.IsNullOrEmpty(txtHours.Text.Trim()), "0", txtHours.Text)
If (Not UInteger.TryParse(sHours, hours)) Then
ShowInputErrorMessage("Invalid Hours", txtHours)
Return
End If
Dim minutes As UInteger
Dim sMinutes = If(String.IsNullOrEmpty(txtMinutes.Text.Trim()), "0", txtMinutes.Text)
If (Not UInteger.TryParse(sMinutes, minutes)) Then
ShowInputErrorMessage("Invalid Minutes", txtMinutes)
Return
End If
Hide()
Dim args = New AddTaskEventArgs(txtTaskName.Text, hours, minutes)
RaiseEvent AddTaskClicked(Me, args)
txtHours.Clear()
txtMinutes.Clear()
txtTaskName.Clear()
ActiveControl = txtTaskName
End Sub
Private Sub ShowInputErrorMessage(msg As String, ctrl As TextBox)
MessageBox.Show(msg)
ctrl.Select()
ctrl.SelectAll()
End Sub
Public Class AddTaskEventArgs
Inherits EventArgs
Public Sub New(sTaskName As String, hours As UInteger, minutes As UInteger)
TaskName = sTaskName
TaskHours = hours
TaskMinutes = minutes
End Sub
Public ReadOnly Property TaskName As String
Public ReadOnly Property TaskHours As UInteger
Public ReadOnly Property TaskMinutes As UInteger
End Class
End Class
How to stop the flickering of Panels containing multiple controls
We finally managed to get rid of the flickering.
There were two factors which caused the problem:
- Use of Color:Transparent
- Use of the AutoScroll feature of the panels
We removed the transparent color and disabled the AutoScroll during resize.
How to eliminate flicker in Windows.Forms custom control when scrolling?
You could try putting the following in your constructor after the InitiliseComponent call.
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint, true);
EDIT:
If you're giving this a go, if you can, remove your own double buffering code and just have the control draw itself in response to the appropriate virtual methods being called.
Refresh problems with double buffered user controls
It sounds like you're approaching the limits of drawing in Winforms :) Have a look at the top answer to this question posted here: How to fix the flickering in User controls
How to avoid screen flickering when showing form with user drawn controls?
Did you try setting the DoubleBuffered property for the form?
flickering with UserControl backgroundimage in DataGridView?
Since the UserControl
holds not only a few Labels
but also a BackgroundImage
it will greatly profit from DoubleBuffering.
To activate it you need to add this line to each of the contructors:
public UCBind()
{
InitializeComponent();
this.Doublebuffered = true;
}
and:
public UCBind(DataRow row, ImageList imglist)
{
InitializeComponent();
this.Doublebuffered = true;
if (row != null)
{
imgList = imglist;
DisplayData(row);
}
}
When adding the UCs you can speed up the process by either:
- enclosing the loop the adds to the FlowLayoutPanel with a
flowLayoutPanel1.SuspendLayout();
andflowLayoutPanel1.ResumeLayout();
- or you can collect the UCs in a
List<UCBind>
and then use theAddRange
method to add them all in one go.
Here is the second option with the modified loop:
List<UCBind> ucs = new List<UCBind>();
foreach (DataRow row in dt.Rows)
{
UCBind ucb = new UCBind(row, imageList1);
ucs.Add(ucb);
}
flowLayoutPanel1.Controls.AddRange(ucs.ToArray());
Related Topics
In .Net, Which Loop Runs Faster, 'For' or 'Foreach'
Unity Scripts Edited in Visual Studio Don't Provide Autocomplete
The Provider Is Not Compatible With the Version of Oracle Client
How to Cancel Task Await After a Timeout Period
Why Xml-Serializable Class Need a Parameterless Constructor
How to Open a Chrome Profile Through --User-Data-Dir Argument of Selenium
C# Lambda Expressions: Why Should I Use Them
Why Is Thread.Sleep So Harmful
How to Simulate Mouse Click in C#
Easiest Way to Split a String on Newlines in .Net
How to Convert Json to Xml or Xml to Json
Filesystemwatcher Changed Event Is Raised Twice
Easiest Way to Read from and Write to Files