You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CListCtrl子类ViewerGet无法正常刷新显示但功能仍可用的问题

MFC CListCtrl(OwnerData模式)ViewerGet控件冻结异常

开发环境与类结构

使用C++17标准结合MFC图形库开发应用,类继承关系如下:

  • ViewerGet、ViewerSet 均为虚类,继承自 Viewer
  • Viewer 继承自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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:47:50