天天看點

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

(#add 兩方面,公共控件庫中沒有類似的 完全重寫;  隻想調整公共控件功能,則可以部分修改)

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

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

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

·處理 WM_PAINT ·所有者繪制(owner draw) ·自定義繪制(custom draw) ·處理 WM_CTLCOLOR

  處理 WM_PAINT

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

  所有者繪制

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

  自定義繪制

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

  處理 WM_CTLCOLOR消息

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

CTLCOLOR_STATIC               Static control

CTLCOLOR_BTN                     Button control

CTLCOLOR_EDIT                    Edit control

CTLCOLOR_LISTBOX             List-box control

CTLCOLOR_SCROLLBAR    Scroll-bar control

CTLCOLOR_DLG                    Dialog box

CTLCOLOR_MSGBOX            Message box

不會為組合框中的下拉清單框調用OnCtlColor函數,因為下拉清單框實際上是組合框的子視窗,而不是視窗的子視窗。要改變下拉清單框的顔色,建立一個CComboBox,在重載的OnCtlColor中的nCtlColor參數中檢查CTLCOLOR_LISTBOX。在這個處理函數中,為設定文本的背景必須使用SetBkColor成員函數。

自定義繪制:

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

·執行一個 NM_CUSTOMDRAW 消息處理程式。 ·指定處理所需的繪制階段。 ·篩選特定的繪制階段(在這些階段中,您需要加入自己的特定于控件的繪制代碼)。

  1,執行一個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

  2,指定處理所需的繪制階段

  正如我在前面提到的,繪制一個控件存在一些“階段”。特别是,您可以将繪制過程了解為一系列階段,其中控件通知其父視窗需要繪制的内容。事實上,控件甚至會在繪制控件及其各項前後發送一個通知,進而讓程式設計人員更好地控制該過程。

  在所有情況下,單一的 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; }

  3,篩選指定的繪制階段

  一旦指定要關注的階段後,您需要處理這些階段。因為繪制過程的每個階段隻有一個消息要發送,慣例是執行一個 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 為應用程式外觀的一個示例。

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

  圖 1. 自定義繪制示例應用程式

  小結

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

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

在開始編碼之前,首先應該确定好,更準确的說應該是設計好按鈕在各種狀态下的外觀。按鈕控件的幾中基本狀态包括:

Normal狀态,就是按鈕一開始顯示時的樣子。

Over狀态,滑鼠指針移動到按鈕上面時按鈕顯示的樣子。

Down狀态,按下按鈕時顯示的樣子。

Focus狀态,按鈕按下後松開的樣子,例如标準按鈕按下松開之後會看到按鈕内部有一個虛線框。

Disable狀态,當然就是按鈕被設定成無效的時候的樣子啦。

我參考了一下WindowsXP中普通按鈕的實際樣子,設計出XP按鈕各種狀态的外觀,如下圖所示:

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

至于Down狀态主要是在Over狀态的基礎上将文字往右下的方向稍微平移,以實作下壓的效果。

二、實作原理及難點

下面我們開始類的建立,在Workspace的ClassView頁中右擊清單樹的根結點,選擇New Class…

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

在彈出視窗中進行派生類的定義,如下圖所示,注意,你需要填寫的隻有Name和Base class兩項,其餘的選項保持預設值就可以了。

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

  下面簡要叙述一下按鈕的實作原理:

1. 在控件初始化時為按鈕添加Owner  Draw的屬性。這是因為在MFC中,要想激活控件的自繪功能,要求該控件的屬性中必須包含屬性值BS_OWNERDRAW,這一步我們可以通過類向導為 CXPButton類添加PreSubclassWindow()函數,在該函數中完成屬性值的設定。當激活控件的自繪功能之後,每次控件狀态改變的時候都會運作函數DrawItem(),該函數的作用就是繪制控件在各種狀态下的外觀。

2. 添加WM_MOUSELEAVE消息函數,當滑鼠指針離開按鈕時,觸發該消息函數,我們在函數中添加代碼,通知DrawItem函數滑鼠指針已經離開了,讓按鈕重繪。

3. 添加WM_MOUSEHOVER消息函數,當滑鼠指針位于按鈕之上時,觸發該消息函數,我們在函數重添加代碼,通知DrawItem函數滑鼠指針現在正在按鈕的上面,讓按鈕重繪。

4. 添加DrawItem函數。在DrawItem中根據按鈕目前的狀态繪制按鈕的外觀。可以說自繪控件的大部分功能都是在這個函數中實作的。DrawItem函數包含了一個LPDRAWITEMSTRUCT的指針,本篇會在稍後予以講解。

這裡有兩個難點,首先是WM_MOUSELEAVE和 WM_MOUSEHOVER不是标準的Windows消息函數,它們不能通過類向導來添加,所有的添加工作都需要通過手工輸入代碼來完成。另一個難點是 DrawItem中的LPDRAWITEMSTRUCT指針,它指向了一個DRAWITEMSTRUCT的結構,這個結構中包含了控件的各種細節,為我們提供了實作自繪功能的必要資訊。

難點一:

