• MFC中如何重绘CListCtrl的表头


    MFC中的CListCtrl实际上是由两个控件组成,一个是表头控件,一个是列表控件。有些时候,我们需要重绘表头,使其满足特定的场景要求。
    本文介绍了CListCtrl表头的重绘方法,自定义表头效果如下。
    在这里插入图片描述

    一、从CHeaderCtrl派生出自定义表头类CCustomHeader

    MFC中表示表头控件的类是CHeaderCtrl,我们从它派生一个新类CCustomHeader 。
    头文件为:

    class CCustomHeader : public CHeaderCtrl
    {
    	DECLARE_DYNAMIC(CCustomHeader)
    
    public:
    	CCustomHeader();
    	virtual ~CCustomHeader();
    	//设置表头单元格的对齐方式,参见DrawText函数中文字对齐格式
    	void SetTextAlign(UINT uFormat = DT_CENTER | DT_SINGLELINE | DT_VCENTER);
    
    protected:
    	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    	DECLARE_MESSAGE_MAP()
    
    private:
    	UINT m_nTextAlignFormat;	//表头文字对齐方式
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    实现文件

    // CCustomHeader.cpp: 实现文件
    //
    
    #include "pch.h"
    #include "TestCustomHeader.h"
    #include "CCustomHeader.h"
    
    
    // CCustomHeader
    
    IMPLEMENT_DYNAMIC(CCustomHeader, CHeaderCtrl)
    
    CCustomHeader::CCustomHeader()
    	:m_nTextAlignFormat(DT_CENTER | DT_SINGLELINE | DT_VCENTER)
    {}
    
    CCustomHeader::~CCustomHeader()
    {}
    
    void CCustomHeader::SetTextAlign(UINT uFormat)
    {
    	m_nTextAlignFormat = uFormat;
    }
    
    BEGIN_MESSAGE_MAP(CCustomHeader, CHeaderCtrl)
    END_MESSAGE_MAP()
    
    void CCustomHeader::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
    	ASSERT(lpDrawItemStruct->CtlType == ODT_HEADER);
    	HDITEM hdi;
    	TCHAR  lpBuffer[256];
    
    	hdi.mask = HDI_TEXT;
    	hdi.pszText = lpBuffer;
    	hdi.cchTextMax = 256;
    	GetItem(lpDrawItemStruct->itemID, &hdi);
    
    	CDC* pDC;
    	pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    	HGDIOBJ hOldFont = pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT));
    	
    	// 绘制按钮边框
    	::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH);
    
    	//绘制文本
    	::DrawText(lpDrawItemStruct->hDC, lpBuffer, _tcslen(lpBuffer),
    		&lpDrawItemStruct->rcItem, m_nTextAlignFormat);
    
    	pDC->SelectObject(hOldFont);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    请注意,在实现文件中,我们重写了DrawItem虚函数;因此当表头控件是自绘样式时,每次绘制都会调用DrawItem函数,在该函数中我们获取表头控件的DC,然后即可进行绘制。
    另请注意在DrawItem中使用的如下函数:

    BOOL GetItem( int nPos, HDITEM* pHeaderItem ) const;
    说明:该函数可以获取表头控件每一项的信息(Retrieves information about a header control item.)

    利用该函数,我们获取了表头中每一项的标题内容。

    二、使用自定义表头类CCustomHeader

    新建一个基于对话框的程序,在对话框类的头文件中增加:

    	CListCtrl m_wndLstMain;		//表示列表控件本身
    	CCustomHeader m_wndHeader;	//表示列表控件的表头控件
    
    • 1
    • 2

    在OnInitDialog函数中增加:

    BOOL CTestDlg::OnInitDialog()
    {
    	//.... 省略
    	
    	// TODO: 在此添加额外的初始化代码
    	CRect rect;
    	m_wndLstMain.GetClientRect(rect);
    	m_wndLstMain.InsertColumn(0, _T("姓名"), LVCFMT_LEFT, rect.Width() / 3);
    	m_wndLstMain.InsertColumn(1, _T("班级"), LVCFMT_LEFT, rect.Width() / 3);
    	m_wndLstMain.InsertColumn(2, _T("学号"), LVCFMT_LEFT, rect.Width() / 3);
    
    	m_wndLstMain.InsertItem(0, _T("张三"));
    	m_wndLstMain.SetItemText(0, 1, _T("一班"));
    	m_wndLstMain.SetItemText(0, 2, _T("001"));
    
    	m_wndLstMain.InsertItem(1, _T("李四"));
    	m_wndLstMain.SetItemText(1, 1, _T("一班"));
    	m_wndLstMain.SetItemText(1, 2, _T("002"));
    
    	m_wndLstMain.InsertItem(2, _T("王五"));
    	m_wndLstMain.SetItemText(2, 1, _T("一班"));
    	m_wndLstMain.SetItemText(2, 2, _T("003"));
    
    	m_wndLstMain.SetExtendedStyle(LVS_EX_GRIDLINES);
    	
    	//子类化列表控件的表头
    	CHeaderCtrl* pHeader = m_wndLstMain.GetHeaderCtrl();
    	if(pHeader)
    		m_wndHeader.SubclassWindow(pHeader->GetSafeHwnd());
    	HDITEM hdItem;
    	hdItem.mask = HDI_FORMAT;
    	for (int i = 0; i < m_wndHeader.GetItemCount(); i++)
    	{
    		m_wndHeader.GetItem(i, &hdItem);
    		hdItem.fmt |= HDF_OWNERDRAW;	//增加自绘样式
    		m_wndHeader.SetItem(i, &hdItem);
    	}
    	
    	//.... 省略
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    注意在上述函数中,我们首先初始化了CListCtrl对象,创建了多个列及多个子项,然后利用GetHeaderCtrl()函数找到了CListCtrl对象的表头控件,最后调用SubclassWindow子类化表头控件并将其绑定到m_wndHeader(这是我们重写的表头控件类对象)。
    但是,仅仅如此还并未结束,我们需要将表头的每一项都改为自绘样式,这样,表头重绘时才会调用DrawItem函数,具体代码为:

    	HDITEM hdItem;
    	hdItem.mask = HDI_FORMAT;
    	for (int i = 0; i < m_wndHeader.GetItemCount(); i++)
    	{
    		m_wndHeader.GetItem(i, &hdItem);
    		hdItem.fmt |= HDF_OWNERDRAW;	//增加自绘样式
    		m_wndHeader.SetItem(i, &hdItem);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    css3动画+svg实现水球进度条
    设计模式之概述篇
    自定义MVC框架
    DolphinScheduler——介绍及架构设计
    deque的插入和删除
    Redis-带你深入学习数据类型Hash【面试重点】
    娄底高校实验室建设材料考虑
    Python爬虫——Scrapy框架使用实例及执行过程
    SQL Server报错:数据库"YourDatabaseName"的事务日志已满,原因为"LOG_BACKUP"
    css之Flex弹性布局(子项常见属性)
  • 原文地址:https://blog.csdn.net/mary288267/article/details/125521880