MFC CListCtrl自绘单元格,设置行高

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);
        //}
    }
}


 

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值