MFC自绘-WzdList列表类

列表分为列表头和列表项两个部分。
从列表头开始自绘。

class CWzdListHeader : public CHeaderCtrl
{
    DECLARE_DYNAMIC(CWzdListHeader)

public:
    CWzdListHeader();
    virtual ~CWzdListHeader();

    // 添加字段
    void AddColumn(int nCol, LPCTSTR sName, int nFormat);

protected:
    afx_msg void OnPaint();
    LRESULT OnLayout(WPARAM wParam, LPARAM lParam);
    DECLARE_MESSAGE_MAP()
};

可以看到列表头自绘很简单,OnPaint里进行绘画,其中列表头的各个字段的显示属性在AddColumn函数中保存到一个multimap中,以便于绘画的时候一一对应。
AddColumn函数会在下面的InsertColumn中用到。

void CWzdListHeader::OnPaint()
{
    CPaintDC dc(this); // device context for painting

    CRect itemRect;
    CBrush colomnBrush;     // Column画刷
    CPen linePen;       // 分隔线画刷
    CPen* pOldPen;
    colomnBrush.CreateSolidBrush(COLOR_LISTHEAR);
    linePen.CreatePen(PS_SOLID, 1, COLOR_WHITE);
    pOldPen = dc.SelectObject(&linePen);

    // 画出所有字段
    int nItem = GetItemCount(); // 得到单元数量
    int i = 0;
    std::multimap<int, std::pair<LPCTSTR, int>>::iterator itor = g_columeMap.begin();
    while (itor != g_columeMap.end() && i < nItem)
    {
        // 画分隔线,填充背景
        GetItemRect(i, &itemRect);
        if (0 != i)
        {
            dc.MoveTo(CPoint(itemRect.left, itemRect.top));
            dc.LineTo(CPoint(itemRect.left, itemRect.bottom));
            itemRect.left += 1;
        }
        dc.FillRect(&itemRect,&colomnBrush);

        // 创建字体
        CFont textFont;
        CFont* pOldFont;
        dc.SetBkMode(TRANSPARENT);
        dc.SetTextColor(RGB(0,0,0));
        textFont.CreateFont(20,0,0,0,0,FALSE,FALSE,0,0,0,0,0,0, DEFAULT_TEXTFACE);
        pOldFont = dc.SelectObject(&textFont);

        // 画字
        TEXTMETRIC metric;
        int ofst = 0;
        dc.GetTextMetrics(&metric);
        ofst = itemRect.Height() - metric.tmHeight;
        itemRect.OffsetRect(0, ofst / 2);
        dc.DrawText(itor->second.first, &itemRect, itor->second.second);

        dc.SelectObject(pOldFont);
        textFont.DeleteObject();
        itor++;
        i++;
    }

    //画头部剩余部分
    CRect lastRect;
    GetClientRect(&lastRect);
    lastRect.left = itemRect.right + 1;
    dc.MoveTo(CPoint(itemRect.right, lastRect.top));
    dc.LineTo(CPoint(itemRect.right, itemRect.bottom));
    dc.FillRect(&lastRect, &colomnBrush);

    colomnBrush.DeleteObject();
    linePen.DeleteObject();
    dc.SelectObject(pOldPen);
}

然后就是列表项的自绘

class CWzdList : public CListCtrl
{
    DECLARE_DYNAMIC(CWzdList)

public:
    CWzdList();
    virtual ~CWzdList();

    int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, int nSubItem = -1);

protected:
    CWzdListHeader m_listHeader;

    virtual void PreSubclassWindow();
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

    DECLARE_MESSAGE_MAP()
};

PreSubclassWindow和OnCreate都是用来在创建之前改变列表的风格,前者是绑定的时候调用,后者是动态创建的时候调用。

