Visual C++2005中開發自定義繪圖控件

發表于:2007-05-25來源:作者:點擊數: 標簽:開發繪圖C++2005visual自定義
本文源代碼下載: CustomDraw.exe 。 在您決定 開發 Windows 提供的常規免費自定義控件范圍之外的控件之后,您必需確定自己的控件將有多少獨到之處 在功能和外觀兩方面。例如,我們假定您正在創建一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中
 本文源代碼下載CustomDraw.exe。

  在您決定開發 Windows 提供的常規免費自定義控件范圍之外的控件之后,您必需確定自己的控件將有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在創建一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,默認終端用戶的交互,以及控件與其父窗口之間需要的任意消息處理。

  另一方面,還包括一些您只想調整公共控件功能的情況。例如,我們假定您想創建一個屏蔽編輯控件,它只允許接受指定的字符。如果使用 MFC,通常涉及從 MFC 提供的類派生一個類,該類封裝了一個公共控件(在屏蔽編輯控件中,通常為 CEdit),重寫必需的虛函數(或處理指定的消息),然后加入自定義的代碼。

  本文討論的重點介于兩者之間 — 公共控件賦予您想要的大部分功能,但控件的外觀并不是您想要的。例如,列表視圖控件提供在許多視圖風格中顯示數據列表的方式 — 小圖標、大圖標、列表和詳細列表(報告)。然而,如果您想要一個網格控件,那結果怎樣呢?盡管公共控件庫里沒有特別包含網格,但是列表視圖控件與它較為接近,它以行和列顯示數據,并有一個相關的標頭控件。因此,許多人以一個標準的列表視圖控件為起點創建自己的網格控件,然后重寫該控件及其子項的呈現方式或繪制方式。

  主宰繪圖操作

  即使“只”進行繪制,您仍然有至少四種選項可用,它們都具有鮮明的優缺點:

  ·處理 WM_PAINT

  ·所有者繪制

  ·自定義繪制

  ·處理 WM_CTLCOLOR

  處理 WM_PAINT

  最極端的選擇是執行一個 WM_PAINT 處理程序,并且自己完成所有的繪制。這意味著,您的代碼將需要進行一些與呈現控件相關的瑣事 — 創建適當的設備上下文(一個或多個),決定控件的大小和位置,繪制控件等。在繪制過程中,很少需要這種級別的控件。

  所有者繪制

  控制控件繪制的另一種方法是利用所有者繪制。事實上,您也許聽開發人員提到過所有者繪制控件,因為它是用于開發自定義控件最普通的技術。該技術普遍使用的主要原因在于,Windows 可為您提供很多幫助。在呈現控件的那一刻,Windows 就已經創建并填寫了設備上下文,決定了控件的大小和位置,并且向您傳遞信息以使您了解此刻繪制的需求。對于列表控件(例如,列表框和列表視圖),Windows 將為列表中的每一項調用繪制代碼,這意味著您只需繪制這些項,而無需考慮控件的其他方面。注意,所有者繪制可用于大多數控件。然而,它不能用于編輯控件;并且考慮到列表控件,它只能用于報表視圖樣式。

  自定義繪制

  對于繪制自己的控件而言,這可能是最少為人所知的技術。事實上,許多技術能力較高的開發人員也混淆了術語所有者繪制 (owner-draw) 和自定義繪制 (custom-draw)。關于自定義控件,首先需要了解,它僅針對于指定的公共控件:標頭、列表視圖、rebar、工具欄、工具提示、跟蹤條和樹視圖。此外,盡管所有者繪制只允許繪制報告視圖風格的列表視圖控件,而自定義繪制則使您能夠處理列表視圖控件所有視圖風格的繪制。使用自定義繪制的另一個明顯優勢是,您可以對希望繪制的內容進行嚴格挑選。實現方式是,在控件繪制的每個階段由 Windows 向代碼發送一個消息。這樣,您可以決定在每個階段是自己進行所有的繪制工作,增加默認的繪制,還是允許 Windows 為該階段執行所有的繪制。(鑒于自定義繪制是本文的一個主題,因此您很快會看到它的工作方式。)

  處理 WM_CTLCOLOR

  這可能是幫助決定如何呈現控件最簡單的方式。正如消息名所指,當要繪制一個控件,并且它能讓您的代碼決定要使用的畫筆時,發送 WM_CTLCOLOR 消息。通常情況下,如果您只想更改控件的顏色,并且不提供除控件本身之外的更多功能,則使用該技術。此外,對于由 Internet Explorer 引入的公共控件(列表視圖、樹視圖、rebar 等),不發送該消息,并且它只與標準控件(編輯、列表框等)協同使用。

  實現自定義繪制的三步曲

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

  ·執行一個 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、idFrom 和 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. 自定義繪制示例應用程序


  小結

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

  要建立一個唯一的且讓人過目難忘的用戶界面,其中一種方式是為應用程序設計并開發自定義的控件。希望本文能對您有所幫助,現在,您了解到一種非常強大的技術,它使您的應用程序能從眾多競爭對手的應用程序中脫穎而出。

原文轉自:http://www.anti-gravitydesign.com

評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97