【问题标题】:Force call to OnCustomDraw with CDDS_ITEMPREPAINT使用 CDDS_ITEMPREPAINT 强制调用 OnCustomDraw
【发布时间】:2021-07-07 07:25:52
【问题描述】:

我在工具栏中有一个自定义滑块。为了在工具栏中显示滑块,它被包裹在一个CMFCToolBarButton 派生类中。

滑块使用OnCustomDraw 进行渲染:

BEGIN_MESSAGE_MAP(CCustomSlider, CSliderCtrl)
  ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
END_MESSAGE_MAP()

void CCustomSlider::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
  const auto lpcd = (LPNMCUSTOMDRAW)pNMHDR;
  std::cout << "Draw stage: " << lpcd->dwDrawStage << '\n'; // for debugging purposes

  switch (lpcd->dwDrawStage) {
  case CDDS_PREPAINT:
    *pResult = CDRF_NOTIFYITEMDRAW;
    break;
  case CDDS_ITEMPREPAINT: // custom drawing done here
    switch (lpcd->dwItemSpec) {
      // ...
    }
    break;
  }
}

(对自定义滑块感兴趣的朋友,我关注了this tutorial)。

在我的应用程序的某个时刻,我不得不强制重绘滑块,因为用户改变了它的视觉风格。

我尝试了以下(包括组合)但没有成功:

  • slider.Invalidate();
  • slider.GetOwner()-&gt;SendMessage(WM_COMMAND, m_nID);m_nID 是滑块发送的命令 ID)
  • slider.SendNotifyMessageA(NULL, NULL, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);

与包装器相同。我也尝试过强制重绘工具栏:

toolbar.AdjustLayout();
toolbar.Invalidate();
toolbar.RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);

当视觉样式发生变化时(包括工具栏),应用程序的其余部分会正确刷新,而不是滑块。

如果我调整窗口大小,滑块会正确更新。

据我所知,我似乎没有向OnCustomDraw 发送正确的消息。当我自己重绘时,它显示 draw stageCDDS_PREPAINT,而在调整大小时它被调用了几次,其中一些包括CDDS_ITEMPREPAINT,所以看来我只是在呼吁一个将军更新不指明具体项目,但不知道怎么做。

总体问题是:如何强制重绘此控件?

【问题讨论】:

  • 您是否尝试过使用InvalidateRect 使包含滑块的窗口区域 无效?或者尝试使窗口的整个客户区无效,看看它是否解决了问题。很久没写 MFC 代码了,但是我感觉你需要使窗口无效,而不是控件,在你的情况下。
  • @Afshin 是的,我尝试使用 slider.GetWindowRect() 使其无效,然后在主应用程序窗口中调用 InvalidateRect(),同时使用屏幕和客户端坐标,但什么也没有。
  • 您是否在 CView 派生类中执行此操作?最好是销毁视图(不销毁附加文档,然后重新创建视图。
  • 不完全是,我忘了提到我将滑块包装成CMFCToolBarButton,以便在工具栏中显示它。我已更新问题以反映这一点。
  • 不确定,但您可以尝试将RDW_FRAME 标志添加到RedrawWindow 调用是否会改变任何内容?如果这不起作用,那么使用RDW_FRAMECHANGED 标志调用SetWindowPos 怎么样?我似乎记得任何一个都应用了更改主题时所需的某种修复,尽管我可能记错了。

标签: c++ windows mfc redraw


【解决方案1】:

到目前为止,我发现强制重绘的唯一方法是使用CSliderCtrl::SetRangeMinCSliderCtrl::SetRangeMax 方法,它们接收可选参数BOOL bRedraw

slider.SetRangeMin(slider.GetRangeMin(), TRUE);

这个 hack 足以解决我遇到的问题,但看起来有点脏和间接

取自MFC的源代码:

_AFXCMN_INLINE void CSliderCtrl::SetRangeMin(_In_ int nMin, _In_ BOOL bRedraw)
{ ASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETRANGEMIN, bRedraw, nMin); }

我无法找到的是 bRedraw = TRUE 时所谓的底层。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 2016-11-04
    • 2013-03-02
    • 2011-04-28
    • 2021-11-30
    • 1970-01-01
    相关资源
    最近更新 更多