SubItem Selection in List Control改进版

本文解决了使用NM_CUSTOMDRAW自绘ListControl时遇到的“花屏”问题,并通过捕捉HDN_ITEMCHANGED消息来减少闪烁,同时介绍了如何通过继承CHeaderCtrl类来实现这一目的。
近期把List Control扩展成Grid,参考了两篇关于SubItem Selection的文章,一篇来自 Piotr Szewczyk.. 采用NM_CUSTOMDRAW方法,另一篇来自 Muhammad Azam. 采用LVS_OWNERDRAWFIXED。我个人偏好于NM_CUSTOMDRAW,因为NM_CUSTOMDRAW可以控制是否自绘,可以控制自绘区域(CDDS_ITEMPREPAINT和CDDS_SUBITEM),详细内容请参阅 Neat Stuff to do in List Controls Using Custom Draw
      但两篇文章都存在一个问题: DrawText 时总是用 DT_LEFT ,因为我为每个 Column 都设了 Alignment ,结果是每列都是 Align Left 。于是我用作了修改, DrawText 时分别对应每个 Column Alignment ,但是问题又出现了。
      我用NM_CUSTOMDRAW方法,当移动Column Header时,如果该列的Alignment为Center或Right时,该列的内容就出现“花屏”现象,原因是作者为了防止闪烁,采用以下方法:
 
void CMyList::InvalidateCell(int nRow, int nCol)
{
       //I add this function to reduce flickering
    CRect rc;
    if(nCol==0)
        GetItemRect(nRow,&rc,LVIR_LABEL);
    else
        GetSubItemRect(nRow,nCol,LVIR_BOUNDS,rc);
    InvalidateRect(&rc);
}     
程序中保存两个变量m_nRow,m_nCol,当变量的值改变了就会调用InvalidateCell(int nRow, int nCol)。而当我拖动Column Header时不会调用InvalidateCell(int nRow, int nCol)。
解决方法当然是捕获拖动Column Header时的消息,查看了相关资料,相关的消息是HDN_ITEMCHANGED:拖动过程中不断收到
HDN_BEGINTRACK:拖到开始时
EDN_ENDTRACK:拖动结束时
       那些消息是从CHeaderCtrl反馈回来的。(注:CHeaderCtrl有两种获得方法:一种是CListCtrl::GetHeaderCtrl(),一种是CListCtrl::GetDlgItem(0)。)
       我的CMyList是继承自CListCtrl,不论我怎样设定风格,如CMyList->GetHeaderCtrl()->ModifyStyle(0, HDS_HOTTRACK),还是捕获不了HDN_ITEMCHANGED,其它消息也捕不了。
       最后的办法只有设计一个类CMyHeaderCtrl继承自CHeaderCtrl,然后在CMyList的SubclassHeaderControl加入以下代码:
       CHeaderCtrl* pHeader = GetHeaderCtrl();
       if (pHeader)
       {
              VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));
       }
       因为ClassView中没有HDN_ITEMCHANGED所以要自己加
       ON_NOTIFY_REFLECT(HDN_ITEMCHANGED, OnItemChanged)
 
以下是OnItemChanged的实现
void CMyHeaderCtrl::OnItemChanged(NMHDR * pNMHDR, LRESULT* pResult)
{
       *pResult = 0;
 
       CMyList* pzList = static_cast<CMyList*>(GetParent());
       ASSERT(pzList);
 
       NMHEADER *pHdr = (NMHEADER*)pNMHDR;
 
       int nCol;//The changed column
       int topRow,bottomRow;//The visit top row and bottom row
 
       nCol = pHdr->iItem;
 
       if( pzList->Cols[nCol].getTextAlignFixed() == AlignLeft)
              return;//因为AlignLeft不存在“花屏”现象
 
       if( 0 == pzList->GetItemCount())
              return;
 
       topRow = pzList->GetTopIndex();
       bottomRow = pzList->GetCountPerPage();
 
       CRect rc1,rc2;
 
       pzList->GetSubItemRect(topRow,nCol,LVIR_BOUNDS,rc1);
       pzList->GetSubItemRect(bottomRow,nCol,LVIR_BOUNDS,rc2);
 
       if( 0 == nCol)
       {
              rc1.left = 0;
              rc2.left = 0;
              rc1.right = pzList->GetColumnWidth(0);
              rc2.right = rc1.right;
       }
       rc1.bottom = rc2.bottom;
 
       pzList->InvalidateRect(&rc1);
       TRACE("ItemChanged/n");
}
       至此,虽然问题已解决,重画区域已减到最小,但也存在小小的闪烁,但比Invalidate好多了。
       一些问题也是想不通:
1.  为什么继承的CListCtrl不能收到CHeaderCtrl的Trace消息?
2.  的Column为什么不会出现“花屏”现象?AlignLeft
 
今天收获很大,虽然最后也要动用到继承CHeaderCtrl,但也不失为好事,因为迟早也要继承CHeaderCtrl才能完成一些功能的,如禁止改变列宽,在列上画排序箭头。
参考资料:
Matt Weagle Using the Header Control
 
 
// ChangeRequest.cpp : 实现文件 // #include "stdafx.h" #include "DrawingCheck.h" #include "ChangeRequest.h" #include "afxdialogex.h" // ChangeRequest 对话框 IMPLEMENT_DYNAMIC(ChangeRequest, CDialogEx) ChangeRequest::ChangeRequest(CWnd* pParent /*=NULL*/) : CDialogEx(IDD_DIALOG2, pParent) { } ChangeRequest::~ChangeRequest() { } void ChangeRequest::SetListDatas(vector<CString> Datas) { listDatas = Datas; } void ChangeRequest::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_list); } BEGIN_MESSAGE_MAP(ChangeRequest, CDialogEx) ON_NOTIFY(NM_CLICK, IDC_LIST1, &ChangeRequest::OnLvnItemClickList) ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST1, &ChangeRequest::OnNMCustomdrawList1) END_MESSAGE_MAP() BOOL ChangeRequest::OnInitDialog() { CDialog::OnInitDialog(); DWORD dwStyle = m_list.GetExtendedStyle(); dwStyle |= LVS_EX_ONECLICKACTIVATE; dwStyle |= LVS_EX_FLATSB; //扁平风格的滚动条 dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮 dwStyle |= LVS_EX_GRIDLINES;//网格线 CImageList high; high.Create(1, 30, TRUE | ILC_COLOR32, 1, 1);//行高 m_list.SetImageList(&high, LVSIL_SMALL); m_list.SetExtendedStyle(dwStyle); //设置扩展风格 m_list.InsertColumn(0, _T("序号"), LVCFMT_CENTER, 38); m_list.InsertColumn(1, _T("跳转变更申请"), LVCFMT_CENTER, 100); m_list.InsertColumn(2, _T("链接"), LVCFMT_CENTER, 0); int size = listDatas.size(); for (int i = 0; i < size; i++) { CString aStrNo; aStrNo.Format(L"%d", i + 1); m_list.EnsureVisible(i, TRUE); m_list.InsertItem(i, aStrNo); } map<CString, CString>::iterator it; for (int i=0;i< listDatas.size(); i++) { m_list.SetItemText(i, 1, L"跳转"); m_list.SetItemText(i, 2, listDatas[i]); } return 0; } void ChangeRequest::OnLvnItemClickList(NMHDR *pNMHDR, LRESULT *pResult) { LPNMLISTVIEW pNMItemActivate = reinterpret_cast<LPNMLISTVIEW>(pNMHDR); int m_nSel; int iCol; if (pNMItemActivate != NULL) { m_nSel = pNMItemActivate->iItem; iCol = pNMItemActivate->iSubItem; } if (m_nSel != -1 && iCol == 1)//跳转 { CString strPath; strPath = m_list.GetItemText(m_nSel, 2); ShellExecute(NULL, NULL, strPath, NULL, NULL, SW_SHOWNORMAL); } *pResult = 0; } void ChangeRequest::OnNMCustomdrawList1(NMHDR * pNMHDR, LRESULT * pResult) { NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR); NM_LISTVIEW * pNMListView = (NM_LISTVIEW*)pNMHDR; CRect rect; CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); // 将设备上下文句柄转换为设备上下文类 *pResult = CDRF_DODEFAULT; if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage) { *pResult = CDRF_NOTIFYITEMDRAW; } else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage) { *pResult = CDRF_NOTIFYSUBITEMDRAW; } else if ((CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage) { COLORREF crText, crBkgnd; if (1 == pLVCD->iSubItem) { crText = RGB(24, 144, 255); crBkgnd = RGB(255, 255, 255); } else { crText = RGB(0, 0, 0); crBkgnd = RGB(255, 255, 255); } pLVCD->clrText = crText; pLVCD->clrTextBk = crBkgnd; } } 将该代码转为QT框架,必须保持原有逻辑,不要增减任何功能或代码,同时需要与ChangeRequest.h联动
最新发布
08-26
// ChangeRequest.cpp : 实现文件 // #include “stdafx.h” #include “DrawingCheck.h” #include “ChangeRequest.h” #include “afxdialogex.h” // ChangeRequest 对话框 IMPLEMENT_DYNAMIC(ChangeRequest, CDialogEx) ChangeRequest::ChangeRequest(CWnd* pParent /=NULL/) CDialogEx(IDD_DIALOG2, pParent) { } ChangeRequest::~ChangeRequest() { } void ChangeRequest::SetListDatas(vector Datas) { listDatas = Datas; } void ChangeRequest::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_list); } BEGIN_MESSAGE_MAP(ChangeRequest, CDialogEx) ON_NOTIFY(NM_CLICK, IDC_LIST1, &ChangeRequest::OnLvnItemClickList) ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST1, &ChangeRequest::OnNMCustomdrawList1) END_MESSAGE_MAP() BOOL ChangeRequest::OnInitDialog() { CDialog::OnInitDialog(); DWORD dwStyle = m_list.GetExtendedStyle(); dwStyle |= LVS_EX_ONECLICKACTIVATE; dwStyle |= LVS_EX_FLATSB; //扁平风格的滚动条 dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮 dwStyle |= LVS_EX_GRIDLINES;//网格线 CImageList high; high.Create(1, 30, TRUE | ILC_COLOR32, 1, 1);//行高 m_list.SetImageList(&high, LVSIL_SMALL); m_list.SetExtendedStyle(dwStyle); //设置扩展风格 m_list.InsertColumn(0, _T("序号"), LVCFMT_CENTER, 38); m_list.InsertColumn(1, _T("跳转变更申请"), LVCFMT_CENTER, 100); m_list.InsertColumn(2, _T("链接"), LVCFMT_CENTER, 0); int size = listDatas.size(); for (int i = 0; i < size; i++) { CString aStrNo; aStrNo.Format(L"%d", i + 1); m_list.EnsureVisible(i, TRUE); m_list.InsertItem(i, aStrNo); } map<CString, CString>::iterator it; for (int i=0;i< listDatas.size(); i++) { m_list.SetItemText(i, 1, L"跳转"); m_list.SetItemText(i, 2, listDatas[i]); } return 0; } void ChangeRequest::OnLvnItemClickList(NMHDR *pNMHDR, LRESULT *pResult) { LPNMLISTVIEW pNMItemActivate = reinterpret_cast(pNMHDR); int m_nSel; int iCol; if (pNMItemActivate != NULL) { m_nSel = pNMItemActivate->iItem; iCol = pNMItemActivate->iSubItem; } if (m_nSel != -1 && iCol == 1)//跳转 { CString strPath; strPath = m_list.GetItemText(m_nSel, 2); ShellExecute(NULL, NULL, strPath, NULL, NULL, SW_SHOWNORMAL); } *pResult = 0; } void ChangeRequest::OnNMCustomdrawList1(NMHDR * pNMHDR, LRESULT * pResult) { NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR); NM_LISTVIEW * pNMListView = (NM_LISTVIEW*)pNMHDR; CRect rect; CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); // 将设备上下文句柄转换为设备上下文类 *pResult = CDRF_DODEFAULT; if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage) { *pResult = CDRF_NOTIFYITEMDRAW; } else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage) { *pResult = CDRF_NOTIFYSUBITEMDRAW; } else if ((CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage) { COLORREF crText, crBkgnd; if (1 == pLVCD->iSubItem) { crText = RGB(24, 144, 255); crBkgnd = RGB(255, 255, 255); } else { crText = RGB(0, 0, 0); crBkgnd = RGB(255, 255, 255); } pLVCD->clrText = crText; pLVCD->clrTextBk = crBkgnd; } },修改该代码,保持原有逻辑转为QT框架,其中控件替代: CListCtrl → QModelIndex checkIndex = model->index(i, 0); Qt::CheckState checkState = static_castQt::CheckState( model->data(checkIndex, Qt::CheckStateRole).toInt());
08-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值