VC控制項自繪製

來源:互聯網
上載者:User
實現自訂繪製的三步曲

既然您已經瞭解了繪製控制項可用的各種選項(包括使用自訂繪製的好處),那麼,讓我們來看看實現一個自訂繪製控制項需要的三個主要步驟。

  • 執行一個 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 這樣只包含三個成員(hwndFromidFromcode)的結構。

因此,您通常需要將該結構指標轉換為資訊量更大的結構 — LPNMCUSTOMDRAWLPNMCUSTOMDRAW 指向 NMCUSTOMDRAW,它包含諸如 dwDrawStagedwItemSpecuItemState 這樣的成員 — 它們是決定當前繪製階段及確切繪製(例如,控制項本身、或控制項的一個項目或子項)所必需的。

這裡值得注意的是,還可以將 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_PREPAINTCDDS_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_PREPAINTCDDS_ITEMCDDS_ITEMPREPAINTCDDS_ITEMPOSTPAINTCDDS_ITEMPREERASECDDS_ITEMPOSTERASECDDS_SUBITEMCDDS_POSTPAINTCDDS_PREERASECDDS_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 並選擇 CListCtrlBase 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_lstBooksVariable name,並單擊 Finish 按鈕。

  • 這時,您就有了一個 CListCtrl 衍生類別 (m_lstBooks),它將對話方塊上的列表視圖控制項進行子類派生。然而,m_lstBooks 需要從最新建立的 CListCtrlWithCustomDraw 派生,以便於調用您的繪製代碼。因此,開啟對話方塊的標題檔案 (ListCtrlColorDlg.h),將 m_lstBooks 更改為 CListCtrlWithCustomDraw 類型。

  • CListCtrlColorDlg 類開始之前,添加以下指令。

    #include "ListCtrlWithCustomDraw.h"
  • 將下面的代碼添加到對話方塊的 OnInitDialog 成員函數,這樣我們就能夠看到一些列表視圖行。

    // Insert the columnsm_lstBooks.InsertColumn(0, _T("Author"));m_lstBooks.InsertColumn(1, _T("Book"));// Define the datastatic 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 dataint 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. 自訂繪製應用程式範例

小結

當 Windows 首次作為“下一代”作業系統引入到應用程式開發之中時,它作為新圖形化使用者介面的一個主要論據就是其一致性。該論據的要點所在是其具有一個通用的外觀:統一的功能表項目、通用控制項等。這一通用性的感覺可能會一直延續,直到有第二家公司想設計其自己的應用程式。簡單說,提供外觀與其他應用程式雷同的應用程式,任何公司都不會逃離這一怪圈。

要建立一個唯一的且讓人過目難忘的使用者介面,其中一種方式是為應用程式設計並開發自訂的控制項。希望本文能對您有所協助,現在,您瞭解到一種非常強大的技術,它使您的應用程式能從眾多競爭者的應用程式中脫穎而出。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.