實作自定義繪制的三步曲
既然您已經了解了繪制控制元件可用的各種選項(包括使用自定義繪制的好處),那么,讓我們來看看實作一個自定義繪制控制元件需要的三個主要步驟。
' 執行一個 NM_CUSTOMDRAW 訊息處理程式。
' 指定處理所需的繪制階段。
' 篩選特定的繪制階段(在這些階段中,您需要加入自己的特定于控制元件的繪制代碼)。
執行一個NM_CUSTOMDRAW 訊息處理程式
當需要繪制一個公共控制元件時,MFC 會將控制元件的自定義繪制通知訊息(最初發送到控制元件的父視窗)以 NM_CUSTOMDRAW 訊息的形式反饋給控制元件。以下是一個 NM_CUSTOMDRAW 處理程式的示例。
void CMyCustomDrawControl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); ... }
正如您所見,NM_CUSTOMDRAW 處理程式將一個指標傳遞給 NMHDR 型別的結構。然而,該值不足以用于象 NMHDR 這樣只包含三個成員(hwndFrom、id From 和 code)的結構。
因此,您通常需要將該結構指標轉換為資訊量更大的結構 — LPNMCUSTOMDRAW。LPNMCUSTOMDRAW 指向 NMCUSTOMDRAW,它包含諸如 dwDrawStage、dwItemSpec 和 uItemState 這樣的成員 — 它們是決定當前繪制階段及確切繪制(例如,控制元件本身、或控制元件的一個專案或子項)所必需的。
這里值得注意的是,還可以將 NMHDR 指標指向特定于正在繪制控制元件的型別的結構。表 1 顯示控制元件的一個串列及其相關的自定義繪制結構型別名。
表 1:控制元件及其相關的自定義繪制結構
控制元件 結構(在 commctrl.h 中定義)
Rebar、Trackbar、AuthTicket、My.Resources、My.Settings、My.User 和 My.WebServices。
NMCUSTOMDRAW
List-view
NMLVCUSTOMDRAW
Toolbar
NMTBCUSTOMDRAW
Tooltip
NMTTCUSTOMDRAW
Tree-view
NMTVCUSTOMDRAW
指定處理所需的繪制階段
正如我在前面提到的,繪制一個控制元件存在一些“階段”。特別是,您可以將繪制程序理解為一系列階段,其中控制元件通知其父視窗需要繪制的內容。事實上,控制元件甚至會在繪制控制元件及其各項前后發送一個通知,從而讓編程人員更好地控制該程序。
在所有情況下,單一的 NM_CUSTOMDRAW 處理程式在每個繪制階段都進行呼叫。然而,謹記:自定義繪制允許您在自己的繪制中合并默認的控制元件繪制,您需要指定您將處理哪個繪制階段。這通過設定 NM_CUSTOMDRAW 處理程式的第二個引數 (pResult) 完成。事實上,如果您從未設定該值,則用初始階段的 CDDS_PREPAINT 呼叫函式后,您的函式將不再被呼叫!
從技術上講,只有兩個階段指定需要的繪制階段(CDDS_PREPAINT 和 CDDS_ITEMPREPAINT),它們影響發送通知訊息的內容。然而,通常只在處理程式的最后指定代碼將處理的繪制階段。表 2 列出用于指定所需繪制階段(代碼關注的)的值。
表 2:自定義繪制回傳標志
自定義繪制回傳標志 含義
CDRF_DEFAULT
指示控制元件自行繪制。該值為默認值,不應該將它與其他值組合在一起。
CDRF_SKIPDEFAULT
用于指定控制元件根本不進行任何繪制。
CDRF_NEWFONT
當代碼更改繪制項/子項的字體時使用。
CDRF_NOTIFYPOSTPAINT
使通知資訊在控制元件或每個項/子項繪制后發送。
CDRF_NOTIFYITEMDRAW
指出項(或子項)將進行繪制。注意,它下面的值與 CDRF_NOTIFYSUBITEMDRAW 相同。
CDRF_NOTIFYSUBITEMDRAW
指出子項(或項)將進行繪制。注意,它下面的值與 CDRF_NOTIFYITEMDRAW 相同。
CDRF_NOTIFYPOSTERASE
當洗掉控制元件后需要通知代碼時使用。
以下為一個示例,其中的代碼指定,當繪制控制元件的項 (CDRF_NOTIFYITEMDRAW) 及子項 (CDRF_NOTIFYPOSTPAINT),以及繪制完成時,應該呼叫 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; }
篩選指定的繪制階段
一旦指定要關注的階段后,您需要處理這些階段。因為繪制程序的每個階段只有一個訊息要發送,慣例是執行一個 switch 陳述句以決定準確的繪制階段。不同的繪制階段由以下標志定義:
CDDS_PREPAINT CDDS_ITEM CDDS_ITEMPREPAINT CDDS_ITEMPOSTPAINT CDDS_ITEMPREERASE CDDS_ITEMPOSTERASE CDDS_SUBITEM CDDS_POSTPAINT CDDS_PREERASE CDDS_POSTERASE
對于一個 CListCtrl 派生的類,有一個 NM_CUSTOMDRAW 處理程式的示例,其中您可以發現,代碼決定當前繪制階段的方式:
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; }
注意,為了決定子項(例如,串列視圖控制元件)繪制的階段,您必需使用按位 or 運算子,它有兩個值:其中一個為 CDDS_ITEMPREPAINT 或者 CDDS_ITEMPOSTPAINT,另一個為 CDDS_SUBITEM。
要說明它,我們假定您想在繪制串列視圖項之前進行一些處理。將撰寫 switch 陳述句來處理 CDDS_ITEMPREPAINT。
case CDDS_ITEMPREPAINT: ... break;
然而,如果是您所關注子項的預繪制階段,則將如下操作:
case CDDS_ITEMPREPAINT | CDDS_SUBITEM: ... break;
回傳頁首回傳頁首
示例:創建一個串列視圖控制元件自定義繪制控制元件
如前面提到的,您可以完全控制控制元件及其項的繪制,或者僅執行一小部分特定于應用程式的繪制,并讓控制元件繼續進行。本文的焦點更多地偏重于控制元件繪制技術而非高級的繪制技術,我們將演練一個簡單的示例,其中串列視圖控制元件是一個自定義的繪制,因此項的文本將在創建拼接外觀的交替單元中顯示為不同的顏色。
' 創建一個基于 Visual C++ 2005 對話框的專案,名為 ListCtrlColor。
' 從 Class View 中選擇 Project 選單選項,并單擊 Add Class 呼叫 Add Class 對話框。
' 從分類串列中選擇 MFC,然后從模板串列中選擇 MFC Class。
' 單擊 Add 按鈕,呼叫 MFC Class Wizard 對話框。
' 對于 Class name,鍵入值 CListCtrlWithCustomDraw 并選擇 CListCtrl 的 Base class。
' 單擊 Finish 按鈕,生成類的標頭和執行檔案。
' 對于 Class View,右鍵單擊 CListCtrlWithCustomDraw 類,并選擇 Properties 背景關系選單選項。
' 顯示 Properties 視窗時,單擊頂部的 Messages 按鈕,顯示一個兩列的訊息串列,您可以為其實作處理程式。
' 在訊息串列中單擊 NM_CUSTOMDRAW 項,然后下拉第二列的組合框箭頭,并選擇值 OnNMCustomdraw。
' 現在,處理繪制代碼。這里,我們只簡單處理項和子項預繪制階段,指定基于當前行(項)和列(子項)的文本和背景色。要進行此操作,按如下所示修改 OnNMCustomdraw 函式:
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 text
lpLVCustomDraw->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; }
現在,我們來測驗新控制元件。要進行此操作,您只需使用 CListCtrlWithCustomDraw 類將串列視圖控制元件放在對話框中,并對其進行子類派生。下面是完成該操作的步驟。
' 在 Resource 視圖中,打開應用程式的主對話框 (IDD_LISTCTRLCOLOR_DIALOG)。
' 從 Toolbox 中,將一個 List Control 拖放到該對話框。
' 右鍵單擊串列控制元件,并選擇 Properties 背景關系選單選項。
'
將 View 屬性設定為 Report。
' 右鍵單擊控制元件,并選擇 Add Variable 背景關系選單選項。
' 出現 Add Member Variable Wizard 對話框時,指定 m_lstBooks 的 Variable name,并單擊 Finish 按鈕。
' 這時,您就有了一個 CListCtrl 派生類 (m_lstBooks),它將對話框上的串列視圖控制元件進行子類派生。然而,m_lstBooks 需要從最新創建的 CListCtrlWithCustomDraw 派生,以便于呼叫您的繪制代碼。因此,打開對話框的標題檔案 (ListCtrlColorDlg.h),將 m_lstBooks 更改為 CListCtrlWithCustomDraw 型別。
' 在 CListCtrlColorDlg 類開始之前,添加以下指令。
#include 'ListCtrlWithCustomDraw.h'
' 將下面的代碼添加到對話框的 OnInitDialog 成員函式,這樣我們就能夠看到一些列
表視圖行。
// 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); }
' 現在,建立并運行應用程式。圖 1 為應用程式外觀的一個示例。
圖 1. 自定義繪制示例應用程式
uj5u.com熱心網友回復:
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/33462.html
標籤:界面
上一篇:drawitem