事實上WM_MOUSELEAVE和WM_MOUSEHOVER兩個Windows消息是通過WM_MOUSEMOVE消息觸發的,而 WM_MOUSEMOVE是标準的Windows消息,是以我們可以通過類向導來為CXPButton類添加WM_MOUSEMOVE消息函數。

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

函數的代碼見如下,這段代碼非常有用,在其它的自繪控件中,如果想觸發WM_MOUSELEAVE和WM_MOUSEHOVER消息,也是使用類似的方法實作的。

voidCXPButton::OnMouseMove(UINT nFlags, CPoint point)

{

       // TODO: Add your message handler codehere and/or call default

       if (!m_bTracking)

       {

              TRACKMOUSEEVENT tme;

              tme.cbSize = sizeof(tme);

              tme.hwndTrack = m_hWnd;

              tme.dwFlags = TME_LEAVE |TME_HOVER;

              tme.dwHoverTime = 1;

             m_bTracking = _TrackMouseEvent(&tme);

       }

       CButton::OnMouseMove(nFlags, point);

}

我們接着添加WM_MOUSELEAVE和WM_MOUSEHOVER消息消息函數。在CXPButton類的聲明中(即在 XPButton.h檔案中)找到afx_msg void OnMouseMove(UINT nFlags, CPoint point);的函數聲明,緊接其下輸入

afx_msgLRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);

afx_msgLRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);

然後在XPButton.cpp檔案中找到ON_WM_MOUSEMOVE(),緊接其後輸入

ON_MESSAGE(WM_MOUSELEAVE,OnMouseLeave)

ON_MESSAGE(WM_MOUSEHOVER,OnMouseHover)

難點二:

下面我們看看DRAWITEMSTRUCE結構為我們提供了哪些有用資訊呢?

DRAWITEMSTRUCT結構的定義如下:

typedefstruct  tagDRAWITEMSTRUCT

    UINT  CtlType;                      //控件類型

    UINT  CtlID;                        //控件ID

    UINT  itemID;                       //菜單項、清單框或組合框中某一項的索引值

    UINT  itemAction;                    //控件行為

    UINT  itemState;                     //控件狀态

    HWND  hwndItem;                   //父視窗句柄或菜單句柄

    HDC   hDC;                        //控件對應的繪圖裝置句柄

    RECT  rcItem;                       //控件所占據的矩形區域

    DWORD itemData;                   //清單框或組合框中某一項的值

}DRAWITEMSTRUCT, *PDRAWITEMSTRUCT, *LPDRAWITEMSTRUCT;

其實不僅是按鈕控件,其它控件,如ComboBox、ListBox、StaticText等都是通過DRAWITEMSTRUCT來記錄控件資訊的。關于這個結構的詳細文檔可參考本篇的附錄。

也許你早已看到許多自繪按鈕的例子,實際上自繪按鈕本身的函數結構都是差不多的,它們顯示效果的差別主要取決于代碼編寫者對GDI作圖函數的運用與掌握程度。有興趣的朋友可以研究一下CXPButton類中DrawItem函數的資料結構,其實隻要修改一下其中GDI繪圖函數的部分代碼,馬上又能做出另一個自繪按鈕控件了。

三、按鈕類的使用

下面示範CXPButton類的使用。往對話框中添加一個按鈕控件,假設它的ID值為IDC_BUTTON1。進入類向導(Class Wizard)的Member Variables屬性頁,為IDC_BUTTON1添加一個變量m_btnNormal。确定退出後再進行編譯,就可以看到重新定義過XP風格按鈕了。

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

如果你是之間把CXPButton的源檔案引入自己的工程中的,那麼在上圖的Variable type中是看不到CXPButton選項的。但是可以通過以下方法加入:

1. 首先儲存工程後退出。

2. 在工程的目錄下找到一個字尾名為.clw的檔案,将其删除。但是為了以防萬一還是建議你實作備份一下。

3. 重新打開工程,進入類向導,此時會看到一下一個彈出對話框,我們選擇“是(Yes)”。

标準控件的使用技巧與自繪控件在您決定開發 Windows 提供的正常免費自定義控件範圍之外的控件之後,您必需确定自己的控件将有多少獨到之處 — 在功能和外觀兩方面。例如,我們假定您正在建立一個類似于計速表的控件。由于公共控件庫 (ComCtrl32.dll) 中沒有類似的控件,您完全需要自己進行以下操作:編寫所有控件功能需要的代碼,進行繪制,預設終端使用者的互動,以及控件與其父視窗之間需要的任意消息處理。 OWNER DRAW實作自繪按鈕 一、準備工作

4. 再選擇“Add All”,這樣我們就可以在類向導中使用CXPButton的變量類型了。

四、小結與提示

對于按鈕來說,當按鈕上面任何可見的部分發生變換的時候,都要調用DrawItem函數進行重繪。自繪制按鈕必須設定BS_OWNERDRAW的屬性,設定的代碼在PreSubclassWindows函數中完成。另外為了防止系統字型設定的變化影響控件的表達效果,還可以在該函數中為控件指定某種固定的字型。但是要注意的是這個

