Windows message queue question

  • Thread starter Thread starter Markus Minichmayr
  • Start date Start date
M

Markus Minichmayr

Hi all!

I'm writing a windows application, that contains a control that shows a
bitmap. The bitmap is painted in the controls OnPaint method, using the
Graphics.DrawImage method. It is possible to drag the bitmap around inside
the control, using the mouse. While dragging the image, I call the controls'
Invalidate method every time i get a MouseMove-event. So far so good. The
problem is, that, when the image is large and I drag it around and then stop
moving the mouse, the image often still keeps moving over the control for a
few more seconds. I guess, this is caused by MouseMove events, still
contained in the applications message queue. A profiling utility showed,
that OnPaint was called exactly as often as MouseMove. I was surprised about
that, since I thought that OnPaint would be called asynchronously and only
when there is time for.

Can anybody explain this to me? How can it happen, that there are MouseMove
events in the queue for such a long time and why is OnPaint obviously called
every single time, Invalidate is called? (I don't call Update!)
Is there a possibility, to drop all MouseMove and Paint events, currently
contained in the message queue, except the last ones?
Does anyone know another solution for the problem?
If this is the wrong NG for my question, wher else should I post it?

Thanks for any comments
Markus
 
Hi Markus,
How Alex told you Control.Invalidate does cause Paint event to be fired as
the next event. The difference between Update and Invalidate is that Update
is synchronous (the caller is blocked in the Update call and Paint method is
called right away) and Invalidate is asynchronous (which in turn means that
the caller is not blocked, but the next event executed after the current
finishes its job is going to be the Paint event).
This is somehow different than InvaldateRect API function and WM_PAINT
message.

Anyway I tried to reproduce your case, but I couldn't get MouseMove event
accumulated while the form is painted. Can you post some code that
demonstrates your issue.

HTH
B\rgds
100
 
Invalidate is specifically meant to cause Paint message. Please check
Control.Invalidate method description.

As about other question you might need to consider to use PeekMessage in
message pump to find out if next message can be ignored.

HTH
Alex
 
Hi 100!

Thanks for your comment! I have created a minimal app which reproduces the
problem. (See below) I understand that Invalidate() only places a paint
message in the message-queue, instead of painting immediately. However, I
thought that, if there are multiple paint messages in the message queue at
the same time, some algorithm would be applied to remove some of them.

I found out, that the problem only occurs in very special situations. I
tested my application on following configuration: PIII 870MHz, 512MB, Matrox
Parhelia 128MB, Resolution 2x1600x1400x32bpp, Win2000, .NET 1.1
I loaded an 753x555x8bpp Grayscale image.
The problem only appeared, if the application window had a special size. In
my case between about 1200x900 and 2800x1200. If the window was smaller,
everything run very smooth and fast. Interesting is, that if the window-size
was above about 2800x1200, then the application was slow and flickering, but
when I stopped moving the mouse, the image only was repainted once, instead
of keeping moving over the screen for several seconds. It seems, that
windows (or the .NET framework) detected, that it would be too slow, to
handle every single paint event and applied some algorithm to reduce
multiple paint messages in that case. Similar, the problem didn't appear at
all, if I loaded very large or very small images. I couldn't reproduce the
problem at all on a WindowsXP machine.

Thanks for any further comments.
Markus

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

namespace DragTest

{

public class Form1 : System.Windows.Forms.Form

{

private Bitmap myBitmap = null;

private Point myBitmapOrigin = new Point(0, 0);

private Point lastMousePos;

private bool isDragging = false;





private System.ComponentModel.Container components = null;

public Form1()

{

this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

this.SetStyle(ControlStyles.UserPaint, true);

this.SetStyle(ControlStyles.DoubleBuffer, true);

this.SetStyle(ControlStyles.ResizeRedraw, true);

InitializeComponent();

OpenFileDialog dlg = new OpenFileDialog();

if (dlg.ShowDialog(this) != DialogResult.OK)

throw new Exception();

myBitmap = new Bitmap(dlg.FileName);

this.Invalidate();

}

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows Form Designer generated code

private void InitializeComponent()

{

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(752, 581);

this.Name = "Form1";

this.Text = "Form1";

this.MouseDown += new
System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);

this.MouseUp += new
System.Windows.Forms.MouseEventHandler(this.Form1_MouseUp);

this.MouseMove += new
System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove);

}

#endregion

[STAThread]

static void Main()

{

try

{

Application.Run(new Form1());

}

catch (Exception e)

{

Console.WriteLine(e);

}

}

protected override void OnPaint(PaintEventArgs e)

{

base.OnPaint (e);

if (this.myBitmap != null)

e.Graphics.DrawImageUnscaled(this.myBitmap, this.myBitmapOrigin);

}

private void Form1_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)

