Search code examples
c#winformswinapipinvoke

Call to BeginPaint via PInvoke returning empty update region in PAINTSTRUCT


I have been working on creating a custom RichTextBox control to add some additional graphics to the text area. From what I've been reading, this control does not expose its Paint event by default.

I followed a suggestion on MSDN (Painting on a RichTextBox Control ) to re-expose the Paint event and create an OnPaint event handler which is triggered by the WM_PAINT message.

In the OnPaint method, I'm trying to call BeginPaint() from the Win32 API to draw some shapes, but nothing is being drawn. When I inspect the rcPaint field inside of the PAINTSTRUCT struct, it's always empty (all values are 0). So my question is, why is the update region always empty? I must be missing something.

Relevant code:

public partial class RichTextBoxEnhanced : RichTextBox
{

    private PAINTSTRUCT ps;


    new public void OnPaint(PaintEventArgs e)
    {
        var hdc = BeginPaint(this.Handle,  out ps);

        FillRect(hdc, ref ps.rcPaint, CreateSolidBrush(100));

        Rectangle(hdc, 1000, 2000, 1000, 2000);

        EndPaint(this.Handle, ref ps);

        Paint?.Invoke(this, e);
    }

    [DllImport("user32.dll")]
    static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);

    [DllImport("user32.dll")]
    static extern bool EndPaint(IntPtr hWnd, [In] ref PAINTSTRUCT lpPaint);

    [DllImport("gdi32.dll")]
    static extern IntPtr CreateSolidBrush(uint crColor);
}

Solution

  • I found the issue. @andlabs comment lead me to look at my overridden WndProc method. My painting method was called after base.WndProc(ref msg) which apparently performs BeginPaint. Moving my OnPaint() method above corrected the issue.

    Wrong code:

    protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_PAINT:
                        mBaseControl.Invalidate();
                        base.WndProc(ref m);
                        OnPaint();
                        break;
                    default:
                        base.WndProc(ref m);
                        break;
                }
    
            }
    

    Correct code:

    protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_PAINT:
                        mBaseControl.Invalidate();
                        OnPaint();
                        base.WndProc(ref m);
                        break;
                    default:
                        base.WndProc(ref m);
                        break;
                }
    
            }