1. 程式人生 > >MFC ListBox控制元件設定字型顏色

MFC ListBox控制元件設定字型顏色

描述

最近的專案中需要用到 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);