讓我們來回顧一下實作自繪按鈕的基本步驟:

a. 确定設計方案;

b. 初始化,但是記得要在函數退出前恢複先前的GDI對象,并釋放所占領的資源;

c. 添加相應消息函數;

d. 添加繪圖函數DrawItem,在DrawItem中作圖的順序一般是先畫外邊框,再上底色,接着寫文字,最後是畫内邊框。不過有些人也喜歡把邊框放到最後畫,這問題不大。

五、附錄

DRAWITEMSTRUCT結構文檔 (根據Msdn翻譯)

DRAWITEMSTRUCT

DRAWITEMSTRUCT為需要自繪的控件或者菜單項提供了必要的資訊。在需要繪制的控件或者菜單項對應的WM_DRAWITEM消息函數中得到一個指向該結構的指針。 DRAWITEMSTRUCT結構的定義如下:

typedef  struct  tagDRAWITEMSTRUCT

UINTCtlType ;

UINTCtlID ;

UINTitemID ;

UINTitemAction ;

UINTitemState ;

HWNDhwndItem ;

HDC hDC;

RECTrcItem ;

ULONG_PTRitemData ;

} DRAWITEMSTRUCT;

結構成員:

CtlType

指定了控件的類型,其取值如下表所示。

取值

描述

ODT_STATIC

靜态文本控件

ODT_BUTTON

按鈕控件

ODT_COMBOBOX

組合框控件

ODT_LISTBOX

清單框控件

ODT_LISTVIEW

清單視圖控件

ODT_MENU

菜單項

ODT_TAB

Tab控件

CtlID

指定了自繪控件的ID值,而對于菜單項則不需要使用該成員

itemID

表示菜單項ID,也可以表示清單框或者組合框中某項的索引值。對于一個空的清單框或組合框,該成員的值為–1。這時應用程式隻繪制焦點矩形(該矩形的坐标由rcItem 成員給出)雖然此時控件中沒有需要顯示的項,但是繪制焦點矩形還是很有必要的,因為這樣做能夠提示使用者該控件是否具有輸入焦點。當然也可以設定itemAction 成員為合适值,使得無需繪制焦點。

itemAction

指定繪制行為,其取值可以為下表中所示值的一個或者多個的聯合。

ODA_DRAWENTIRE

當整個控件都需要被繪制時,設定該值

ODA_FOCUS

如果控件需要在獲得或失去焦點時被繪制,則設定該值。此時應該檢查itemState成員,以确定控件是否具有輸入焦點。

ODA_SELECT

如果控件需要在選中狀态改變時被繪制,則設定該值。此時應該檢查itemState 成員,以确定控件是否處于選中狀态。

itemState

指定了目前繪制操作完成後,所繪項的可見狀态。例如,如果菜單項應該被灰色顯示,則可以指定ODS_GRAYED狀态标志。其取值可以為下表中所示值的一個或者多個的聯合。

ODS_CHECKED

如果菜單項将被選中,則可設定該值。該值隻對菜單項有用。

ODS_COMBOBOXEDIT

在自繪組合框控件中隻繪制選擇區域。

ODS_DEFAULT

預設值。

ODS_DISABLED

如果控件将被禁止,則設定該值。

ODS_FOCUS

如果控件需要輸入焦點,則設定該值。

ODS_GRAYED

如果控件需要被灰色顯示,則設定該值。該值隻在繪制菜單時使用。

ODS_HOTLIGHT

Windows 98/Me, Windows 2000/XP: 如果滑鼠指針位于控件之上,則設定該值,這時控件會顯示高亮顔色。

ODS_INACTIVE

Windows 98/Me, Windows 2000/XP: 表示沒有激活的菜單項。

ODS_NOACCEL

Windows 2000/XP: 控件是否有快速鍵盤。

ODS_NOFOCUSRECT

Windows 2000/XP: 不繪制捕獲焦點的效果。

ODS_SELECTED

選中的菜單項。

hwndItem

指定了組合框、清單框和按鈕等自繪控件的視窗句柄;如果自繪的對象時菜單項,則表示包含該菜單項的菜單句柄。

hDC

指定了繪制操作所使用的裝置環境。

rcItem

指定了将被繪制的矩形區域。這個矩形區域就是上面hDC的作用範圍。系統會自動裁剪組合框、清單框或按鈕等控件的自繪制區域以外的部分。也就是說 rcItem中的坐标點(0,0)指的就是控件的左上角。但是系統不裁剪菜單項,是以在繪制菜單項的時候,必須先通過一定的換算得到該菜單項的位置,以保證繪制操作在我們希望的區域中進行。

itemData

對于菜單項,該成員的取值可以是由

CMenu::AppendMenu、

CMenu::InsertMenu或者

CMenu::ModifyMenu

等函數傳遞給菜單的值。

對于清單框或這組合框,該成員的值可以為由

ComboBox::AddString、

CComboBox::InsertString、

CListBox::AddString或者

CListBox::InsertString

等傳遞給控件的值。

如果ctlType 的取值是ODT_BUTTON或者ODT_STATIC, itemData的取值為0。

繼續閱讀