【发布时间】: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