【问题标题】:Draw invalidated rectangle using WM_PAINT使用 WM_PAINT 绘制无效矩形
【发布时间】:2011-11-23 17:10:54
【问题描述】:

为了在ComboBox(或其他任何东西)上绘制文本,我覆盖WndProc() 并捕获0x000F 消息——即WM_PAINT

代码如下:

Protected Overrides Sub WndProc(ByRef m As Message)
    MyBase.WndProc(m)

    If m.Msg = &HF Then
        TextRenderer.DrawText(CreateGraphics, "The text over my control.", Font, _
                              ClientRectangle, ForeColor)
    End If
End Sub

效果很好,但是有一个问题:如果我将父窗口拖到屏幕的一侧(为了隐藏窗口窗体的一部分),我的控件的可见部分会被无限重绘。这会使文本重新绘制在自身之上!

我想有一种方法可以只绘制控件的无效(隐藏)部分。我该怎么办?

编辑

这是一张图片中的问题:http://i.stack.imgur.com/WqGfI.png(这是一个链接,因为我现在无法发布图片。)

更新

我尝试使用 BeginPaint API,但返回的 PAINTSTRUCT 结构中包含的 RECT 结构只包含零。

If m.Msg = WM_PAINT Then
    Dim ps As PAINTSTRUCT = New PAINTSTRUCT
    BeginPaint(Handle, ps)
    Console.WriteLine(ps.rcPaint.right)

    'painting goes here

    EndPaint(Handle, ps)
End If

我可以用它做点什么吗?我不知道如何继续,以便仅绘制无效区域。

【问题讨论】:

  • 有什么特别的理由在这里使用低级窗口处理而不是控件的Paint 事件?
  • 这是因为ComboBox(或ListView,我也在使用它)永远不会触发OnPaint。我读到这些控件不是由框架绘制的,而是由操作系统绘制的。
  • 其实是Me.CreateGraphics。这个方法是否准确地检索了我的控件的设备上下文图形(我要在哪里绘画)?

标签: vb.net gdi


【解决方案1】:

一个好的经验法则是,永远不要使用 Me.CreateGraphics。

尝试将您的代码更改为:

<DllImport("User32.dll")> _
Public Shared Function GetWindowDC(ByVal hWnd As IntPtr) As IntPtr
End Function

<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Boolean
End Function

Protected Overrides Sub WndProc(ByRef m As Message)
  MyBase.WndProc(m)

  If m.Msg = &HF Then
    Dim dc As IntPtr = GetWindowDC(m.HWnd)
    Using g As Graphics = Graphics.FromHdc(dc)
      TextRenderer.DrawText(g, "The text over my control.", Font, _
                          ClientRectangle, ForeColor)
    End Using
    ReleaseDC(m.HWnd, dc)
  End If
End Sub

【讨论】:

  • GetWindowDC 和 Me.CreateGraphics 有区别吗?我不知道。无论如何,您的代码会产生相同的结果。我使用此链接编辑了我的初始帖子:i.stack.imgur.com/WqGfI.png
  • @Velcro 将控件移动到屏幕边缘会使控件绘制太多次。您必须清除图形,这意味着您必须从头开始重新绘制组合框。不容易。
【解决方案2】:

您可能会在没有实际更新时尝试绘画。 PAINTSTRUCT 的一部分指示是否应该实际擦除窗口。

这是我过去使用过的。 GetUpdateRect 让您可以轻松查看是否有任何实际更新以及获取更新的区域,而无需致电 BeginPaint。请记住,BeginPaint 仅在您不将消息传递给基类时才使用。 (我在这个 sn-p 中使用 NativeWindow 类进行子类化。)

Dim hasUpdates As Boolean
Dim updRect As RECT
Dim r As Rectangle

hasUpdates = GetUpdateRect(_parentControl.Handle, updRect, False)
With updRect
    r = New Rectangle(.Left, .Top, .Right - .Left, .Bottom - .Top)
End With

' Invalidate the control so that the whole thing will be redrawn since
' our custom painting routine could redraw the entire client area.
If hasUpdates Then _parentControl.Invalidate(r)

' Pass the message along to be handled by the default paint event.
MyBase.DefWndProc(m)

' Now draw extras over the existing control.
If hasUpdates Then Me.PaintExtras(r)

因此,如果 hasUpdates 为 False,您要做的就是退出例程。

就用于绘制的图形而言,我已经成功使用Graphics.FromHwnd(ctrl.Handle)

【讨论】:

  • 我放弃了这个,因为我没有找到好的方法来做到这一点。感谢您的提示,我一定会尝试的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-20
  • 2011-02-17
  • 2020-11-25
  • 2021-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多