【问题标题】:Call to BeginPaint via PInvoke returning empty update region in PAINTSTRUCT通过 PInvoke 调用 BeginPaint,返回 PAINTSTRUCT 中的空更新区域
【发布时间】:2016-11-21 20:03:31
【问题描述】:

我一直致力于创建一个自定义的RichTextBox 控件,以向文本区域添加一些额外的图形。根据我一直在阅读的内容,此控件默认情况下不会公开其 Paint 事件。

我按照 MSDN (Painting on a RichTextBox Control ) 上的建议重新公开 Paint 事件并创建由 WM_PAINT 消息触发的 OnPaint 事件处理程序。

OnPaint 方法中,我尝试从Win32 API 调用BeginPaint() 来绘制一些形状,但没有绘制任何内容。当我检查PAINTSTRUCT 结构内的rcPaint 字段时,它始终为空(所有值均为0)。所以我的问题是,为什么更新区域总是空的?我一定是错过了什么。

相关代码:

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);
}

【问题讨论】:

  • 我猜在调用OnPaint() 时,.net 已经调用了BeginPaint() 来填充PaintEventArgs,因此返回的DC 可能是NULL(到表示失败)...
  • @andlabs,这确实是问题所在。显然 base.WndPrc 在到达我的 OnPaint 方法之前调用了 BeginPaint。

标签: c# winforms winapi pinvoke


【解决方案1】:

您必须通过WndProc 并允许控件执行其默认绘制。您可以使用Graphics 对象进行绘画。示例:

public partial class MyRichEdit : RichTextBox
{
    public MyRichEdit()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message msg)
    {
        switch (msg.Msg)
        {
            case 15://WM_PAINT
                base.WndProc(ref msg);
                Graphics g = Graphics.FromHwnd(Handle);
                Pen pen = new Pen(Color.Red);
                g.DrawRectangle(pen, 0, 0, 10, 10);
                return;
        }
        base.WndProc(ref msg);
    }
}

【讨论】:

  • 谢谢,巴马克。我使用 Graphics 类进行了测试,效果很好,但是当我想使用 BeginPaint 方法进行测试时,我遇到了问题。我不知道 base.WndProc 已经调用了 BeginPaint,所以我不得不改变我的逻辑来调用我的 OnPaint 方法。
【解决方案2】:

我发现了问题。 @andlabs 评论引导我查看我被覆盖的 WndProc 方法。我的绘画方法是在base.WndProc(ref msg) 之后调用的,它显然执行了BeginPaint。将我的OnPaint() 方法移到上面更正了问题。

错误代码:

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;
            }

        }

正确代码:

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;
            }

        }

【讨论】:

  • 我的建议是在PaintEventArgs 中使用Graphics 对象。那不行吗?
  • 是的,这行得通。我只是在尝试 BeginPaint。
  • 好的。关于BeginPaint() 需要注意的一件重要事情是它验证更新矩形,因此如果您在同一个WM_PAINT 调用中调用它两次,您将获得不同的更新矩形值。事实上,更新 rect 的存在决定了 WM_PAINT 是否被生成,所以 BeginPaint() 告诉 Windows “好的,我们现在正在绘画;在下一次之前无需再担心 WM_PAINTs更新矩形更改的时间”。
猜你喜欢
  • 2015-10-10
  • 2023-04-09
  • 2011-09-25
  • 2016-09-01
  • 2014-02-11
  • 2019-05-11
  • 2011-08-16
  • 2016-07-02
  • 2014-06-20
相关资源
最近更新 更多