使用 WM_PAINT 绘制无效矩形

发布于 2024-12-05 22:32:09 字数 1077 浏览 1 评论 0原文

为了在 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

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

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

编辑

这是一张图片中的问题:https://i.sstatic.net /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

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

In order to draw a text over a ComboBox (or whatever else), I override WndProc() and catch 0x000F message -- which is WM_PAINT.

Code is like the following:

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

It works very well, but there is a problem: if I drag parent window on a side of the screen (in order to hide a part of the window form), the visible part of my control is infinitely redrawn. This makes the text redrawn over itself!

I suppose there is a way to draw only invalidated (hidden) part of the control. How can I do?

EDIT

Here is the problem in one picture: https://i.sstatic.net/WqGfI.png (it's a link since I can't post image for now.)

UPDATE

I tried using BeginPaint API, but the RECT structure included in the returned PAINTSTRUCT structure contains only zeros.

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

Can I do something with that? I don't know how to proceed, in order to paint only invalidated area.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

北笙凉宸 2024-12-12 22:32:09

一个好的经验法则是,永远不要使用 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

A good rule of thumb is, never use Me.CreateGraphics.

Try changing your code to this:

<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
掌心的温暖 2024-12-12 22:32:09

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

这是我过去使用过的。 GetUpdateRect 可让您轻松查看是否任何内容实际上都已更新,并且无需调用 开始绘制。请记住,BeginPaint 仅在您不将消息传递给基类时才执行。 (我在这段代码中使用 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)

You might be attempting to paint when there has been no actual update. Part of the PAINTSTRUCT indicates whether the window should actually be erased.

Here's what I've used in the past. GetUpdateRect allows you to easily see if anything has actually been updated as well as get the updated region without having to call BeginPaint. Remember, BeginPaint is only when you're not passing the message down to your base class. (I'm subclassing using the NativeWindow class in this snippet.)

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)

So what you would do is exit the routine if hasUpdates is False.

As far as the graphics to use to draw, I've had success using Graphics.FromHwnd(ctrl.Handle)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文