CListCtrl用法!

CListView是那样简单好用,以至于咱们爱它就好像老鼠爱大米一样。可是你是否知道它的控制类CListCtrl有很多雷区呢?尤其当 Items非常大时(5000以上),对CListCtrl的用法不当,会导致CListView的加载明显延迟。来看看下面载入逗号分隔文本CSV的常见用法:


--------------------------------------------------------------------------------

void CRecordDoc::LoadFileCSV(CStdioFile& file_Open,
                                                  CListCtrl& openListCtrl, int& ReadNum)
{

       ReadNum = 0;
      
       int nSubItem = 0;
       int nStart = 0, nEnd = 0;
       CString strTempOpen;
       const TCHAR tcSplit = _T(',');
       while(1)
       {
              if(file_Open.ReadString(strTempOpen)==FALSE) break;
             
              for (nSubItem = nStart = 0; nSubItem < 11; nSubItem++)
              {
                     nEnd = strTempOpen.Find(tcSplit, nStart);
                     if (nEnd == -1)
                     {
                            break;
                     }
 
                     if (nSubItem == 0)
                     {
                            ReadNum ++;
                            openListCtrl.InsertItem(0, strTempOpen.Mid(nStart, nEnd - nStart));
                     }
                     else
                     {
                            openListCtrl.SetItemText(0, nSubItem, strTempOpen.Mid(nStart, nEnd - nStart));
                     }
 
                     nStart = nEnd + 1;
              }
       };
 }

--------------------------------------------------------------------------------

看出问题了么?

优化1:巧用CListCtrl::SetRedraw(BOOL bRedraw)函数,性能提升75%


--------------------------------------------------------------------------------

void CRecordDoc::LoadFileCSV(CStdioFile& file_Open,
                                                  CListCtrl& openListCtrl, int& ReadNum)
{
       SetCursor(LoadCursor(NULL, IDC_WAIT));
       openListCtrl.SetRedraw(FALSE);
      
       ReadNum = 0;
      
      int nSubItem = 0;
       int nStart = 0, nEnd = 0;
       CString strTempOpen;
       const TCHAR tcSplit = _T(',');
       while(1)
       {
              if(file_Open.ReadString(strTempOpen)==FALSE) break;
             
              for (nSubItem = nStart = 0; nSubItem < 11; nSubItem++)
              {
                     nEnd = strTempOpen.Find(tcSplit, nStart);
                     if (nEnd == -1)
                     {
                            break;
                     }
 
                     if (nSubItem == 0)
                     {
                            ReadNum ++;
                            openListCtrl.InsertItem(0, strTempOpen.Mid(nStart, nEnd - nStart));
                     }
                     else
                     {
                            openListCtrl.SetItemText(0, nSubItem, strTempOpen.Mid(nStart, nEnd - nStart));
                     }
 
                     nStart = nEnd + 1;
              }
       };
      
       openListCtrl.SetRedraw(TRUE);
       SetCursor(LoadCursor(NULL, IDC_ARROW));
}

--------------------------------------------------------------------------------

不卖关子了,接着来

优化2:新纪录项追加在List底部,性能提高35%


--------------------------------------------------------------------------------

void CRecordDoc::LoadFileCSV(CStdioFile& file_Open,
                                                  CListCtrl& openListCtrl, int& ReadNum)
{
       SetCursor(LoadCursor(NULL, IDC_WAIT));
       openListCtrl.SetRedraw(FALSE);
      
        ReadNum = 0;
      
       int nSubItem = 0;
       int nRootItem = -1;//插入项
       int nStart = 0, nEnd = 0;
       CString strTempOpen;
       const TCHAR tcSplit = _T(',');
       while(1)
       {
              if(file_Open.ReadString(strTempOpen)==FALSE) break;
             
              for (nSubItem = nStart = 0; nSubItem < 11; nSubItem++)
              {
                     nEnd = strTempOpen.Find(tcSplit, nStart);
                     if (nEnd == -1)
                     {
                            break;
                     }
 
                     if (nSubItem == 0)
                     {
                            nRootItem ++;
                            openListCtrl.InsertItem(nRootItem, strTempOpen.Mid(nStart, nEnd - nStart));
                     }
                     else
                     {
                            openListCtrl.SetItemText(nRootItem, nSubItem, strTempOpen.Mid(nStart, nEnd - nStart));
                     }
 
                     nStart = nEnd + 1;
              }
       };
       ReadNum = nRootItem + 1;
      
       openListCtrl.SetRedraw(TRUE);
       SetCursor(LoadCursor(NULL, IDC_ARROW));
}
//优化3:删除某些选择行的数据,从后往前删,并且删之前把进度条置顶,并关刷新,性能提升80%


--------------------------------------------------------------------------------
 
void CRecordDoc::DeleteCSV(CListCtrl& openListCtrl)
{
 
        SetCursor(LoadCursor(NULL, IDC_WAIT));
        openListCtrl.SetRedraw(FALSE);
        ::SendMessage(openListCtrl.m_hWnd, WM_VSCROLL, SB_TOP, 0);
      
       DWORD dwNum = openListCtrl.GetItemCount();
        for (int i = dwNum - 1; i > 0; i--)
       {
              if(openListCtrl.GetCheck(i))
                      openListCtrl.DeleteItem(i);
       }
       //或者这里是openListCtrl.DeleteAllItems();
       openListCtrl.SetRedraw(TRUE);
       SetCursor(LoadCursor(NULL, IDC_ARROW));
}

--------------------------------------------------------------------------------
 如何解决CListCtrl的数据更新时候的闪烁问题
方案1:窗口锁定技术。
m_list.LockWindowUpdate()
while()
  {
    //取出数据,插入到数据库中。
}
m_list.UnlockWindowUpdate()
程序在每次循环的时候不闪烁了,但是每个查询周期还是闪烁。
方案2:   不知道是否用对,每个查询周期还是要闪烁一次
SetWindowRedraw(hwnd,   FALSE);
    //过程同上
SetWindowRedraw(hwnd,   TRUE);
方案3:虚拟列表技术
  codePrject上下载的例子是针对一次插入大容量数据时候的不闪烁技术。更改以后,
每个周期改变数组的值,达到了要求。还是存在一些问题:
(1)虚拟列表技术必须要CListCtrl控件设置成自绘。原来的一个类用来设置每行的背景
        颜色,不能用了。是在CodePrject上下载了一个CListCtrl例子,类CReportCtrl例子,
        二者不能融合。调试时候发现是OnCustomdrawMyList   ()函数中错误?
          它使用了消息反射技术,如函数
          OnCustomdrawMyList   (   NMHDR*   pNMHDR,   LRESULT*   pResult   ),不知道和虚拟列表
        技术使用的LVN_GETDISPINFO消息(对应函数OnGetdispinfoList(NMHDR*   pNMHDR,     
          LRESULT*   pResult))二者是怎么样的对应关系?

原文地址:https://www.cnblogs.com/liyanwei/p/2002960.html