VC控件自绘制三步曲
既然您已經(jīng)了解了繪制控件可用的各種選項(xiàng)(包括使用自定義繪制的好處),那么,讓我們來看看實(shí)現(xiàn)一個自定義繪制控件需要的三個主要步驟。
-
執(zhí)行一個?NM_CUSTOMDRAW?消息處理程序。
-
指定處理所需的繪制階段。
-
篩選特定的繪制階段(在這些階段中,您需要加入自己的特定于控件的繪制代碼)。
執(zhí)行一個NM_CUSTOMDRAW 消息處理程序
當(dāng)需要繪制一個公共控件時,MFC 會將控件的自定義繪制通知消息(最初發(fā)送到控件的父窗口)以NM_CUSTOMDRAW?消息的形式反饋給控件。以下是一個?NM_CUSTOMDRAW?處理程序的示例。
void CMyCustomDrawControl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) {LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR);... }正如您所見,NM_CUSTOMDRAW?處理程序?qū)⒁粋€指針傳遞給?NMHDR?類型的結(jié)構(gòu)。然而,該值不足以用于象NMHDR?這樣只包含三個成員(hwndFrom、idFrom?和?code)的結(jié)構(gòu)。
因此,您通常需要將該結(jié)構(gòu)指針轉(zhuǎn)換為信息量更大的結(jié)構(gòu) —?LPNMCUSTOMDRAW。LPNMCUSTOMDRAW?指向?NMCUSTOMDRAW,它包含諸如?dwDrawStage、dwItemSpec?和?uItemState?這樣的成員 — 它們是決定當(dāng)前繪制階段及確切繪制(例如,控件本身、或控件的一個項(xiàng)目或子項(xiàng))所必需的。
這里值得注意的是,還可以將?NMHDR?指針指向特定于正在繪制控件的類型的結(jié)構(gòu)。表 1 顯示控件的一個列表及其相關(guān)的自定義繪制結(jié)構(gòu)類型名。
表 1:控件及其相關(guān)的自定義繪制結(jié)構(gòu)
| Rebar、Trackbar、AuthTicket、My.Resources、My.Settings、My.User 和 My.WebServices。 | NMCUSTOMDRAW |
| List-view | NMLVCUSTOMDRAW |
| Toolbar | NMTBCUSTOMDRAW |
| Tooltip | NMTTCUSTOMDRAW |
| Tree-view | NMTVCUSTOMDRAW |
指定處理所需的繪制階段
正如我在前面提到的,繪制一個控件存在一些“階段”。特別是,您可以將繪制過程理解為一系列階段,其中控件通知其父窗口需要繪制的內(nèi)容。事實(shí)上,控件甚至?xí)诶L制控件及其各項(xiàng)前后發(fā)送一個通知,從而讓編程人員更好地控制該過程。
在所有情況下,單一的?NM_CUSTOMDRAW?處理程序在每個繪制階段都進(jìn)行調(diào)用。然而,謹(jǐn)記:自定義繪制允許您在自己的繪制中合并默認(rèn)的控件繪制,您需要指定您將處理哪個繪制階段。這通過設(shè)置NM_CUSTOMDRAW?處理程序的第二個參數(shù) (pResult) 完成。事實(shí)上,如果您從未設(shè)置該值,則用初始階段的CDDS_PREPAINT?調(diào)用函數(shù)后,您的函數(shù)將不再被調(diào)用!
從技術(shù)上講,只有兩個階段指定需要的繪制階段(CDDS_PREPAINT?和?CDDS_ITEMPREPAINT),它們影響發(fā)送通知消息的內(nèi)容。然而,通常只在處理程序的最后指定代碼將處理的繪制階段。表 2 列出用于指定所需繪制階段(代碼關(guān)注的)的值。
表 2:自定義繪制返回標(biāo)志
| CDRF_DEFAULT | 指示控件自行繪制。該值為默認(rèn)值,不應(yīng)該將它與其他值組合在一起。 |
| CDRF_SKIPDEFAULT | 用于指定控件根本不進(jìn)行任何繪制。 |
| CDRF_NEWFONT | 當(dāng)代碼更改繪制項(xiàng)/子項(xiàng)的字體時使用。 |
| CDRF_NOTIFYPOSTPAINT | 使通知信息在控件或每個項(xiàng)/子項(xiàng)繪制后發(fā)送。 |
| CDRF_NOTIFYITEMDRAW | 指出項(xiàng)(或子項(xiàng))將進(jìn)行繪制。注意,它下面的值與 CDRF_NOTIFYSUBITEMDRAW 相同。 |
| CDRF_NOTIFYSUBITEMDRAW | 指出子項(xiàng)(或項(xiàng))將進(jìn)行繪制。注意,它下面的值與 CDRF_NOTIFYITEMDRAW 相同。 |
| CDRF_NOTIFYPOSTERASE | 當(dāng)刪除控件后需要通知代碼時使用。 |
以下為一個示例,其中的代碼指定,當(dāng)繪制控件的項(xiàng) (CDRF_NOTIFYITEMDRAW) 及子項(xiàng) (CDRF_NOTIFYPOSTPAINT),以及繪制完成時,應(yīng)該調(diào)用?NM_CUSTOMDRAW?處理程序。
void CListCtrlWithCustomDraw::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) {LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR);...*pResult = 0; // Initialize value*pResult |= CDRF_NOTIFYITEMDRAW;*pResult |= CDRF_NOTIFYSUBITEMDRAW;*pResult |= CDRF_NOTIFYPOSTPAINT; }篩選指定的繪制階段
一旦指定要關(guān)注的階段后,您需要處理這些階段。因?yàn)槔L制過程的每個階段只有一個消息要發(fā)送,慣例是執(zhí)行一個?switch?語句以決定準(zhǔn)確的繪制階段。不同的繪制階段由以下標(biāo)志定義:
CDDS_PREPAINT CDDS_ITEM CDDS_ITEMPREPAINT CDDS_ITEMPOSTPAINT CDDS_ITEMPREERASE CDDS_ITEMPOSTERASE CDDS_SUBITEM CDDS_POSTPAINT CDDS_PREERASE CDDS_POSTERASE對于一個?CListCtrl?派生的類,有一個?NM_CUSTOMDRAW?處理程序的示例,其中您可以發(fā)現(xiàn),代碼決定當(dāng)前繪制階段的方式:
void CMyCustomDrawControl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) {LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR);switch(pNMCD->dwDrawStage){case CDDS_PREPAINT:...break;case CDDS_ITEMPREPAINT:...break;case CDDS_ITEMPREPAINT | CDDS_SUBITEM:...break;...}*pResult = 0; }注意,為了決定子項(xiàng)(例如,列表視圖控件)繪制的階段,您必需使用按位?or?操作符,它有兩個值:其中一個為?CDDS_ITEMPREPAINT?或者?CDDS_ITEMPOSTPAINT,另一個為?CDDS_SUBITEM。
要說明它,我們假定您想在繪制列表視圖項(xiàng)之前進(jìn)行一些處理。將編寫?switch?語句來處理CDDS_ITEMPREPAINT。
case CDDS_ITEMPREPAINT: ... break;然而,如果是您所關(guān)注子項(xiàng)的預(yù)繪制階段,則將如下操作:
case CDDS_ITEMPREPAINT | CDDS_SUBITEM: ... break; ?返回頁首?示例:創(chuàng)建一個列表視圖控件自定義繪制控件
如前面提到的,您可以完全控制控件及其項(xiàng)的繪制,或者僅執(zhí)行一小部分特定于應(yīng)用程序的繪制,并讓控件繼續(xù)進(jìn)行。本文的焦點(diǎn)更多地偏重于控件繪制技術(shù)而非高級的繪制技術(shù),我們將演練一個簡單的示例,其中列表視圖控件是一個自定義的繪制,因此項(xiàng)的文本將在創(chuàng)建拼接外觀的交替單元中顯示為不同的顏色。
-
創(chuàng)建一個基于 Visual C++ 2005 對話框的項(xiàng)目,名為?ListCtrlColor。
-
從?Class View?中選擇?Project?菜單選項(xiàng),并單擊?Add Class?調(diào)用?Add Class?對話框。
-
從分類列表中選擇?MFC,然后從模板列表中選擇?MFC Class。
-
單擊?Add?按鈕,調(diào)用?MFC Class Wizard?對話框。
-
對于?Class name,鍵入值 CListCtrlWithCustomDraw 并選擇?CListCtrl?的?Base class。
-
單擊?Finish?按鈕,生成類的標(biāo)頭和執(zhí)行文件。
-
對于?Class View,右鍵單擊?CListCtrlWithCustomDraw?類,并選擇?Properties?上下文菜單選項(xiàng)。
-
顯示?Properties?窗口時,單擊頂部的?Messages?按鈕,顯示一個兩列的消息列表,您可以為其實(shí)現(xiàn)處理程序。
-
在消息列表中單擊?NM_CUSTOMDRAW?項(xiàng),然后下拉第二列的組合框箭頭,并選擇值OnNMCustomdraw。
-
現(xiàn)在,處理繪制代碼。這里,我們只簡單處理項(xiàng)和子項(xiàng)預(yù)繪制階段,指定基于當(dāng)前行(項(xiàng))和列(子項(xiàng))的文本和背景色。要進(jìn)行此操作,按如下所示修改?OnNMCustomdraw?函數(shù):
void CListCtrlWithCustomDraw::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) {LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast(pNMHDR);switch(lpLVCustomDraw->nmcd.dwDrawStage){case CDDS_ITEMPREPAINT:case CDDS_ITEMPREPAINT | CDDS_SUBITEM:if (0 == ((lpLVCustomDraw->nmcd.dwItemSpec + lpLVCustomDraw->iSubItem) % 2)){lpLVCustomDraw->clrText = RGB(255,255,255); // white textlpLVCustomDraw->clrTextBk = RGB(0,0,0); // black background}else {lpLVCustomDraw->clrText = CLR_DEFAULT;lpLVCustomDraw->clrTextBk = CLR_DEFAULT;}break;default: break; }*pResult = 0;*pResult |= CDRF_NOTIFYPOSTPAINT;*pResult |= CDRF_NOTIFYITEMDRAW;*pResult |= CDRF_NOTIFYSUBITEMDRAW; }
現(xiàn)在,我們來測試新控件。要進(jìn)行此操作,您只需使用?CListCtrlWithCustomDraw?類將列表視圖控件放在對話框中,并對其進(jìn)行子類派生。下面是完成該操作的步驟。
-
在?Resource?視圖中,打開應(yīng)用程序的主對話框 (IDD_LISTCTRLCOLOR_DIALOG)。
-
從?Toolbox?中,將一個?List Control?拖放到該對話框。
-
右鍵單擊列表控件,并選擇?Properties?上下文菜單選項(xiàng)。
-
將?View?屬性設(shè)置為?Report。
-
右鍵單擊控件,并選擇?Add Variable?上下文菜單選項(xiàng)。
-
出現(xiàn)?Add Member Variable Wizard?對話框時,指定?m_lstBooks?的?Variable name,并單擊?Finish按鈕。
-
這時,您就有了一個 CListCtrl 派生類 (m_lstBooks),它將對話框上的列表視圖控件進(jìn)行子類派生。然而,m_lstBooks 需要從最新創(chuàng)建的 CListCtrlWithCustomDraw 派生,以便于調(diào)用您的繪制代碼。因此,打開對話框的標(biāo)題文件 (ListCtrlColorDlg.h),將 m_lstBooks 更改為 CListCtrlWithCustomDraw 類型。
-
在?CListCtrlColorDlg?類開始之前,添加以下指令。
#include "ListCtrlWithCustomDraw.h" -
將下面的代碼添加到對話框的?OnInitDialog?成員函數(shù),這樣我們就能夠看到一些列表視圖行。
// Insert the columns m_lstBooks.InsertColumn(0, _T("Author")); m_lstBooks.InsertColumn(1, _T("Book"));// Define the data static struct {TCHAR m_szAuthor[50];TCHAR m_szTitle[100]; } BOOK_INFO[] = { _T("Tom Archer"), _T("Visual C++.NET Bible"), _T("Tom Archer"), _T("Extending MFC with the .NET Framework"), _T("Brian Johnson"), _T("XBox 360 For Dummies") };// Insert the data int idx; for (int i = 0; i < sizeof BOOK_INFO / sizeof BOOK_INFO[0]; i++) {idx = m_lstBooks.InsertItem(i, BOOK_INFO[i].m_szAuthor);m_lstBooks.SetItemText(i, 1, BOOK_INFO[i].m_szTitle); } -
現(xiàn)在,建立并運(yùn)行應(yīng)用程序。圖 1 為應(yīng)用程序外觀的一個示例。
圖 1. 自定義繪制示例應(yīng)用程序
小結(jié)
當(dāng) Windows 首次作為“下一代”操作系統(tǒng)引入到應(yīng)用程序開發(fā)之中時,它作為新圖形用戶界面的一個主要論據(jù)就是其一致性。該論據(jù)的要點(diǎn)所在是其具有一個通用的外觀:統(tǒng)一的菜單項(xiàng)、通用控件等。這一通用性的感覺可能會一直延續(xù),直到有第二家公司想設(shè)計(jì)其自己的應(yīng)用程序。簡單說,提供外觀與其他應(yīng)用程序雷同的應(yīng)用程序,任何公司都不會逃離這一怪圈。
要建立一個唯一的且讓人過目難忘的用戶界面,其中一種方式是為應(yīng)用程序設(shè)計(jì)并開發(fā)自定義的控件。希望本文能對您有所幫助,現(xiàn)在,您了解到一種非常強(qiáng)大的技術(shù),它使您的應(yīng)用程序能從眾多競爭對手的應(yīng)用程序中脫穎而出。
總結(jié)
以上是生活随笔為你收集整理的VC控件自绘制三步曲的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自绘控件的4种方法
- 下一篇: VC++获取屏幕大小第一篇 像素大小Ge