CListCtrl
CListCtrl所在的对话框是代码创建,该对话框中动态创建了一个ClistCtrl的子类,
结果不能响应消息WM_DRAWITEM,不能触发虚函数DrawItem.于是我在对话框(父窗口)messagemap中加入了
ON_WM_DRAWITEM()
ON_WM_MEASUREITEM()
ON_WM_MEASUREITEM_REFLECT()
//CListCtrl子类
//precreatewindow中加入LVS_OWNERDRAWFIXED属性,不能在PreSubclassWindow中修改属性,试过,修改了依然不会触发ON_WM_DRAWITEM
BOOL CMyListCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此添加专用代码和/或调用基类
//ModifyStyle(0, LVS_OWNERDRAWFIXED);
cs.style |= LVS_OWNERDRAWFIXED;
return CListCtrl::PreCreateWindow(cs);
}
//对话框
//对话框中的drawitem消息转发给ClistCtrl
void CourseSituation::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (LIST_CTRL_ID == nIDCtl)
{
::SendMessage(GetDlgItem(LIST_CTRL_ID)->GetSafeHwnd(), WM_DRAWITEM, nIDCtl, (LPARAM)lpDrawItemStruct);
}
}
ClistCtrl子类中这才从WinndowProc中收到WM_ITEM消息,然后手动调用了DrawItem函数,这个函数本不是消息响应函数,而是继承自ClistCtrl的虚函数,应该会自动调用的,不知道跟我的对话框有没有关系,对话框中虽然触发了ON_WM_MEASUREITEM,但是即使继续传递给CListCtrl子类,依然没能改变行高。ON_WM_MEASUREITEM_REFLECT始终无响应。很纠结,明明可以重绘单元格了,但是改变不了高度。应该是调用的时机不对,我在InsertColumn、setColumn之后调用setRowHeight(子类封装的接口),然后行高果然改变了
void CListCtrlCl::SetRowHeight(int nHeight) // 设置行高
{
m_nRowHeight = nHeight;
CRect rcWin;
GetWindowRect(&rcWin);
WINDOWPOS wp;
wp.hwnd = m_hWnd;
wp.cx = rcWin.Width();
wp.cy = rcWin.Height();
wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp);
}
void CListCtrlCl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if (m_nRowHeight > 0)
{
lpMeasureItemStruct->itemHeight = m_nRowHeight;
}
}
LRESULT CListCtrlCl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_MEASUREITEM:
{
MeasureItem((LPMEASUREITEMSTRUCT)lParam);
CListCtrl::OnMeasureItem(wParam, (LPMEASUREITEMSTRUCT)lParam);
break;
}
default:
break;
}
return CListCtrl::DefWindowProc(message, wParam, lParam);
}
//父窗口messagemap中加入ON_WM_MEASUREITEM()
void CourseSituation::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
//判断是不是我的ClistCtrl控件
if (LIST_CTRL_ID == nIDCtl)
{
::SendMessage(GetDlgItem(LIST_CTRL_ID)->GetSafeHwnd(), WM_MEASUREITEM, nIDCtl, (LPARAM)lpMeasureItemStruct);
}
}
//设置第0列文字居中,微软神经病,第0列就是不让居中,insetColumn的时候设置了居中都不行,添加如下代码可以居中了
LVCOLUMN lvc;
lvc.mask = LVCF_FMT;
m_pList->GetColumn(0, &lvc);
lvc.fmt &= ~LVCFMT_JUSTIFYMASK;
lvc.fmt |= LVCFMT_CENTER;
m_pList->SetColumn(0, &lvc);
//微软官方的建议是:添加一个空的第0列,然后删除,这样开始添加的第1列会变成第0列,这么傻缺的方法,越来越觉得微软欺负人,还不改进//设置第0列不显示图片,其他列可以显示图片
微软神经病,如果设置了imageList,第0列默认就有图片,即使设置纯文本列,其他列默认没图片,即使设置了图片属性
m_pList->SetExtendedStyle(LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP);//LVS_EX_SUBITEMIMAGES设置其他列也可显示图片,LVS_EX_HEADERDRAGDROP设置列可拖动,只是一个效果,还可能影响布局
要想第0列不显示图片,有两种方法:
1.添加一个空的第0列(虽然也会显示图片),然后将其宽度设置为0,再禁止修改列宽(禁止修改列宽我的做法是禁止标题头的窗口m_pList->GetHeaderCtrl()->EnableWindow(FALSE))
2.加入有多列,把第一个要显示图片的列作为第0列,把不想显示图片,但是要第一个显示的作为第1列,然后交换第0列和第1列
//空列就不多说了,
//交换列主要用到如下代码
//其中1可以指定为其他数值,自己理解,就是给标题头换序,列的序号还展示的位置本身并无关系
void CourseSituation::changeListColumn()
{
CHeaderCtrl *pmyHeaderCtrl = m_pList->GetHeaderCtrl();
int nCount = pmyHeaderCtrl->GetItemCount();
LPINT pnOrder = (LPINT)malloc(nCount * sizeof(int));
ASSERT(pnOrder != NULL);
pmyHeaderCtrl->GetOrderArray(pnOrder, nCount);
int nTemp;
nTemp = pnOrder[0];
pnOrder[0] = pnOrder[1];
pnOrder[1] = nTemp;
pmyHeaderCtrl->SetOrderArray(nCount, pnOrder);
free(pnOrder);
}
//设置列表图片,非得指定大小一样,还得自己拉伸、缩放
//根据路径,获取图片,缩放图片
CBitmap* getStretchPng(const TCHAR* ptszPath, int newWidth, int newHeight)
{
Gdiplus::Image img(ptszPath);
Gdiplus::Bitmap *pNewImage = (Gdiplus::Bitmap*)img.GetThumbnailImage(newWidth, newHeight, NULL, NULL);
HBITMAP hBmp;
pNewImage->GetHBITMAP(Color(255, 255, 255), &hBmp);
return CBitmap::FromHandle(hBmp);
}
//设置所有要加入列表框的图片,然后设置到imagelist中去
void CourseSituation::initListIcon()
{
const TCHAR* icons[ICON_COUNT]= {
_T(".\\res\\1.png"),
_T(".\\res\\2.png"),
_T(".\\res\\3.png"),
_T(".\\res\\4.png"),
_T(".\\res\\5.png"),
_T(".\\res\\6.png"),
_T(".\\res\\7.png"),
_T(".\\res\\8.png"),
_T(".\\res\\9.png"),
_T(".\\res\\10.png")
};
for (int i = 0; i < ICON_COUNT; ++i)
{
CBitmap *bitmap = getStretchPng(icons[i], LIST_IMAGE_WIDTH, LIST_IMAGE_WIDTH);
int iRet = m_pImageList->Add(bitmap, RGB(0xff, 0xff, 0xff));
m_iconIndex[i] = iRet;//设置LVITEM用的索引,可以这么理解
bitmap->DeleteObject();
}
}
//初始化列表的图片集
m_pImageList = new CImageList();
m_pImageList->Create(LIST_IMAGE_WIDTH, LIST_IMAGE_WIDTH, ILC_COLOR24 | ILC_MASK, 8, 1);
initListIcon();
m_pList->SetImageList(m_pImageList, LVSIL_SMALL);//插入行的办法,insertItem,插入一行的第一个元素,这一行的其他元素要使用setItem。
void CourseSituation::addStudent(STUDENT_INFO* pSs)
{
int listindex, iColumn = 0, iImageIndex = 0;
LV_ITEM lvItem;
listindex = m_pList->GetItemCount();
TCHAR *ptszText = new TCHAR[100];
//第一列和第二列进行了交换。所以上下台变成了第一列,学生姓名变成了第二列,但是显示学生姓名第一列,上下台第二列,显示的列号不是列索引
iImageIndex = ICON_STAGE_INDEX;
_stprintf(ptszText, _T("x%d"), pSs->numOfHandPicked);
lvItem.mask = LVIF_IMAGE | LVIF_TEXT;
lvItem.iItem = listindex;
lvItem.iSubItem = iColumn++;
lvItem.iImage = m_iconIndex[iImageIndex];
lvItem.pszText = ptszText;
m_pList->InsertItem(&lvItem);//新插入的一行,第一个元素要insertItem
//学生姓名
_tcscpy(ptszText, _T("微软死敌"));
lvItem.mask = LVIF_TEXT;
lvItem.iItem = listindex;
lvItem.iSubItem = iColumn++;
lvItem.iImage = 0;
lvItem.pszText = ptszText;
m_pList->SetItem(&lvItem);//不要写成pList->InsertItem(0, ptszText));//会导致除了第一列,其他列只显示最后一行
//权限
if (pSs->isWritingEnable)
{
iImageIndex = ICON_AUTH_INDEX;
}
else
{
iImageIndex = ICON_NO_AUTH_INDEX;
}
lvItem.mask = LVIF_IMAGE;
lvItem.iItem = listindex;
lvItem.iSubItem = iColumn++;
lvItem.iImage = m_iconIndex[iImageIndex];
lvItem.pszText = NULL;
m_pList->SetItem(&lvItem);
}
//不继承CListCtrl自绘单元格,可扩展性不高,但是好歹能稍微有点样式
1.不同行用不同颜色
void CourseSituation::OnCustomDrawList(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
*pResult = 0;
if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
{
//通知mfc,告诉我接下来要传入的消息
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage)
{
//准备画图之前,设置文本颜色
COLORREF crText;
if ((pLVCD->nmcd.dwItemSpec % 3) == 0)
crText = RGB(255, 0, 0);
else if ((pLVCD->nmcd.dwItemSpec % 3) == 1)
crText = RGB(0, 255, 0);
else
crText = RGB(128, 128, 255);
//修改结构体
pLVCD->clrText = crText;
//告诉控件,自己画
*pResult = CDRF_DODEFAULT;
}
}
2.不同行、列用不同背景色
void CourseSituation::OnCustomDrawList(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
*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;
int nItem = static_cast<int>(pLVCD->nmcd.dwItemSpec);
//第几行
if (0 == nItem)
{
crText = RGB(255, 0, 0);
crBkgnd = RGB(128, 128, 255);
}
else
{
crText = RGB(128, 128, 255);
crBkgnd = RGB(0xfa, 0xfa, 0xfa);
}
//设置了颜色背景色也怪怪的,因为如果单元格中有图片,图片会像隔断一样阻止背景色铺满整行。
pLVCD->clrText = crText;
pLVCD->clrTextBk = crBkgnd;
*pResult = CDRF_DODEFAULT;
}
}
3.自己画图
void CourseSituation::OnCustomDrawList(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
*pResult = 0;
if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage)
{
// This is the pre-paint stage for an item. We need to make another
// request to be notified during the post-paint stage.
LVITEM rItem;
int nItem = static_cast<int>(pLVCD->nmcd.dwItemSpec);
// Get the image index and state of this item. Note that we need to
// check the selected state manually. The docs _say_ that the
// item's state is in pLVCD->nmcd.uItemState, but during my testing
// it was always equal to 0x0201, which doesn't make sense, since
// the max CDIS_ constant in commctrl.h is 0x0100.
ZeroMemory(&rItem, sizeof(LVITEM));
//rItem.mask = LVIF_IMAGE ;
rItem.iItem = nItem;
rItem.iSubItem = 2;
//rItem.stateMask = LVIS_SELECTED;
m_pList->GetItem(&rItem);
// If this item is selected, redraw the icon with its normal colors.
if (rItem.state & LVIS_SELECTED)
{
CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect rcIcon;
// Get the rect that holds the item's icon.
m_pList->GetItemRect(nItem, &rcIcon, LVIR_ICON);
// Draw the icon.
m_pImageList->Draw(pDC, rItem.iImage, CPoint(rcIcon.left + 30, rcIcon.top), //rcIcon.TopLeft(),
ILD_TRANSPARENT);
}
else
{
static int iLine = 1;
CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect rcIcon;
// Get the rect that holds the item's icon.
m_pList->GetSubItemRect(nItem, 2, LVIR_BOUNDS, rcIcon);
// Draw the icon.
m_pImageList->Draw(pDC, rItem.iImage, CPoint(rcIcon.left + 30, rcIcon.top), //rcIcon.TopLeft(),
ILD_TRANSPARENT);
}
//*pResult = CDRF_NOTIFYPOSTPAINT;
*pResult = CDRF_SKIPDEFAULT;
}
else if (CDDS_ITEMPOSTPAINT == pLVCD->nmcd.dwDrawStage)
{
If this item is selected, re-draw the icon in its normal
color (not blended with the highlight color).
//LVITEM rItem;
//int nItem = static_cast<int>(pLVCD->nmcd.dwItemSpec);
//CommonLog::getInstance()->WriteLog(LogLevel_debug, _T("%d item post paint rc %d,%d,%d,%d"),
// nItem,
// pLVCD->nmcd.rc.left, pLVCD->nmcd.rc.top, pLVCD->nmcd.rc.right, pLVCD->nmcd.rc.bottom);
Get the image index and state of this item. Note that we need to
check the selected state manually. The docs _say_ that the
item's state is in pLVCD->nmcd.uItemState, but during my testing
it was always equal to 0x0201, which doesn't make sense, since
the max CDIS_ constant in commctrl.h is 0x0100.
//ZeroMemory(&rItem, sizeof(LVITEM));
rItem.mask = LVIF_IMAGE ;
//rItem.iItem = nItem;
//rItem.iSubItem = 2;
rItem.stateMask = LVIS_SELECTED;
//m_pList->GetItem(&rItem);
If this item is selected, redraw the icon with its normal colors.
//if (rItem.state & LVIS_SELECTED)
//{
// CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
// CRect rcIcon;
// // Get the rect that holds the item's icon.
// m_pList->GetItemRect(nItem, &rcIcon, LVIR_ICON);
// // Draw the icon.
// m_pImageList->Draw(pDC, rItem.iImage, CPoint(rcIcon.left + 30, rcIcon.top), //rcIcon.TopLeft(),
// ILD_TRANSPARENT);
// *pResult = CDRF_SKIPDEFAULT;
//}
//else
//{
// static int iLine = 1;
// CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
// CRect rcIcon;
// // Get the rect that holds the item's icon.
// m_pList->GetSubItemRect(nItem, 2, LVIR_BOUNDS, rcIcon);
// // Draw the icon.
// m_pImageList->Draw(pDC, rItem.iImage, CPoint(rcIcon.left + 30, rcIcon.top), //rcIcon.TopLeft(),
// ILD_TRANSPARENT);
//}
}
}