描述
最近的项目中需要用到 MFC 的列表控件,列表控件中需要动态插入产品不同的测试状态,产品的不同状态下的测试结果分为 PASS 和 FAIL 两种情况,这两种测试结果插入的状态字符串颜色分别呈现为绿色和红色。并且双击状态 Item,绘制出对应状态下的测试结果曲线。
需要解决的问题
- MFC 自带的 ListBox 控件无法设置 Item 中的颜色
- 捕获控件中的 Item 双击事件
方案
设置 Item 颜色
ListBox 无法直接设置字体颜色,因此需要自己实现对字体重绘功能。创建一个继承 ListBox 的类
CColorListBox,重写ListBox 中以下虚函数
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
以及重载函数
int AddString(LPCTSTR lpszItem, COLORREF itemColor);
实现如下:
void CColorListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// Losing focus ?
if (lpDrawItemStruct->itemID == -1)
{
DrawFocusRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem);
return;
}
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
COLORREF clrOld;
CString sText;
// get color info from item data
COLORREF clrNew = (COLORREF)(lpDrawItemStruct->itemData);
// item selected ?
if ((lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
CBrush brush(::GetSysColor(COLOR_HIGHLIGHT));
pDC->FillRect(&lpDrawItemStruct->rcItem, &brush);
}
// item deselected ?
if (!(lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & ODA_SELECT))
{
CBrush brush(::GetSysColor(COLOR_WINDOW));
pDC->FillRect(&lpDrawItemStruct->rcItem, &brush);
}
// item has focus ?
if ((lpDrawItemStruct->itemAction & ODA_FOCUS) &&
(lpDrawItemStruct->itemState & ODS_FOCUS))
{
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
// lost focus ?
if ((lpDrawItemStruct->itemAction & ODA_FOCUS) &&
!(lpDrawItemStruct->itemState & ODS_FOCUS))
{
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
// set the background mode to TRANSPARENT
int nBkMode = pDC->SetBkMode(TRANSPARENT);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
clrOld = pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
else
if (lpDrawItemStruct->itemState & ODS_DISABLED)
clrOld = pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
else
clrOld = pDC->SetTextColor(clrNew);
// get item text
GetText(lpDrawItemStruct->itemID, sText);
CRect rect = lpDrawItemStruct->rcItem;
// text format
UINT nFormat = DT_LEFT | DT_SINGLELINE | DT_VCENTER;
if (GetStyle() & LBS_USETABSTOPS)
nFormat |= DT_EXPANDTABS;
// draw the text
pDC->DrawText(sText, -1, &rect, nFormat);
// restore old values
pDC->SetTextColor(clrOld);
pDC->SetBkMode(nBkMode);
}
void CColorListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENUCHECK);
}
int CColorListBox::AddString(LPCTSTR lpszItem, COLORREF itemColor)
{
// Add the string to the list box
int nIndex = CListBox::AddString(lpszItem);
// save color data
if (nIndex >= 0)
SetItemData(nIndex, itemColor);
return nIndex;
}
鼠标双击事件
右击工程进入类向导,选择自定义的 CColorListBox 类,添加 WM_LBUTTONDBLCLK 消息响应函数。进入响应函数即可添加相应的响应代码。
void CColorListBox::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
int nIndex = GetCurSel();//双击了哪个 Item
int nCount = GetCount();//得到总共的 Item 数
if (nIndex >= 0 && nIndex < nCount)
{
//回调函数,具体实现取决于给它注册的类
m_dbClick(m_result[nIndex - 1]);
}
CListBox::OnLButtonDblClk(nFlags, point);
}
本项目采用回调的方式,即在创建列表时,给 CColorListBox 类注册一个双击事件的回调函数,如此可以在其他类中响应这一双击事件。当然,也可以在捕获到双击事件后,将消息post到指定窗口进行响应。
回调函数原型:
//回调函数:双击列表中的姿态,图表进行响应切换,参数为自定义的测试结果结构体
typedef void(CALLBACK DBCLICKBOXCALLBACK) (ANTRes res);
typedef DBCLICKBOXCALLBACK FAR *LPDBCLICKBOXCALLBACK;
回调函数注册
//m_dbClick为 LPDBCLICKBOXCALLBACK 类型的成员,参数为具体实现的函数指针,在创建列表时调用该函数进行回调注册
void CColorListBox::setDbClickFunc(LPDBCLICKBOXCALLBACK DbClick)
{
m_dbClick = DbClick;
}
调用
- CColorListBox.h 头文件大致如下:
#include <vector>
using namespace std;
//回调函数:双击列表中的姿态,图标进行响应切换,可以设置参数
typedef void(CALLBACK DBCLICKBOXCALLBACK) ();
typedef DBCLICKBOXCALLBACK FAR *LPDBCLICKBOXCALLBACK;
class CColorListBox : public CListBox
{
public:
CColorListBox();
virtual ~CColorListBox();
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
//重载
int AddString(LPCTSTR lpszItem, COLORREF itemColor);
DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
public:
void setDbClickFunc(LPDBCLICKBOXCALLBACK DbClick);
LPDBCLICKBOXCALLBACK m_dbClick;//双击回调函数
};
- CColorListBox.cpp 文件大致如下:
#include "CColorListBox.h"
CColorListBox::CColorListBox()
{
}
CColorListBox::~CColorListBox()
{
}
BEGIN_MESSAGE_MAP(CColorListBox, CListBox)
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()
void CColorListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// Losing focus ?
if (lpDrawItemStruct->itemID == -1)
{
DrawFocusRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem);
return;
}
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
COLORREF clrOld;
CString sText;
// get color info from item data
COLORREF clrNew = (COLORREF)(lpDrawItemStruct->itemData);
// item selected ?
if ((lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
CBrush brush(::GetSysColor(COLOR_HIGHLIGHT));
pDC->FillRect(&lpDrawItemStruct->rcItem, &brush);
}
// item deselected ?
if (!(lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & ODA_SELECT))
{
CBrush brush(::GetSysColor(COLOR_WINDOW));
pDC->FillRect(&lpDrawItemStruct->rcItem, &brush);
}
// item has focus ?
if ((lpDrawItemStruct->itemAction & ODA_FOCUS) &&
(lpDrawItemStruct->itemState & ODS_FOCUS))
{
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
// lost focus ?
if ((lpDrawItemStruct->itemAction & ODA_FOCUS) &&
!(lpDrawItemStruct->itemState & ODS_FOCUS))
{
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
// set the background mode to TRANSPARENT
int nBkMode = pDC->SetBkMode(TRANSPARENT);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
clrOld = pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
else
if (lpDrawItemStruct->itemState & ODS_DISABLED)
clrOld = pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
else
clrOld = pDC->SetTextColor(clrNew);
// get item text
GetText(lpDrawItemStruct->itemID, sText);
CRect rect = lpDrawItemStruct->rcItem;
// text format
UINT nFormat = DT_LEFT | DT_SINGLELINE | DT_VCENTER;
if (GetStyle() & LBS_USETABSTOPS)
nFormat |= DT_EXPANDTABS;
// draw the text
pDC->DrawText(sText, -1, &rect, nFormat);
// restore old values
pDC->SetTextColor(clrOld);
pDC->SetBkMode(nBkMode);
}
void CColorListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENUCHECK);
}
int CColorListBox::AddString(LPCTSTR lpszItem, COLORREF itemColor)
{
// Add the string to the list box
int nIndex = CListBox::AddString(lpszItem);
// save color data
if (nIndex >= 0)
SetItemData(nIndex, itemColor);
return nIndex;
}
void CColorListBox::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
int nIndex = GetCurSel();
int nCount = GetCount();
if (nIndex >= 0 && nIndex < nCount)
{
m_dbClick();
}
CListBox::OnLButtonDblClk(nFlags, point);
}
void CColorListBox::setDbClickFunc(LPDBCLICKBOXCALLBACK DbClick)
{
m_dbClick = DbClick;
}
- 使用
//回调函数具体实现,必须和回调声明类型一致
static void WINAPI OnDbClickListItem()
{
//此处实现双击事件具体需要做的事,如刷新图表
}
//创建一个 CColorListBox 类指针,注意不用时释放
CColorListBox *listBox = new CColorListBox();
listBox->Create(LBS_HASSTRINGS | LBS_OWNERDRAWFIXED | WS_HSCROLL | LBS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, CRect(0, 0, 60, 60), this, IDC_LISTBOX + iAnt);
//为列表注册双击回调函数
listBox->setDbClickFunc(OnDbClickListItem);
//插入字符串,第二个参数为可选的颜色
listBox->AddString("全部姿态", RGB(255, 0, 0));//红色
- 效果
每种状态测试完将状态插入列表,并以颜色表示测试结果
双击右侧列表中的状态,刷新对应状态下的测试曲线
Static 控件的背景颜色设置
界面在显示测试结果时,经常采用 Static 控件,有时候需要让结果看起来很醒目,这时也需要设置控件的背景颜色以达到醒目效果。那么顺便简单说一下 Static 控件的背景颜色与字体的设置,效果如下:
- 实现
进入类向导,选择 Static 控件所在的类,添加 WM_CTLCOLOR 消息响应函数。在响应函数中根据控件 ID 设置控件背景颜色。
HBRUSH CDlgControl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
int n = pWnd->GetDlgCtrlID();
if (n == IDC_STATIC_RESULT_TOTAL)
{
pDC->SetBkColor(RGB(255, 0, 0));
pDC->SetTextColor(RGB(0, 0, 0));
hbr = CreateSolidBrush(RGB(255, 0, 0));
}
else if (n == IDC_STATIC_TIME)
{
pDC->SetTextColor(RGB(0, 0, 0)); //设置字体颜色
pDC->SetBkMode(TRANSPARENT); //设置字体背景为透明
hbr = CreateSolidBrush(RGB(255, 255, 0));//控件背景色
}
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}
设置 Static 控件字体与字号
CFont m_font;
m_font.CreatePointFont(580, _T("宋体"));
GetDlgItem(IDC_STATIC_RESULT_TOTAL)->SetFont(&m_font);