Redraw Window Control Synchronously (with blocking method)
Asked Answered
P

1

7

What I am trying to do is to cause a control (in the same process, but which I have no control of) to redraw itself, and for my code to block until it finished redrawing.

I tried using UpdateWindow but that doesn't seem to wait for the redraw to finish.

The reason I need to wait for it to finish redrawing is that I would like to grab the screen afterwards.

The control is not a dotNet control, it's a regular windows control.

I've confirmed that:

  • The handle is correct.
  • UpdateWindow returns true.
  • Tried sending InvalidateRect(hWnd, IntPtr.Zero, true) just before the call to UpdateWindow to make sure the window needs invalidating.
  • Tried doing the same thing on the parent window of the control.

Code used:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InvalidateRect(IntPtr hWnd, IntPtr rect, bool bErase);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateWindow(IntPtr hWnd);

public bool PaintWindow(IntPtr hWnd)
{
    InvalidateRect(hWnd, IntPtr.Zero, true);
    return UpdateWindow(hWnd);
}
//returns true
Piscator answered 27/12, 2012 at 15:27 Comment(3)
Maybe you can disable the rendering in WM_PAINT case and then after that re-enable that.Iata
@Iata Could you elaborate please?Piscator
Hmm.. Why do you think that UpdateWindow doesn't seem to wait for the redraw to finish?Oneself
O
2

You can force application to process all the enqueued messages (including the WM_PAINT!) using Application.DoEvents. Something like this:

public bool PaintWindow(IntPtr hWnd)
{
    InvalidateRect(hWnd, IntPtr.Zero, true);
    if (UpdateWindow(hWnd))
    {
        Application.DoEvents();
        return true;
    }

    return false;
}

But if you're going to grab screen anyway, wouldn't it better to kill two birds with one stone by sending WM_PRINT message?

You can do it by the following code:

internal static class NativeWinAPI
{
    [Flags]
    internal enum DrawingOptions
    {
        PRF_CHECKVISIBLE = 0x01,
        PRF_NONCLIENT = 0x02,
        PRF_CLIENT = 0x04,
        PRF_ERASEBKGND = 0x08,
        PRF_CHILDREN = 0x10,
        PRF_OWNED = 0x20
    }

    internal const int WM_PRINT = 0x0317;

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg,
        IntPtr wParam, IntPtr lParam);
}

public static void TakeScreenshot(IntPtr hwnd, Graphics g)
{
    IntPtr hdc = IntPtr.Zero;
    try
    {
        hdc = g.GetHdc();

        NativeWinAPI.SendMessage(hwnd, NativeWinAPI.WM_PRINT, hdc,
            new IntPtr((int)(
                NativeWinAPI.DrawingOptions.PRF_CHILDREN |
                NativeWinAPI.DrawingOptions.PRF_CLIENT |
                NativeWinAPI.DrawingOptions.PRF_NONCLIENT |
                NativeWinAPI.DrawingOptions.PRF_OWNED
                ))
            );
    }
    finally
    {
        if (hdc != IntPtr.Zero)
            g.ReleaseHdc(hdc);
    }
}
Oneself answered 27/12, 2012 at 20:20 Comment(3)
Thanks, I'll give this a try and let you know.Piscator
Will only be able to test it tomorrow, I'll update as soon as I do.Piscator
Application.DoEvents worked, the WM_PRINT unfortunately didn't. Thanks for the help!Piscator

© 2022 - 2024 — McMap. All rights reserved.