我的工具栏中有一个自定义滑块。为了在工具栏中显示滑块,它被包装在
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;
}
}
(对于那些对自定义滑块感兴趣的人,我遵循了本教程)。
在我的应用程序的某个时刻,我必须强制重绘滑块,因为用户更改了其视觉样式。
我尝试了以下方法(包括组合)但没有成功:
slider.Invalidate();
slider.GetOwner()->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 stage是CDDS_PREPAINT
,而在调整大小时它被调用了几次,其中一些包括CDDS_ITEMPREPAINT
,所以看来我只是要求一般更新而没有指明具体内容项目,但我不知道该怎么做。
总体问题是:如何强制重绘此控件?
CSliderCtrl::SetRangeMin
或 CSliderCtrl::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
。
另一种对我们有用的方法是调用 PostMessage(WM_SYSCOLORCHANGE, 0, 0);