views:

525

answers:

6

I have written a custom control that renders some graphics. The graphics itself is fairly expensive to render but once rendered it rarely changes. The problem I have is that if you move the mouse really fast over the graphics surface it keeps calling the control's overridden Paint method which then incurs a high CPU penalty:

private void UserControl1_Paint(object sender, PaintEventArgs e)

What techniques could be used to avoid this situation or minimise any unnecessary redrawing since the graphics/image underneath the mouse pointer is not actually changing?

+2  A: 

You can use a buffer image on which to draw when something is changed and in the Paint method just copy the image on the screen. Should be pretty fast. Also you can use the clip region to copy only the part that needs updating. This should reduce CPU usage.

You can also use an IsDirty flag to know when to update the buffer image (i.e. fully redraw it) if needed.

rslite
this is currently what I do. However I was wondering if there is a way to stop the environment calling Paint at all when the mouse moves.
freddy smith
OnPaint is not called when the cursor moves over a control.
Ed Swangren
+3  A: 

EDIT: After seeing your edit, I can assure you that OnPaint is not called by default when the mouse moves over a control. Something in your code is definitely causing the re-paint, you just don't see it yet. Perhaps posting some of your code would help us find the problem.

Are you invalidating your control on MouseMove? That is likely a bad idea, and if you really needed to do it (i.e., you are making a graphics editor or something), you would have to be smart about how large a region was actually re-drawn. So, solution; don't paint your control in MouseMove.

Otherwise, I would not expect OnPaint to fire when the mouse moves over the control. You can also just generate the image once and then blt it to the Graphics object until it needs to be re-generated.

Ed Swangren
thanks. You must be correct. I am actually overriding another Control's behaviour so it might be doing something naughty under the sheets that is causing the mousemove to raise an invalidate
freddy smith
A: 

You can use this code to supress and resume redrawing of a control :] Good luck.

   using System;
    using System.Windows.Forms;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;

namespace pl.emag.audiopc.gui {
    // ************************************************************************
//`enter code here`
    // ************************************************************************
    public class PaintingHelper {
        // ********************************************************************
//
        // ********************************************************************
        public static void SuspendDrawing(Control parent) {
            SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
        }
        // ********************************************************************
//
        // ********************************************************************
        public static void ResumeDrawing(Control parent) {
            SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
            parent.Refresh();
        }
        // ********************************************************************
//
        // ********************************************************************
        [DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, 
            bool wParam, Int32 lParam);
        // ********************************************************************
        //
        // ********************************************************************
        private const int WM_SETREDRAW = 11;
    }
}
lbownik
+2  A: 

You should not call Paint directly.

Instead, call Invalidate (Control.Invalidate). This queues the need to be repainted, and Windows will take care of the call itself. This way, a lot of rapid invalidations (repaint requests) can be serviced by one call to Paint.

Frank Krueger
A: 

Also check the Clip Rectange in the PaintArgs event argument so that you only repaint the area that needs to get updated instead of re-drawing the entire image.

TJB
+1  A: 

One of the things that can cause high CPU load in Paint method is improper (unmanaged) resource release. Make sure you do Dispose() all of the pens, brushes and so forth (probably all of the System.Drawing classes instances has some unmanaged resource tied to them). Basically you should Dispose() of the object as soon as you have finished working with it. Do not cache or anything - GDI+ resources are system resources and should be returned to system as soon as possible. Acquiring them (creating new Brush class instance for example) should be pretty quick, but I do not have anything to back this statement up at the moment.

Audrius