{

this.lastMousePos = new Point(e.X, e.Y);

this.isDragging = true;

}

private void Form1_MouseUp(object sender,
System.Windows.Forms.MouseEventArgs e)

{

this.isDragging = false;

}

private void Form1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)

{

if (this.isDragging)

{

this.myBitmapOrigin.Offset(e.X - this.lastMousePos.X, e.Y -
this.lastMousePos.Y);

this.Invalidate();

this.lastMousePos = new Point(e.X, e.Y);

}

}

}

}
 
Hi Markus,

This might not apply to your situation, but that way I have done this out in
the unmanaged world is to ScrollDC my image around the client area as the
mouse moves, simply FillRect'ing the invalidated areas (white) and only
repainting the invalid areas when the user lifts the mouse button. This
makes the app seem very responsive to the user as the image then tracks the
mouse very precisely.

Cheers

Doug Forster
 
Hi Doug!

In fact, responsetime not really is the problem, the application is fast
enough. The problem is this very special behaviour of my application in some
specific situations (see below). And the problem also occurs where I don't
drag the whole image but only some overlays, painted on top of the image. In
such cases using windows scrolling functionality wouldn't work.

Below I attached a message I wrote to 100, precising the situatins where the
problem occurs.

Thanks for your comment anyway!
Markus

I have created a minimal app which reproduces the
problem. (See below) I understand that Invalidate() only places a paint
message in the message-queue, instead of painting immediately. However, I
thought that, if there are multiple paint messages in the message queue at
the same time, some algorithm would be applied to remove some of them.

I found out, that the problem only occurs in very special situations. I
tested my application on following configuration: PIII 870MHz, 512MB, Matrox
Parhelia 128MB, Resolution 2x1600x1400x32bpp, Win2000, .NET 1.1
I loaded an 753x555x8bpp Grayscale image.
The problem only appeared, if the application window had a special size. In
my case between about 1200x900 and 2800x1200. If the window was smaller,
everything run very smooth and fast. Interesting is, that if the window-size
was above about 2800x1200, then the application was slow and flickering, but
when I stopped moving the mouse, the image only was repainted once, instead
of keeping moving over the screen for several seconds. It seems, that
windows (or the .NET framework) detected, that it would be too slow, to
handle every single paint event and applied some algorithm to reduce
multiple paint messages in that case. Similar, the problem didn't appear at
all, if I loaded very large or very small images. I couldn't reproduce the
problem at all on a WindowsXP machine.

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

namespace DragTest

{

public class Form1 : System.Windows.Forms.Form

{

private Bitmap myBitmap = null;

private Point myBitmapOrigin = new Point(0, 0);

private Point lastMousePos;

private bool isDragging = false;





private System.ComponentModel.Container components = null;

public Form1()

{

this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

this.SetStyle(ControlStyles.UserPaint, true);

this.SetStyle(ControlStyles.DoubleBuffer, true);

this.SetStyle(ControlStyles.ResizeRedraw, true);

InitializeComponent();

OpenFileDialog dlg = new OpenFileDialog();

if (dlg.ShowDialog(this) != DialogResult.OK)

throw new Exception();

myBitmap = new Bitmap(dlg.FileName);

this.Invalidate();

}

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows Form Designer generated code

private void InitializeComponent()

{

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(752, 581);

this.Name = "Form1";

this.Text = "Form1";

this.MouseDown += new
System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);

this.MouseUp += new
System.Windows.Forms.MouseEventHandler(this.Form1_MouseUp);

this.MouseMove += new
System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove);

}

#endregion

[STAThread]

static void Main()

{

try

{

Application.Run(new Form1());

}

catch (Exception e)

{

Console.WriteLine(e);

}

}

protected override void OnPaint(PaintEventArgs e)

{

base.OnPaint (e);

if (this.myBitmap != null)

e.Graphics.DrawImageUnscaled(this.myBitmap, this.myBitmapOrigin);

}

private void Form1_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)

{

this.lastMousePos = new Point(e.X, e.Y);

this.isDragging = true;

}

private void Form1_MouseUp(object sender,
System.Windows.Forms.MouseEventArgs e)

{

this.isDragging = false;

}

private void Form1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)

{

if (this.isDragging)

{

this.myBitmapOrigin.Offset(e.X - this.lastMousePos.X, e.Y -
this.lastMousePos.Y);

this.Invalidate();

this.lastMousePos = new Point(e.X, e.Y);

}

}

}

}
 
Back
Top