void CWzdList::PreSubclassWindow()
{

    SetWindowLong(this->m_hWnd, GWL_STYLE, GetStyle() | LVS_OWNERDRAWFIXED | LVS_REPORT);
    SetExtendedStyle(GetExtendedStyle()| LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
    CHeaderCtrl *pHeader = GetHeaderCtrl();
    m_listHeader.SubclassWindow(pHeader->GetSafeHwnd());

    CListCtrl::PreSubclassWindow();
}

int CWzdList::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CListCtrl::OnCreate(lpCreateStruct) == -1)
        return -1;

    SetWindowLong(this->m_hWnd, GWL_STYLE, GetStyle() | LVS_OWNERDRAWFIXED | LVS_REPORT);
    SetExtendedStyle(GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
    CHeaderCtrl *pHeader = GetHeaderCtrl();
    m_listHeader.SubclassWindow(pHeader->GetSafeHwnd());

    return 0;
}

同样添加了LVS_EX_FULLROWSELECT 属性后,可以在DrawItem种进行自绘。

void CWzdList::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    LV_COLUMN lvColumn, lvColumnPre ;
    ZeroMemory(&lvColumn, sizeof(lvColumn));
    ZeroMemory(&lvColumnPre, sizeof(lvColumnPre));
    lvColumn.mask = LVCF_WIDTH | LVCF_FMT;
    lvColumnPre.mask = LVCF_WIDTH | LVCF_FMT;

    // 获取DC和列表尺寸
    CDC* pDC;
    CRect rtClient;
    pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    GetClientRect(&rtClient);

    std::multimap<int, std::pair<LPCTSTR, int>>::iterator itor = g_columeMap.begin();
    for (int nCol = 0; GetColumn(nCol, &lvColumn); nCol++, itor++)
    {
        // 获取列表项尺寸,全部LVIR_BOUNDS
        CRect rcItem;
        GetSubItemRect(lpDrawItemStruct->itemID, nCol, LVIR_BOUNDS, rcItem);

        // 初始化列表项, 获取列表项文字
        LV_ITEM lvItem;
        WCHAR lpBuffer[256];
        ZeroMemory(&lvItem, sizeof(lvItem));
        lvItem.iItem = lpDrawItemStruct->itemID;
        lvItem.mask = LVIF_TEXT | LVIF_PARAM;
        lvItem.iSubItem = nCol;
        lvItem.pszText = lpBuffer;
        lvItem.cchTextMax = sizeof(lpBuffer);
        VERIFY(GetItem(&lvItem));

        // 画选中和不选中的Item
        if (lpDrawItemStruct->itemState & ODS_SELECTED)
        {
            pDC->FillSolidRect(&rcItem, COLOR_LISTITEM_SELECT);
        }
        else
        {
            pDC->FillSolidRect(rcItem, COLOR_LISTITEM_NORMAL);
        }

        // 创建字体
        CFont nFont;
        CFont* pOldFont;
        nFont.CreateFont(17,0,0,0,0,FALSE,FALSE,0,0,0,0,0,0, DEFAULT_TEXTFACE);
        pOldFont = pDC->SelectObject(&nFont);

        // 获取列表项尺寸,文字部分LVIR_LABEL
        GetSubItemRect(lpDrawItemStruct->itemID, nCol, LVIR_LABEL, rcItem);
        // 画字
        CString strText(lpBuffer);
        int uFormat = itor->second.second;
        pDC->SetTextColor(COLOR_BLACK);
        pDC->DrawText(strText, strText.GetLength(), &rcItem, uFormat);

    }
}

最后重写InsertColumn来储存插入的列表头关键字属性

int CWzdList::InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat /*= LVCFMT_LEFT*/, int nWidth /*= -1*/, int nSubItem /*= -1*/)
{
    m_listHeader.AddColumn(nCol, lpszColumnHeading, nFormat);

    return CListCtrl::InsertColumn(nCol, lpszColumnHeading, nFormat, nWidth, nSubItem);
}

猜你喜欢

转载自blog.csdn.net/wizardtoh/article/details/48574671
今日推荐