Note that there are some explanatory texts on larger screens.

plurals
  1. POGDI+ Paint Queue Problem
    text
    copied!<p>comrades) I've found some interesting behavior of Invalidate method in multithreaded applications. I hope you could help me with a problem...</p> <p>I experience troubles while trying to invalidate different controls at one time: while they're identical, one succesfully repaints itself, but another - not.</p> <p>Here is an example: I have a form (MysticForm) with two panels (SlowRenderPanel) on it. Each panel has a timer and with a period of 50ms Invalidate() method is called. In OnPaint method I draw number of current OnPaint call in the centre of panel. But notice that in OnPaint method System.Threading.Thread.Sleep(50) is called to simulate long time draw procedure.</p> <p>So the problem is that the panel added first repaints itself much more often than another one.</p> <pre><code>using System; using System.Drawing; using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsFormsApplication1 { static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MysticForm()); } } public class MysticForm : Form { public SlowRenderPanel panel1; public SlowRenderPanel panel2; public MysticForm() { // add 2 panels to the form Controls.Add(new SlowRenderPanel() { Dock = DockStyle.Left, BackColor = Color.Red, Width = ClientRectangle.Width / 2 }); Controls.Add(new SlowRenderPanel() { Dock = DockStyle.Right, BackColor = Color.Blue, Width = ClientRectangle.Width / 2 }); } } public class SlowRenderPanel : Panel { // synchronized timer private System.Windows.Forms.Timer timerSafe = null; // simple timer private System.Threading.Timer timerUnsafe = null; // OnPaint call counter private int counter = 0; // allows to use one of the above timers bool useUnsafeTimer = true; protected override void Dispose(bool disposing) { // active timer disposal (useUnsafeTimer ? timerUnsafe as IDisposable : timerSafe as IDisposable).Dispose(); base.Dispose(disposing); } public SlowRenderPanel() { // anti-blink DoubleBuffered = true; // large font Font = new Font(Font.FontFamily, 36); if (useUnsafeTimer) { // simple timer. starts in a second. calls Invalidate() with period = 50ms timerUnsafe = new System.Threading.Timer(state =&gt; { Invalidate(); }, null, 1000, 50); } else { // safe timer. calls Invalidate() with period = 50ms timerSafe = new System.Windows.Forms.Timer() { Interval = 50, Enabled = true }; timerSafe.Tick += (sender, e) =&gt; { Invalidate(); }; } } protected override void OnPaint(PaintEventArgs e) { string text = counter++.ToString(); // simulate large bitmap drawing System.Threading.Thread.Sleep(50); SizeF size = e.Graphics.MeasureString(text, Font); e.Graphics.DrawString(text, Font, Brushes.Black, new PointF(Width / 2f - size.Width / 2f, Height / 2f - size.Height / 2f)); base.OnPaint(e); } } } </code></pre> <p>Debug info:</p> <p>1) Each panel has a bool field useUnsafeTime (set to true by default) which allows using System.Windows.Forms.Timer (false) insted of System.Threading.Timer (true). In the first case (System.Windows.Forms.Timer) everything works fine. Removing System.Threading.Sleep call in OnPaint also makes execution fine.</p> <p>2) Setting timer interval to 25ms or less prevents second panel repainting at all (while user doesn't resize the form).</p> <p>3) Using System.Windows.Forms.Timer leads to speed increasement</p> <p>4) Forcing control to enter synchronization context (Invoke) doesn't make sense. I mean that Invalidate(invalidateChildren = false) is "thread-safe" and could possibly have different behavior in diffenent contexts</p> <p>5) Nothing interesting found in IL comparison of these two timers... They just use different WinAPI functions to set and remove timers (AddTimerNative, DeleteTimerNative for Threading.Timer; SetTimer, KillTimer for Windows.Forms.Timer), and Windows.Forms.Timer uses NativeWindow's WndProc method for rising Tick event</p> <p>I use a similar code snippet in my application and unfortunately there is no way of using System.Windows.Forms.Timer) I use long-time multithreaded image rendering of two panels and Invalidate method is called after rendering is completed on each panel...</p> <p>That would be great if someone could help me to understand what's different happening behind the scenes and how to solve the problem.</p> <p>P.S. Interesting behavior isn't it?=)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload