CListCtrl子类ViewerGet无法正常刷新显示但功能仍可用的问题
MFC CListCtrl(OwnerData模式)ViewerGet控件冻结异常
开发环境与类结构
使用C++17标准结合MFC图形库开发应用,类继承关系如下:
ViewerGet、ViewerSet均为虚类,继承自ViewerViewer继承自MFC的CListCtrl
ViewerGet初始化代码
void ViewerGet::InitViewer(CWnd* pParent, CRect oWndRect) { if (!Create(WS_TABSTOP | WS_CHILD | LVS_REPORT | LVS_OWNERDATA, oWndRect, pParent, 0)) return; // 设置扩展样式 + 提示 SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_INFOTIP | LVS_EX_LABELTIP); ListView_SetExtendedListViewStyleEx(*pParent, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); // 创建列 LPTSTR lpszCols[] = { _T("Variable name"),_T("Variable path"),_T("Value read"),_T("Validity"),_T("Recording"),_T("Type"),0}; CRect rect; GetWindowRect(&rect); int columnSize[] = { int(rect.Width() * 0.3), int(rect.Width() * 0.45), int(rect.Width() * 0.12), int(rect.Width() * 0.07), int(rect.Width() * 0.03) }; LV_COLUMN lvColumn; lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvColumn.fmt = LVCFMT_LEFT; for (int x = 0; lpszCols[x] != NULL; x++) { lvColumn.pszText = lpszCols[x]; lvColumn.cx = columnSize[x]; InsertColumn(x, &lvColumn); } m_arrowImage.Create(IDB_LOGO_LIST, 24, 0, RGB(255, 255, 255)); SetImageList(&m_arrowImage, LVSIL_SMALL); }
数据添加流程
- 在另一个
CListCtrl中选择行 - 右键呼出弹窗
- 点击「Add to ViewerGet」按钮,数据可正常添加至ViewerGet,此阶段无异常
异常现象
添加数据后操作ViewerGet时,会出现三种随机情况:
- 完全正常(可选择、删除、勾选复选框等)
- 立即冻结(点击行无法选中)
- 初始正常,但在不确定时间点冻结
额外现象:窗口最小化再最大化后,ViewerGet会刷新一次,但后续无法再次刷新;该问题仅出现在ViewerGet,ViewerSet无此异常;即使未连接CAN总线(ViewerGet本应每50ms接收CAN数据并刷新),冻结问题仍会出现。
添加数据的调用代码
void AppDlg::OnEditOnLeftTab() { m_oTabViewer.SetCurSel(0); OnSelchangeTabViewer(NULL, NULL); m_poViewGet->LockWindowUpdate(); EnterCriticalSection(database.GetMutex()); std::vector<IascVariable*> selVars = m_list.GetSelectedVariables(); for (IascVariable* var : selVars) if (database.ReadEntryFor(var)==NULL) database.AddReadEntry(new ReadEntry(var)); m_poViewGet->SetItemCount(database.ReadEntriesListCount()); LeaveCriticalSection(database.GetMutex()); }
预期行为
ViewerGet可正常工作,在需要时能刷新(如切换选中行、CAN通信时刷新对应列新值等)
已尝试的解决方法
- 尝试添加
WS_CLIPCHILDREN等多种CListCtrl样式 - 使用
LockWindowUpdate()与UnlockWindowUpdate()方法 - 使用
SetRedraw(FALSE)与SetRedraw(TRUE)方法 - 验证后台数据已正常更新
- 检查
OnGetDispInfo通知实现:
void Viewer::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult) { NMLVDISPINFO* plvdi = (NMLVDISPINFO*)pNMHDR; if (plvdi->item.mask & LVIF_TEXT) lstrcpyn(plvdi->item.pszText, CellContent(plvdi->item.iItem, plvdi->item.iSubItem), plvdi->item.cchTextMax); if (plvdi->item.mask & LVIF_IMAGE) { plvdi->item.iImage = CellImage(plvdi->item.iItem, plvdi->item.iSubItem); plvdi->item.mask |= LVIF_STATE; plvdi->item.stateMask |= LVIS_STATEIMAGEMASK; plvdi->item.state = INDEXTOSTATEIMAGEMASK(CheckboxContent(plvdi->item.iItem, plvdi->item.iSubItem)); } *pResult = 0; }
- 尝试用
RedrawItems(item,item)(单行)或RedrawWindow()强制刷新ViewerGet - 修改
OnCustomDraw方法:
void Viewer::OnCustomDrawBgColor(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR); *pResult = 0; pLVCD->nmcd.rc.top += 7; int nItem = pLVCD->nmcd.dwItemSpec; int nSubItem = pLVCD->iSubItem; // 根据绘制阶段执行不同操作 switch (pLVCD->nmcd.dwDrawStage) { (...) // 其他预处理阶段的逻辑 case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: this->RedrawWindow(); break; case CDDS_ITEMPOSTPAINT:; this->RedrawWindow(); break; } }
补充说明
排除死锁可能,因为死锁会导致ViewerGet完全瘫痪,但当前控件仍可后台工作,仅显示异常。
内容的提问来源于stack exchange,提问作者liebhurnout
相关产品推荐
相关产品推荐

