1. MyListCtrl.h

#pragma once
#include <vector>
#include "resource.h"
// CMyListCtrl
#ifdef DLL_IMPLEMENT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
typedef unsigned int UINT;
#define ID_LIST_SET_FONT 1001
#define ID_LIST_SAVE_ALL 1002
class DLL_API CMyListCtrl : public CListCtrl
{
struct SColumn
{
UINT index, order, width;
TCHAR name[128];
};
std::vector<SColumn> m_vecColumn;
COLORREF m_clrFore, m_clrBack;
CFont m_font;
CString m_strCfgFileName;
int m_nSortCol;//排序列的索引,-1表示无排序列
bool m_bAscent;//true:升序 false:降序
CImageList m_imgList;//列表头的排序图标
DECLARE_DYNAMIC(CMyListCtrl)
public:
CMyListCtrl();
virtual ~CMyListCtrl();
protected:
DECLARE_MESSAGE_MAP()
public:
// 用配置文件填充列
void FillColumn(LPCTSTR szConfigFileName);
private:
// 保存颜色信息到ini文件
void WriteColorInfoINI(const CString& strCfgFileName)
{
::WritePrivateProfileStruct(_T("COLOR"), _T("color_fore"), &m_clrFore, sizeof(m_clrFore), strCfgFileName);
}
// 从ini配置文件获取颜色信息
void GetColorInfoINI(const CString& strCfgFileName)
{
::GetPrivateProfileStruct(_T("COLOR"), _T("color_fore"), &m_clrFore, sizeof(m_clrFore), strCfgFileName);
}
// 保存字体信息到ini配置文件
void WriteFontInfoINI(const CString& strCfgFileName)
{
LOGFONT lf;
GetFont()->GetLogFont(&lf);
::WritePrivateProfileStruct(_T("FONT"), _T("lf"), &lf, sizeof(lf), strCfgFileName);
}
// 从ini配置文件中获取字体信息
void GetFontInfoINI(const CString& strCfgFileName)
{
LOGFONT lf;
if (::GetPrivateProfileStruct(_T("FONT"), _T("lf"), &lf, sizeof(lf), strCfgFileName))
{
m_font.DeleteObject();
m_font.CreateFontIndirect(&lf);
}
else
{
m_font.DeleteObject();
m_font.CreateFont(20, 0, 0, 0, 400, 0, 0, 0, GB2312_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_ROMAN, _T("微软雅黑"));
}
}
// 保存列信息到ini文件
void WriteColumnInfoINI(const CString& strCfgFileName)
{
CHeaderCtrl* pHeader = GetHeaderCtrl();
if (NULL == pHeader)
return;
int nCount = pHeader->GetItemCount();
if (0 == nCount)
return;
int* pOrder = new int[nCount];
GetColumnOrderArray(pOrder, nCount);
CString strSection, strOrder, strColumnWidth;
for (int i = 0; i < nCount; ++i)
{
strSection.Format(_T("%d"), i);
strOrder.Format(_T("%d"), pOrder[i]);
strColumnWidth.Format(_T("%d"), GetColumnWidth(i));
::WritePrivateProfileString(strSection, _T("Order"), strOrder, strCfgFileName);
::WritePrivateProfileString(strSection, _T("Width"), strColumnWidth, strCfgFileName);
}
delete[] pOrder;
}
static int CALLBACK pfnSort(LPARAM lp1, LPARAM lp2, LPARAM lpSort);
virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
public:
afx_msg void OnSetFont();
afx_msg void OnSaveAll();
afx_msg void OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult);
// 初始化一些变量,像m_imgList
void InitListCtrl();
};
2. MyListCtrl.cpp

