![[Win32/WTL]_[虛擬列表]_[如何避免添加行數據時頻繁刷新]](http://pic.xiahunao.cn/yaotu/[Win32/WTL]_[虛擬列表]_[如何避免添加行數據時頻繁刷新])
場景在使用Win32/WTL虛擬列表時有時候底層短時間內接收到批量的數據之后需要更新到列表。這時如果只用SetItemCount更新列表時更新的越頻繁列表就會越閃爍。這樣給用戶的體驗就會比較差怎么解決閃爍的問題說明虛擬列表[5]主要是用來大數據的顯示卡頓問題。 但現在使用SetItemCount增加行數時會頻繁刷新列表可見區域如果可見區域有自定義的圖片區域那么刷新圖片會有明顯的閃爍問題。 這個虛擬列表已經有解決方案使用SetItemCountEx或者ListView_SetItemCountEx函數并傳遞LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL參數。LVSICF_NOINVALIDATEALL:除非受影響的項目當前處于視圖中否則列表視圖控件不會重新繪出。 即當前通過SetItemCountEx添加的行數剛好在列表的可見區域中,那么就會刷新。LVSICF_NOSCROLL: 當項計數發生更改時列表視圖控件不會更改滾動位置。 即當前你滾動到4-10行調用SetItemCountEx會影響當前的列表可見的行數4-10行, 只會滑塊變小。因此項目中把SetItemCount替換為SetItemCountEx, 即不會閃爍也提供性能。listview_.SetItemCountEx(photos_.size(),LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);listview_.SetItemCountEx(photos_.size(),LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);例子View.h// View.h : interface of the CView class///////////////////////////////////////////////////////////////////////////////#pragmaonce#includeutility#includestring#includevector#includememory#includeatlmisc.h#includeatlctrls.h#includeatlctrlx.h#includeGdiPlus.husingnamespacestd;enum{kMyButtonIdWM_USER1,kMyButtonId2,kMyButtonId3,kMyListViewId};classPhoto{public:wstring name;wstring createDate;wstring path;wstring format;};classCView:publicCWindowImplCView{public:DECLARE_WND_CLASS(NULL)BOOLPreTranslateMessage(MSG*pMsg);BEGIN_MSG_MAP_EX(CView)MSG_WM_CREATE(OnCreate)MESSAGE_HANDLER(WM_PAINT,OnPaint)NOTIFY_HANDLER(kMyListViewId,NM_CLICK,OnNMClickListResult)NOTIFY_HANDLER(kMyListViewId,LVN_GETDISPINFO,OnGetListViewData)NOTIFY_HANDLER(kMyListViewId,LVN_ODCACHEHINT,OnPrepareListViewData)NOTIFY_HANDLER(kMyListViewId,LVN_ODFINDITEM,OnFindListViewData)COMMAND_RANGE_HANDLER_EX(kMyButtonId,kMyButtonId3,OnCommandIDHandlerEX)MSG_WM_TIMER(OnTimer)REFLECT_NOTIFICATIONS()END_MSG_MAP()protected:// Handler prototypes (uncomment arguments if needed):// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL /*bHandled*/)// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL /*bHandled*/)// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL /*bHandled*/)intOnCreate(LPCREATESTRUCT lpCreateStruct);LRESULTOnPaint(UINT/*uMsg*/,WPARAM/*wParam*/,LPARAM/*lParam*/,BOOL/*bHandled*/);voidUpdateLayout();LRESULTOnNMClickListResult(intidCtrl,LPNMHDR pnmh,BOOLbHandled);LRESULTOnGetListViewData(intidCtrl,LPNMHDR pnmh,BOOLbHandled);LRESULTOnPrepareListViewData(intidCtrl,LPNMHDR pnmh,BOOLbHandled);LRESULTOnFindListViewData(intidCtrl,LPNMHDR pnmh,BOOLbHandled);voidOnCommandIDHandlerEX(UINT uNotifyCode,intnID,CWindow wndCtl);voidReloadMockData();voidReloadListView();voidOnTimer(UINT_PTR nIDEvent);private:std::wstringGetControlText(HWND hwnd,wchar_t*bufNULL);CListViewCtrl listview_;vectorshared_ptrPhoto*photos_;CFont font_normal_;CFont font_bold_;CBrushHandle brush_white_;CBrushHandle brush_hollow_;CBrush brush_red_;CButton buttonReloadMockData_;CButton buttonReloadListView_;CButton buttonDeleteListViewOneRow_;};View.cpp// View.cpp : implementation of the CView class///////////////////////////////////////////////////////////////////////////////#includestdafx.h#includeresource.h#includeutility#includesstream#includestdint.h#includeassert.h#includeStrsafe.h#includerandom#includeView.h#includeCommCtrl.h#includestring#includeregexusingnamespacestd;BOOLCView::PreTranslateMessage(MSG*pMsg){returnFALSE;}LRESULTCView::OnPaint(UINT/*uMsg*/,WPARAM/*wParam*/,LPARAM/*lParam*/,BOOL/*bHandled*/){CPaintDCdc(m_hWnd);CMemoryDCmdc(dc,dc.m_ps.rcPaint);CRect rect_client;GetClientRect(rect_client);mdc.FillSolidRect(rect_client,RGB(255,255,255));//TODO: Add your drawing code herereturn0;}staticHFONTGetFont(intpixel,boolbold,constwchar_t*font_name){LOGFONT lf;memset(lf,0,sizeof(LOGFONT));// zero out structurelf.lfHeightpixel;// request a 8-pixel-height fontif(bold){lf.lfWeightFW_BOLD;}lstrcpy(lf.lfFaceName,font_name);// request a face name ArialHFONT font::CreateFontIndirect(lf);returnfont;}std::wstringCView::GetControlText(HWND hwnd,wchar_t*buf){autolength::GetWindowTextLength(hwnd);boolbufNullfalse;if(!buf){bufnewwchar_t[length1]();bufNulltrue;}::GetWindowText(hwnd,buf,length1);std::wstringstr(buf);if(bufNull)delete[]buf;returnstr;}staticstd::wstringGetProductBinDir(){staticwchar_tszbuf[MAX_PATH];GetModuleFileName(NULL,szbuf,MAX_PATH);PathRemoveFileSpec(szbuf);intlengthlstrlen(szbuf);szbuf[length]L\\;szbuf[length1]0;returnstd::wstring(szbuf);}inttoRandInt(intmin,intmax){// thread_local 修飾符的變量只初始化一次,每個線程一個gen,避免多線程調用同一個gen導致數據競爭。// 使用minstd_rand生成性能高簡單的可預測的隨機數。// 使用mt19937 生成性能稍低很難預測的隨機數。//thread_local std::mt19937 gen(std::random_device{}());thread_localstd::minstd_randgen(std::random_device{}());std::uniform_int_distributiondist(min,max);returndist(gen);}LRESULTCView::OnGetListViewData(intidCtrl,LPNMHDR pnmh,BOOLbHandled){NMLVDISPINFO*plvdi(NMLVDISPINFO*)pnmh;autoiItemplvdi-item.iItem;if(-1iItem)return0;autocountphotos_.size();if(!count||countiItem)return0;autophotophotos_[iItem];if(plvdi-item.maskLVIF_TEXT){switch(plvdi-item.iSubItem){case0:{autovalueto_wstring((int64_t)iItem1);StringCchCopy(plvdi-item.pszText,plvdi-item.cchTextMax,value.c_str());break;}case1:{autovaluephoto-get()-nameL-to_wstring(toRandInt(1,100));StringCchCopy(plvdi-item.pszText,plvdi-item.cchTextMax,value.c_str());break;}case2:StringCchCopy(plvdi-item.pszText,plvdi-item.cchTextMax,photo-get()-format.c_str());break;case3:StringCchCopy(plvdi-item.pszText,plvdi-item.cchTextMax,photo-get()-createDate.c_str());break;}}return0;}LRESULTCView::OnPrepareListViewData(intidCtrl,LPNMHDR pnmh,BOOLbHandled){return0;}LRESULTCView::OnFindListViewData(intidCtrl,LPNMHDR pnmh,BOOLbHandled){LPNMLVFINDITEM pnmfi(LPNMLVFINDITEM)pnmh;autoiItempnmfi-iStart;if(-1iItem)return-1;autocountphotos_.size();if(!count||countiItem)return-1;return0;}LRESULTCView::OnNMClickListResult(intidCtrl,LPNMHDR pnmh,BOOLbHandled){return0;}voidCView::OnCommandIDHandlerEX(UINT uNotifyCode,intnID,CWindow wndCtl){switch(nID){casekMyButtonId:{ReloadMockData();break;}casekMyButtonId2:{ReloadListView();MessageBox(L刷新表格數據完成);break;}casekMyButtonId3:{std::vectorintrows;intiItem-1;while((iItemlistview_.GetNextItem(iItem,LVNI_SELECTED))!-1)rows.push_back(iItem);if(rows.empty())break;for(intirows.size()-1;i0;--i)photos_.erase(photos_.begin()rows.at(i));listview_.SetItemCountEx(photos_.size(),LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);MessageBox(L已刪除);break;}}}voidCView::ReloadListView(){listview_.SetItemCount(0);for(autoi0;iphotos_.size();i)deletephotos_[i];photos_.clear();}voidCView::OnTimer(UINT_PTR nIDEvent){staticintconstkMaxCount200;staticintindex0;staticintcount0;if(countkMaxCount){count0;index0;KillTimer(nIDEvent);MessageBox(L刷新模擬數據完成);return;}wchar_tbuf[MAX_PATH]{0};LVCOLUMN co;memset(co,0,sizeof(co));co.maskLVCF_TEXT;co.pszTextbuf;co.cchTextMaxMAX_PATH;listview_.GetColumn(1,co);std::wstringc0Text(buf);listview_.GetColumn(2,co);std::wstringc1Text(buf);listview_.GetColumn(3,co);std::wstringc2Text(buf);for(inti0;i500countkMaxCount;i,index){autophotonewPhoto();wsprintf(buf,(c0TextL-%d).c_str(),index);photo-namebuf;wsprintf(buf,(c1TextL-%d).c_str(),index);photo-formatbuf;wsprintf(buf,(c2TextL-%d).c_str(),index);photo-createDatebuf;photos_.push_back(newshared_ptrPhoto(photo));}listview_.SetItemCountEx(photos_.size(),LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);//listview_.SetItemCount(photos_.size());count;}voidCView::ReloadMockData(){SetTimer(1,500);}intCView::OnCreate(LPCREATESTRUCT lpCreateStruct){font_normal_::GetFont(16,false,LArial);font_bold_::GetFont(16,true,LArial);brush_hollow_AtlGetStockBrush(HOLLOW_BRUSH);brush_white_AtlGetStockBrush(WHITE_BRUSH);brush_red_.CreateSolidBrush(RGB(255,0,0));// 1.創建CListViewCtrllistview_.Create(m_hWnd,0,NULL,WS_CHILD|WS_TABSTOP|WS_VISIBLE|LVS_ALIGNLEFT|LVS_REPORT|LVS_SHOWSELALWAYS|WS_BORDER|LVS_OWNERDATA,0,kMyListViewId);listview_.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_DOUBLEBUFFER);//listview_.SetBkColor(RGB(255, 255, 255));listview_.SetTextBkColor(RGB(255,255,255));listview_.SetFont(font_normal_);autoheaderlistview_.GetHeader();header.SetFont(font_bold_);listview_.SetBkColor(RGB(255,255,255));listview_.InsertColumn(0,LNo.,LVCFMT_LEFT,100);listview_.InsertColumn(1,LName,LVCFMT_LEFT,100);listview_.InsertColumn(2,LFormat,LVCFMT_LEFT,100);listview_.InsertColumn(3,LCreate Date,LVCFMT_LEFT,100);// 2.創建按鈕buttonReloadMockData_.Create(m_hWnd,0,L加載新數據,WS_CHILD|WS_VISIBLE,0,kMyButtonId);buttonReloadMockData_.SetFont(font_normal_);buttonReloadListView_.Create(m_hWnd,0,L刷新表格,WS_CHILD|WS_VISIBLE,0,kMyButtonId2);buttonReloadListView_.SetFont(font_normal_);buttonDeleteListViewOneRow_.Create(m_hWnd,0,L刪除選中行,WS_CHILD|WS_VISIBLE,0,kMyButtonId3);buttonDeleteListViewOneRow_.SetFont(font_normal_);UpdateLayout();return0;}voidCView::UpdateLayout(){CRect rect;GetClientRect(rect);CClientDCdc(m_hWnd);dc.SelectFont(font_normal_);CSizesize_control(500,300);CRect rect_controlCRect(CPoint(20,20),size_control);listview_.MoveWindow(rect_control);CSize size_button;buttonReloadMockData_.GetIdealSize(size_button);rect_controlCRect(CPoint(rect_control.left,rect_control.bottom10),size_button);buttonReloadMockData_.MoveWindow(rect_control);CSize sizeButton2;buttonReloadListView_.GetIdealSize(sizeButton2);rect_controlCRect(CPoint(rect_control.right10,rect_control.top),sizeButton2);buttonReloadListView_.MoveWindow(rect_control);CSize sizeButton3;buttonDeleteListViewOneRow_.GetIdealSize(sizeButton3);rect_controlCRect(CPoint(rect_control.right10,rect_control.top),sizeButton3);buttonDeleteListViewOneRow_.MoveWindow(rect_control);}項目項目源碼,VS2010以上 https://download.csdn.net/download/infoworld/93325545運行帶SetItemCountEx帶LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL參數:SetItemCount不帶參數不帶參數時當前可見區域一直刷新這里Name一直變化就是說明在插入數據時一直刷新參考LVM_GETNEXTITEMLVM_SETITEMCOUNTListView_SetItemCountListView_SetItemCountEx使用虛擬列表視圖來解決新增大量數據卡頓問題關于列表視圖控件