【问题标题】:MFC GUI custom control: how to draw cursor updates in response to mouse moves?MFC GUI 自定义控件:如何绘制光标更新以响应鼠标移动?
【发布时间】:2019-09-16 21:35:43
【问题描述】:

我有一个从 CButton 子类化的自定义 Windows 控件(不知道为什么选择它——这是 17 年前的代码;没有任何按钮功能的外观)。

它的DrawItem( LPDRAWITEMSTRUCT pdis ) 方法由CButton::OnChildNotify 调用以响应WM_DRAWITEM。它使用DC CDC::FromHandle( pdis->hDC ) 渲染其场景。

鼠标事件方法OnMouseMove()计算新的光标位置并调用RedrawWindow( NULL, NULL, RDW_INVALIDATE )。跟随鼠标的光标适时出现在新的鼠标位置。它工作正常,但速度很慢。事实上,只需要重绘之前和新的光标单元格(如果那样的话),但随着整个场景被多次渲染,图形更新开始滞后。

我认为在我的OnMouseMove() 方法中,与其重新绘制整个场景,不如只绘制有问题的单元格。它已经具有单元格的确切 X 和 Y 坐标以及指向其数据的指针。我以为CPaintDC(this) 会提供一个DC 允许这样做,但它不会绘画。 (也不会崩溃,这是一种难得的快乐。)

我模糊的回忆是,执行此操作的“最佳”方法是仅使两个单元格的区域无效,DrawItem() 方法最终会被告知这些区域无效,而不是完全重新绘制它可以只需从坐标中计算出它们是哪些单元格(顺便说一句,这不是一个简单的操作)并重新绘制它们,这不仅可以简化这个光标问题,而且还可以确保只有少数几个单元格被绘制,部分隐藏的控件部分显示。但时间压力不允许,用例似乎也没有要求对此进行优化。

所以问题是:有没有什么好办法让OnMouseMove() 立即重新渲染单个控件,如果是这样,用DC 做什么? (例如,我可以缓存我在DrawItem() 通过FromHandle()? 收到的DC

现在我唯一的想法是让一个对象成员指向要重绘的单个单元格,使用此 RDW_UPDATENOW 标志调用 RedrawWindow(),并设置 DrawItem(),如果设置了该标志,就这样做一项。这将导致 DrawItem() 获得一个 DC,该 DC 可能会以它一直拥有的方式工作。看起来像一个真正的黑客,有没有更好的方法?

【问题讨论】:

  • WM_PAINT 中,默认情况下会裁剪 DC,以允许仅在围绕已失效区域的框内绘制。 RedrawWindow() 使整个窗口无效。改用InvalidateRect() 仅使实际需要重绘的区域无效。
  • 不要依赖鼠标位置来决定画什么。您可以使用GetClipBox() 获取 DC 的剪贴框,然后不要在框外绘制任何内容。使用框的坐标确定框内的单元格,然后仅绘制这些单元格。
  • A CPaintDC “对象只能在回复WM_PAINT 消息时使用”。当然,您不能在 WM_MOUSEMOVE 处理程序中使用它。您可以在 WM_MOUSEMOVE 处理程序中执行的操作是计算您的网格光标位置,调用您的实现来设置它,并让该实现更新以前的(如果有的话)以及新的单元格,如果网格光标位置是与之前的网格光标位置不同。
  • 响应WM_PAINT 专门渲染的规则是一个经验法则,真的。遵循它时,您无需考虑太多。一切都正常工作,高效,也是。你可以WM_PAINT之外渲染,只要你的WM_PAINT处理程序和带外渲染产生相同的视觉效果(如果他们不这样做,你会得到闪烁,一个不一致的用户界面和困惑的用户)。如果您想响应WM_MOUSEMOVE 进行渲染,请获取CClientDC 并执行您的渲染(恢复...
  • ...以前突出显示的单元格,以及呈现新的突出显示)。您可能想要应用适当的剪辑区域,尽管这不是严格要求的。您还必须确保注册WM_MOUSELEAVE 消息,以便在移出控件时恢复突出显示的单元格。

标签: c++ windows user-interface mfc


【解决方案1】:

在 Windows 应用程序中,通常执行所有渲染以响应 WM_PAINT(或 WM_NCPAINT)消息。需要触发重绘的代码通过调用InvalidateRect(和朋友)将窗口客户区的部分或全部标记为脏。系统针对这种方法进行了优化,将多个请求合并到一个更新区域,然后在没有更重要的工作要做(如处理输入)时发出WM_PAINT 消息。

这工作可靠,并且通常比将渲染分布在多个地方更容易实现。但是,偏离这一点并在代码中的任何位置执行渲染是完全合法的。尽管WM_PAINT 消息仍然可以随时到达,但最好让带外渲染产生与WM_PAINT 处理程序相同的视觉结果,以防止视觉伪影。

所有渲染都经过一个称为device context (DC) 的抽象。在MFC应用程序中处理WM_PAINT消息时,可以通过构造CPaintDC实例来获得合适的DC。在其他任何地方渲染时,您不能使用CPaintDC,而是需要使用CClientDC(或CWindowDC,也可以渲染非客户区)。一般来说,渲染代码不需要知道它正在渲染到哪种类型的 DC,并且通常可以在不改变的情况下重复使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-20
    • 1970-01-01
    • 2016-09-24
    相关资源
    最近更新 更多