// MyListCtrl.cpp : 实现文件
//
#include "stdafx.h"
#define DLL_IMPLEMENT
#include "MyListCtrl.h"
// CMyListCtrl
IMPLEMENT_DYNAMIC(CMyListCtrl, CListCtrl)
CMyListCtrl::CMyListCtrl()
{
//m_nSortCol = -1;
//m_bAscent = true;
}
CMyListCtrl::~CMyListCtrl()
{
}
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
ON_COMMAND(ID_LIST_SET_FONT, OnSetFont)
ON_COMMAND(ID_LIST_SAVE_ALL, OnSaveAll)
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, &CMyListCtrl::OnLvnColumnclick)
END_MESSAGE_MAP()
// CMyListCtrl 消息处理程序
// 用配置文件填充列
void CMyListCtrl::FillColumn(LPCTSTR szConfigFileName)
{
m_strCfgFileName = szConfigFileName;
CString strSection;
TCHAR szName[512] = { 0 };
for (UINT i = 0; true; ++i)
{
SColumn sC;
strSection.Format(_T("%d"), i);
sC.order = ::GetPrivateProfileInt(strSection, _T("Order"), -1, m_strCfgFileName);
if (-1 == sC.order)
break;
sC.width = ::GetPrivateProfileInt(strSection, _T("Width"), -1, m_strCfgFileName);
::GetPrivateProfileString(strSection, _T("ColumnName"), _T("-1"), sC.name, sizeof(sC.name)/sizeof(sC.name[0]), m_strCfgFileName);
m_vecColumn.push_back(sC);
}
//插入列
for (UINT i = 0; i < m_vecColumn.size(); ++i)
{
auto& c = m_vecColumn.at(i);
InsertColumn(i, c.name, 0, c.width);
}
//按order排序
UINT nCount = m_vecColumn.size();
if (0 == nCount) return;
int* arrOrder = new int[nCount];
for (UINT i = 0; i < nCount; ++i)
{
arrOrder[i] = m_vecColumn[i].order;
}
SetColumnOrderArray(nCount, arrOrder);
delete[] arrOrder;
//获取字体,设置字体
GetFontInfoINI(m_strCfgFileName);
SetFont(&m_font);
}
BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// TODO: 在此添加专用代码和/或调用基类
if (0 == wParam && ((NMHDR*)lParam)->code == NM_RCLICK)
{
POINT pt;
GetCursorPos(&pt);
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenu(MF_STRING, ID_LIST_SET_FONT, _T("字体..."));
menu.AppendMenu(MF_STRING, ID_LIST_SAVE_ALL, _T("保存所有设置"));
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
}
return CListCtrl::OnNotify(wParam, lParam, pResult);
}
void CMyListCtrl::OnSetFont()
{
LOGFONT lf;
GetFont()->GetLogFont(&lf);
CFontDialog dlg(&lf, CF_SCREENFONTS, NULL, this);
if (IDOK == dlg.DoModal())
{
m_font.DeleteObject();
m_font.CreateFontIndirect(&dlg.m_lf);
SetFont(&m_font);
}
}
void CMyListCtrl::OnSaveAll()
{
WriteFontInfoINI(m_strCfgFileName);
WriteColumnInfoINI(m_strCfgFileName);
}
void CMyListCtrl::OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult)
{
//LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
//// TODO: 在此添加控件通知处理程序代码
//*pResult = 0;
//当点击不同的列表头时显示对应的排序图标
NM_LISTVIEW* p = (NM_LISTVIEW*)pNMHDR;
int nSub = p->iSubItem;//点击的列的索引
CHeaderCtrl* pHeader = GetHeaderCtrl();
/* 方法1:用自己的图标或位图
//pHeader->SetImageList(&m_imgList);
HDITEM hdi = { HDI_IMAGE | HDI_FORMAT };
if (nSub != m_nSortCol)
{//点击不同列时,删除旧的列图标
if (m_nSortCol > -1)
{
HDITEM hdi = { HDI_FORMAT };
pHeader->GetItem(m_nSortCol, &hdi);
hdi.fmt &= ~HDF_IMAGE ;//移除排序图标,win7下有问题,去不掉,加上 HDITEM hdi = { HDI_FORMAT };解决,看来开关mask很重要
pHeader->SetItem(m_nSortCol, &hdi);
}
m_nSortCol = nSub;
}
else
{//点击相同列时,显示相反排序图标
m_bAscent = !m_bAscent;
}
pHeader->GetItem(nSub, &hdi);
hdi.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
hdi.iImage = m_bAscent;
pHeader->SetItem(nSub, &hdi);
*/
//方法2:用系统提供的排序图标
HDITEM hdi = { HDI_FORMAT };
if (nSub != m_nSortCol)
{//点击不同列时,删除旧的列图标
if (m_nSortCol > -1)
{
pHeader->GetItem(m_nSortCol, &hdi);
hdi.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
pHeader->SetItem(m_nSortCol, &hdi);
}
m_nSortCol = nSub;
}
else
{//点击相同列时,显示相反排序图标
m_bAscent = !m_bAscent;
}
pHeader->GetItem(nSub, &hdi);
hdi.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
hdi.fmt |= (m_bAscent ? HDF_SORTUP : HDF_SORTDOWN);
pHeader->SetItem(nSub, &hdi);
//排序前,要将所有列表项的索引保存到关联数据中
int i = GetItemCount();
while(i--)
{
SetItemData(i, i);
}
SortItems(pfnSort, (DWORD)this); // 排序第二个参数是比较函数的第三个参数
*pResult = 0;
}
int CALLBACK CMyListCtrl::pfnSort(LPARAM lp1, LPARAM lp2, LPARAM lpSort)
{
CMyListCtrl* p = (CMyListCtrl*)lpSort;
CString s1 = p->GetItemText(lp1, p->m_nSortCol);
CString s2 = p->GetItemText(lp2, p->m_nSortCol);
if (p->m_bAscent)
return s2 < s1;
else
return s1 < s2;
}
// 初始化一些变量,像m_imgList
void CMyListCtrl::InitListCtrl()
{
m_imgList.Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 4);
CBitmap b1, b2;
b1.LoadBitmap(IDB_ASCENT);
b2.LoadBitmap(IDB_DESCENT);
m_imgList.Add(&b2, RGB(255, 255, 255));
m_imgList.Add(&b1, RGB(255, 255, 255));
CHeaderCtrl* pHeader = GetHeaderCtrl();
pHeader->SetImageList(&m_imgList);
m_nSortCol = -1;
m_bAscent = false;
}
系统默认的排序图标:

自己画的图标或位图:

