[轉貼] Windows編程和麪向對象技術 chap6

第六課 控件

  在上一課中,同學們已經接觸到了一些常用的控件。控件實際上是子窗口,在應用程序與用戶進行交互的過程中,控件是主要角色。因此,有必要對控件進行詳細的討論。

  Windows提供了五花八門的標準控件,這些控件可粗分爲兩類。一類是在Windows 3.x就已支持的傳統控件,一類是Windows 95/NT支持的新型Win32控件。Windows提供控件的目的就是方便程序與用戶的交互。應用程序應該根據自己的實際情況,選擇合適的控件。

  不管是什麼類型的控件,一般都具有WS_CHILD和WS_VISIBLE窗口風格.WS_CHILD指定窗口爲子窗口,WS_VISIBLE使窗口是可見的.另外,大部分控件還具有WS_TABSTOP風格,WS_TABSTOP使控件具有Tabstop屬性.

  MFC提供了大量的控件類,它們封裝了控件的功能。通過這些控件類,程序可以方便地創建控件,對控件進行查詢和控制。所有的控件類都是CWnd類的直接或間接派生類.

  在學習這一講之前,有幾個問題需要先行說明:

在本節中,同學們會經常遇到控件類的Create成員函數,該函數負責創建控件.在上一章中同學們已經試驗過,只要把控件放入對話框模板中,在調用DoModal或Create創建對話框時,框架會根據模板資源中的信息自動地創建控件。但有時需要用手工動態地創建控件,這通常需要按下面的步驟進行:

構建一個控件對象。

調用控件對象的成員函數Create來創建控件。

在6.3節中將對控件的創建進行詳細討論。

在上一章中,介紹了用ClassWizard爲對話框類創建與傳統控件對應的成員變量的方法.成員變量可以是數據變量或控件對象.需要指出的是,對於新的Win32控件,只能創建控件對象,不能創建數據變量.

在控件類的函數說明中,讀者會經常看到LPCTSTR參數類型,LPCTSTR是一個宏,相當於const char far *,它用來說明指向常量字符串的指針.MFC的字符串類CString定義了一個與LPCTSTR同名的操作符,該操作符可以把一個CString對象轉換成一個常量字符串.因此,如果函數的參數是用LPCTSTR來說明的,則既可以向該參數傳遞一個指向常量字符串的指針,也可以傳遞一個CString對象.

  這一講將對一些常用的控件及其控件類進行較詳細的討論,討論的側重點包括控件的創建、控件類的成員函數以及控件的通知消息。具體講,本章主要包括以下主要內容:

傳統控件

新型Win32控件

控件的技術總結

在非對話框窗口中使用控件

 

6.1 傳統控件

  在上一課的表5.1已經列出了Windows的傳統控件及其對應的控件類。在這些控件中,讀者應該重點掌握命令按鈕、選擇框、單選按鈕、編輯框、列表框和組合框。

.1.1 傳統控件的控件通知消息

  控件通過向父窗口發送控件通知消息來表明發生了某種事件.例如,當用戶在按鈕上單擊鼠標時,按鈕控件會向父窗口發送BN_CLICKED消息.傳統控件的通知消息實際上是通過WM_COMMAND消息發給父窗口的(滾動條除外),在該消息的wParam中含有通知消息碼(如BN_CLICKED)和控件的ID,在lParam中則包含了控件的句柄.

  利用ClassWizard可以很容易地爲控件通知消息加入消息映射和消息處理函數,這在上一章中已經演示過了.傳統控件的消息映射宏是ON_XXXX,其中XXXX表示通知消息碼,如BN_CLICKED.ON_XXXX消息映射如下所示,該宏有兩個參數,一個是控件的ID,一個是消息處理函數名.

ON_XXXX(nID, memberFxn)

消息處理函數的聲明應該有如下形式:

afx_msg void memberFxn( );

例如,某按鈕的BN_CLICKED消息的消息映射及其處理函數的聲明如下所示

ON_BN_CLICKED(IDC_ADD,OnAdd)

afx_msg void OnAdd( );

  有時,爲了處理方便,需要把多個ID連續的控件發出的相同消息映射到同一個處理函數上.這就要用到ON_CONTROL_RANGE宏.ON_CONTROL_RANGE消息映射宏的第一個參數是控件消息碼,第二和第三個參數分別指明瞭一組連續的控件ID中的頭一個和最後一個ID,最後一個參數是消息處理函數名。例如,要處理一組單選按鈕發出的BN_CLICKED消息,相應的消息映射如下所示:

ON_CONTROL_RANGE(BN_CLICKED, IDC_FIRST, IDC_LAST, OnRadioClicked)

函數OnRadioClicked的聲明如下,該函數比上面的OnAdd多了一個參數nID以說明發送通知消息的控件ID.

afx_msg void OnRadioClicked(UINT nID);

ClassWizard不支持ON_CONTROL_RANGE宏,所以需要手工建立消息映射和消息處理函數.

提示:事實上,在使用ClassWizard時只要運用一個小小的技巧,就可以把不同控件的通知消息映射到同一個處理函數上,也可以把一個控件的不同通知消息映射到同一個處理函數上.這個技巧就是在用ClassWizard創建消息處理函數時,指定相同的函數名即可.此方法的優點在於控件的ID不必是連續的,缺點是處理函數沒有nID參數,因而不能確定是哪一個控件發送的消息.

6.1.2 靜態控件

  靜態控件包括靜態正文(Static Text)和圖片控件(Picture)。靜態正文控件用來顯示正文。圖片控件可以顯示位圖、圖標、方框和圖元文件,在圖片控件中顯示圖片的好處是不必操心圖片的重繪問題。靜態控件不能接收用戶的輸入。在上一章中,讀者已經用過靜態正文和組框控件。圖片控件的例子可以在AppWizard創建的IDD_ABOUTBOX對話框模板中找到,在該模板中有一個圖片控件用來顯示圖標。

  靜態控件的主要起說明和裝飾作用。MFC的CStatic類封裝了靜態控件。CStatic類的成員函數Create負責創建靜態控件,該函數的聲明爲

BOOL Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff );

  參數lpszText指定了控件顯示的正文。dwStyle指定了靜態控件的風格,表6.1顯示了靜態控件的各種風格,dwStyle可將這些風格組合起來。rect是一個對RECT或CRect結構的引用,用來說明控件的位置和尺寸。pParentWnd指向父窗口,該參數不能爲NULL。nID則說明了控件的ID。如果創建成功,該函數返回TRUE,否則返回FALSE.

表6.1 靜態控件的風格

控件風格

含義

SS_BLACKFRAME

指定一個具有與窗口邊界同色的框(缺省爲黑色)。

SS_BLACKRECT

指定一個具有與窗口邊界同色的實矩形(缺省爲黑色)。

SS_CENTER

使顯示的正文居中對齊,正文可以迴繞。

SS_GRAYFRAME

指定一個具有與屏幕背景同色的邊框。

SS_GRAYRECT

指定一個具有與屏幕背景同色的實矩形。

SS_ICON

使控件顯示一個在資源中定義的圖標,圖標的名字有Create函數的lpszText參數指定。

SS_LEFT

左對齊正文,正文能迴繞。

SS_LEFTNOWORDWRAP

左對齊正文,正文不能迴繞。

SS_NOPREFIX

使靜態正文串中的&不是一個熱鍵提示符。

SS_NOTIFY

使控件能向父窗口發送鼠標事件消息。

SS_RIGHT

右對齊正文,可以迴繞。

SS_SIMPLE

使靜態正文在運行時不能被改變並使正文顯示在單行中。

SS_USERITEM

指定一個用戶定義項。

SS_WHITEFRAME

指定一個具有與窗口背景同色的框(缺省爲白色)。

SS_WHITERECT

指定一個具有與窗口背景同色的實心矩形(缺省爲白色)。

  除了上表中的風格外,一般還要爲控件指定WS_CHILD和WS_VISIBLE窗口風格。一個典型的靜態正文控件的風格爲WS_CHILD|WS_VISIBLE|SS_LEFT。

  對於用對話框模板編輯器創建的靜態控件,可以在控件的屬性對話框中指定表6.1中列出的控件風格。例如,可以在靜態正文控件的屬性對話框中選擇Simple,這相當於指定了SS_SIMPLE風格。

  Cstatic類主要的成員函數在表6.2中列出。可以利用CWnd類的成員函數GetWindowText,SetWindowText和GetWindowTextLength等函數來查詢和設置靜態控件中顯示的正文.

表6.2 CStatic類的主要成員函數

函數聲明

用途

HBITMAP SetBitmap( HBITMAP hBitmap );

指定要顯示的位圖。

HBITMAP GetBitmap( ) const;

獲取由SetBitmap指定的位圖。

HICON SetIcon( HICON hIcon );

指定要顯示的圖標。

HICON GetIcon( ) const;

獲取由SetIcon指定的圖標。

HCURSOR SetCursor( HCURSOR hCursor );

指定要顯示的光標圖片。

HCURSOR GetCursor( );

獲取由SetCursor指定的光標。

HENHMETAFILE SetEnhMetaFile( HENHMETAFILE hMetaFile );

指定要顯示的增強圖元文件。

HENHMETAFILE GetEnhMetaFile( ) const;

獲取由SetEnhMetaFile指定的圖元文件。

  靜態控件較簡單,故這裏就不舉例說明了。

6.1.3 按鈕控件

  按鈕是指可以響應鼠標點擊的小矩形子窗口。按鈕控件包括命令按鈕(Pushbutton)、檢查框(Check Box)、單選按鈕(Radio Button)、組框(Group Box)和自繪式按鈕(Owner-draw Button)。命令按鈕的作用是對用戶的鼠標單擊作出反應並觸發相應的事件,在按鈕中既可以顯示正文,也可以顯示位圖。選擇框控件可作爲一種選擇標記,可以有選中、不選中和不確定三種狀態。單選按鈕控件一般都是成組出現的,具有互斥的性質,即同組單選按鈕中只能有一個是被選中的。組框用來將相關的一些控件聚成一組.自繪式按鈕是指由程序而不是系統負責重繪的按鈕。

  按鈕主要是指命令按鈕、選擇框和單選按鈕。後二者實際上是一種特殊的按鈕,它們有選擇和未選擇狀態。當一個選擇框處於選擇狀態時,在小方框內會出現一個“√”,當單選按鈕處於選擇狀態時,會在圓圈中顯示一個黑色實心圓。此外,檢查框還有一種不確定狀態,這時檢查框呈灰色顯示,不能接受用戶的輸入,以表明控件是無效的或無意義的。

  按鈕控件會向父窗口發出如表6.3所示的控件通知消息。

表6.3 按鈕控件的通知消息

消息

含義

BN_CLICKED

用戶在按鈕上單擊了鼠標。

BN_DOUBLECLICKED

用戶在按鈕上雙擊了鼠標。

FC的CButton類封裝了按鈕控件。CButton類的成員函數Create負責創建按鈕控件,該函數的聲明爲

BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

  參數lpszCaption指定了按鈕顯示的正文。dwStyle指定了按鈕的風格,如表6.4所示,dwStyle可以是這些風格的組合。rect說明了按鈕的位置和尺寸。pParentWnd指向父窗口,該參數不能爲NULL。nID是按鈕的ID。如果創建成功,該函數返回TRUE,否則返回FALSE.

 

表6.4 按鈕的風格

控件風格

含義

BS_AUTOCHECKBOX

同BS_CHECKBOX,不過單擊鼠標時按鈕會自動反轉。

BS_AUTORADIOBUTTON

同BS_RADIOBUTTON,不過單擊鼠標時按鈕會自動反轉。

BS_AUTO3STATE

同BS_3STATE,不過單擊按鈕時會改變狀態。

BS_CHECKBOX

指定在矩形按鈕右側帶有標題的選擇框。

BS_DEFPUSHBUTTON

指定缺省的命令按鈕,這種按鈕的周圍有一個黑框,用戶可以按回車鍵來快速選擇該按鈕。

BS_GROUPBOX

指定一個組框。

BS_LEFTTEXT

使控件的標題顯示在按鈕的左邊。

BS_OWNERDRAW

指定一個自繪式按鈕。

BS_PUSHBUTTON

指定一個命令按鈕。

BS_RADIOBUTTON

指定一個單選按鈕,在圓按鈕的右邊顯示正文。

BS_3STATE

同BS_CHECKBOX,不過控件有三種狀態:選擇、未選擇和變灰。

 

 

除了上表中的風格外,一般還要爲控件指定WS_CHILD、WS_VISIBLE和WS_TABSTOP窗口風格,WS_TABSTOP使控件具有Tabstop屬性。創建一個普通按鈕應指定的風格爲WS_CHILD|WS_VISIBLE|WS_TABSTOP。創建一個普通檢查框應指定風格WS_CHILD|WS_VISIBLE|WS_TABSTOP| BS_AUTOCHECKBOX。創建組中第一個單選按鈕應指定風格WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP| BS_AUTORADIOBUTTON,組中其它單選按鈕應指定風格則不應該包括WS_TABSTOP和WS_GROUP。

對於用對話框模板編輯器創建的按鈕控件,可以在控件的屬性對話框中指定表6.4中列出的控件風格。例如,在命令按鈕的屬性對話框中選擇Default button,相當於指定了BS_DEFPUSHBUTTON。

CButton類的主要的成員函數有:

UINT GetState( ) const;
該函數返回按鈕控件的各種狀態。可以用下列屏蔽值與函數的返回值相與,以獲得各種信息。

0x0003。用來獲取檢查框或單選按鈕的狀態。0表示未選中,1表示被選中,2表示不確定狀態(僅用於檢查框)。

0x0004。用來判斷按鈕是否是高亮度顯示的。非零值意味着按鈕是高亮度顯示的。當用戶點擊了按鈕並按主鼠標左鍵時,按鈕會呈高亮度顯示。

0x0008。非零值表示按鈕擁有輸入焦點。

void SetState( BOOL bHighlight );
當參數bHeightlight值爲TRUE時,該函數將按鈕設置爲高亮度狀態,否則,去除按鈕的高亮度狀態。

int GetCheck( ) const;
返回檢查框或單選按鈕的選擇狀態。返回值0表示按鈕未被選擇,1表示按鈕被選擇,2表示按鈕處於不確定狀態(僅用於檢查框)。

void SetCheck( int nCheck );
設置檢查框或單選按鈕的選擇狀態。參數nCheck值的含義與GetCheck返回值相同。

UINT GetButtonStyle( ) const;
獲得按鈕控件的BS_XXXX風格。

void SetButtonStyle( UINT nStyle, BOOL bRedraw = TRUE );
設置按鈕的風格。參數nStyle指定了按鈕的風格。bRedraw爲TRUE則重繪按鈕,否則就不重繪。

HBITMAP SetBitmap( HBITMAP hBitmap );
設置按鈕顯示的位圖。參數hBitmap指定了位圖的句柄。該函數還會返回按鈕原來的位圖。

HBITMAP GetBitmap( ) const;
返回以前用SetBitmap設置的按鈕位圖。

HICON SetIcon( HICON hIcon );
設置按鈕顯示的圖標。參數hIcon指定了圖標的句柄。該函數還會返回按鈕原來的圖標。

HICON GetIcon( ) const;
返回以前用SetIcon設置的按鈕圖標。

HCURSOR SetCursor( HCURSOR hCursor );
設置按鈕顯示的光標圖。參數hCursor指定了光標的句柄。該函數還會返回按鈕原來的光標。

HCURSOR GetCursor( );
返回以前用GetCursor設置的光標。

 

另外,可以使用下列的一些與按鈕控件有關的CWnd成員函數來設置或查詢按鈕的狀態。用這些函數的好處在於不必構建按鈕控件對象,只要知道按鈕的ID,就可以直接設置或查詢按鈕。

void CheckDlgButton( int nIDButton, UINT nCheck );
用來設置按鈕的選擇狀態。參數nIDButton指定了按鈕的ID。nCheck的值0表示按鈕未被選擇,1表示按鈕被選擇,2表示按鈕處於不確定狀態。

void CheckRadioButton( int nIDFirstButton, int nIDLastButton, int nIDCheckButton );
用來選擇組中的一個單選按鈕。參數nIDFirstButton指定了組中第一個按鈕的ID,nIDLastButton指定了組中最後一個按鈕的ID,nIDCheckButton指定了要選擇的按鈕的ID。

int GetCheckedRadioButton( int nIDFirstButton, int nIDLastButton );
該函數用來獲得一組單選按鈕中被選中按鈕的ID。參數nIDFirstButton說明了組中第一個按鈕的ID,nIDLastButton說明了組中最後一個按鈕的ID。

UINT IsDlgButtonChecked( int nIDButton ) const;
返回檢查框或單選按鈕的選擇狀態。返回值0表示按鈕未被選擇,1表示按鈕被選擇,2表示按鈕處於不確定狀態(僅用於檢查框)。

 

可以調用CWnd成員函數GetWindowText,GetWindowTextLength和SetWindowText來查詢或設置按鈕中顯示的正文.

MFC還提供了CButton的派生類CBitmapButton。利用該類可以創建一個擁有四幅位圖的命令按鈕,按鈕在不同狀態時會顯示不同的位圖,這樣可以使界面顯得生動活潑。如果讀者對CBitmapButton感興趣,可以參看VC5.0隨盤提供的MFC例子CTRLTEST。

在上一章的Register例子中已演示了各種按鈕控件的使用,故這裏就不再舉例了。

 

 

6.1.4 編輯框控件

編輯框(Edit Box)控件實際上是一個簡易的正文編輯器,用戶可以在編輯框中輸入並編輯正文。編輯框既可以是單行的,也可以是多行的,多行編輯框是從零開始編行號的.在一個多行編輯框中,除了最後一行外,每一行的結尾處都有一對回車換行符(用"/r/n"表示).這對回車換行符是正文換行的標誌,在屏幕上是不可見的.

編輯框控件會向父窗口發出如表6.5所示的控件通知消息。

 

表6.5

消息

含義

EN_CHANGE

編輯框的內容被用戶改變了。與EN_UPDATE不同,該消息是在編輯框顯示的正文被刷新後才發出的。

EN_ERRSPACE

編輯框控件無法申請足夠的動態內存來滿足需要。

EN_HSCROLL

用戶在水平滾動條上單擊鼠標。

EN_KILLFOCUS

編輯框失去輸入焦點。

EN_MAXTEXT

輸入的字符超過了規定的最大字符數。在沒有ES_AUTOHSCROLL或ES_AUTOVSCROLL的編輯框中,當正文超出了編輯框的邊框時也會發出該消息。

EN_SETFOCUS

編輯框獲得輸入焦點。

EN_UPDATE

在編輯框準備顯示改變了的正文時發送該消息。

EN_VSCROLL

用戶在垂直滾動條上單擊鼠標。

 

 

MFC的CEdit類封裝了編輯框控件。CEdit類的成員函數Create負責創建按鈕控件,該函數的聲明爲

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

參數dwStyle指定了編輯框控件風格,如表6.6所示,dwStyle可以是這些風格的組合。rect指定了編輯框的位置和尺寸。pParentWnd指定了父窗口,不能爲NULL。編輯框的ID由nID指定。如果創建成功,該函數返回TRUE,否則返回FALSE.

 

表6.6 編輯框控件的風格

控件風格

含義

ES_AUTOHSCROLL

當用戶在行尾鍵入一個字符時,正文將自動向右滾動10個字符,當用戶按回車鍵時,正文總是滾向左邊。

ES_AUTOVSCROLL

當用戶在最後一個可見行按回車鍵時,正文向上滾動一頁。

ES_CENTER

在多行編輯框中使正文居中。

ES_LEFT

左對齊正文。

ES_LOWERCASE

把用戶輸入的字母統統轉換成小寫字母。

ES_MULTILINE

指定一個多行編輯器。若多行編輯器不指定ES_AUTOHSCROLL風格,則會自動換行,若不指定ES_AUTOVSCROLL,則多行編輯器會在窗口中正文裝滿時發出警告聲響。

ES_NOHIDESEL

缺省時,當編輯框失去輸入焦點後會隱藏所選的正文,當獲得輸入焦點時又顯示出來。設置該風格可禁止這種缺省行爲。

ES_OEMCONVERT

使編輯框中的正文可以在ANSI字符集和OEM字符集之間相互轉換。這在編輯框中包含文件名時是很有用的。

ES_PASSWORD

使所有鍵入的字符都用“*”來顯示。

ES_RIGHT

右對齊正文。

ES_UPPERCASE

把用戶輸入的字母統統轉換成大寫字母。

ES_READONLY

將編輯框設置成只讀的。

ES_WANTRETURN

使多行編輯器接收回車鍵輸入並換行。如果不指定該風格,按回車鍵會選擇缺省的命令按鈕,這往往會導致對話框的關閉。

 

 

除了上表中的風格外,一般還要爲控件指定WS_CHILD、WS_VISIBLE、WS_TABSTOP和WS_BORDER窗口風格,WS_BORDER使控件帶邊框。創建一個普通的單行編輯框應指定風格爲WS_CHILD|WS_VISIBLE|WS_TABSTOP |WS_BORDER|ES_LEFT|ES_AUTOHSCROLL,這將創建一個帶邊框、左對齊正文、可水平滾動的單行編輯器。要創建一個普通多行編輯框,還要附加ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL |WS_HSCROLL| WS_VSCROLL風格,這將創建一個可水平和垂直滾動的,帶有水平和垂直滾動條的多行編輯器。

對於用對話框模板編輯器創建的編輯框控件,可以在控件的屬性對話框中指定表6.6中列出的控件風格。例如,在屬性對話框中選擇Multi-line項,相當與指定了ES_MULTILINE風格。

編輯框支持剪貼板操作。CEdit類提供了一些與剪貼板有關的成員函數,如表6.7所示。

 

表6.7 與剪切板有關的CEdit成員函數

函數聲明

用途

void Clear( )

清除編輯框中被選擇的正文。

void Copy( )

把在編輯框中選擇的正文拷貝到剪貼板中。

void Cut( )

清除編輯框中被選擇的正文並把這些正文拷貝到剪貼板中。

void Paste( )

將剪貼板中的正文插入到編輯框的當前插入符處。

BOOL Undo( )

撤消上一次鍵入。對於單行編輯框,該函數總返回TRUE,對於多行編輯框,返回TRUE表明操作成功,否則返回FALSE。

 

 

可以用下列CEdit或CWnd類的成員函數來查詢編輯框。在學習下面的函數時,讀者會經常遇到術語字符索引.字符的字符索引是指從編輯框的開頭字符開始的字符編號,它是從零開始編號的.也就是說,字符索引實際上是指當把整個編輯正文看作一個字符串數組時,該字符所在的數組元素的下標.

 

int GetWindowText( LPTSTR lpszStringBuf, int nMaxCount ) const;
void GetWindowText( CString& rString ) const;
這兩個函數均是CWnd類的成員函數,可用來獲得窗口的標題或控件中的正文。第一個版本的函數用lpszStringBuf參數指向的字符串數組作爲拷貝正文的緩衝區,參數nMaxCount可以拷貝到緩衝區中的最大字符數,該函數返回以字節爲單位的實際拷貝字符數(不包括結尾的空字節)。第二個版本的函數用一個CString對象作爲緩衝區。

int GetWindowTextLength( ) const;
CWnd的成員函數,可用來獲得窗口的標題或控件中的正文的長度。

DWORD GetSel( ) const;
void GetSel( int& nStartChar, int& nEndChar ) const;
兩個函數都是CEdit的成員函數,用來獲得所選正文的位置。GetSel的第一個版本返回一個DWORD值,其中低位字說明了被選擇的正文開始處的字符索引,高位字說明了選擇的正文結束處的後面一個字符的字符索引,如果沒有正文被選擇,那麼返回的低位和高位字節都是當前插入符所在字符的字符索引。GetSel的第二個版本的兩個參數是兩個引用,其含義與第一個版本函數返回值的低位和高位字相同。

int LineFromChar( int nIndex = –1 ) const;
CEdit的成員函數,僅用於多行編輯框,用來返回指定字符索引所在行的行索引(從零開始編號)。參數nIndex指定了一個字符索引,如果nIndex是-1,那麼函數將返回選擇正文的第一個字符所在行的行號,若沒有正文被選擇,則該函數會返回當前的插入符所在行的行號。

int LineIndex( int nLine = –1 ) const;
CEdit的成員函數,僅用於多行編輯框,用來獲得指定行的開頭字符的字符索引,如果指定行超過了編輯框中的最大行數,該函數將返回-1。參數nLine是指定了從零開始的行索引,如果它的值爲-1,則函數返回當前的插入符所在行的字符索引。

int GetLineCount( ) const;
CEdit的成員函數,僅用於多行編輯框,用來獲得正文的行數。如果編輯框是空的,那麼該函數的返回值是1。

int LineLength( int nLine = –1 ) const;
CEdit的成員函數,用於獲取指定字符索引所在行的字節長度(行尾的回車和換行符不計算在內)。參數nLine說明了字符索引.如果nLine的值爲-1,則函數返回當前行的長度(假如沒有正文被選擇),或選擇正文佔據的行的字符總數減去選擇正文的字符數(假如有正文被選擇)。若用於單行編輯框,則函數返回整個正文的長度。

int GetLine( int nIndex, LPTSTR lpszBuffer ) const;
int GetLine( int nIndex, LPTSTR lpszBuffer, int nMaxLength ) const;
CEdit的成員函數,僅用於多行編輯框,用來獲得指定行的正文(不包括行尾的回車和換行符)。參數nIndex是行號,lpszBuffer指向存放正文的緩衝區,nMaxLength規定了拷貝的最大字節數,若。函數返回實際拷貝的字節數,若指定的行號大於編輯框的實際行數,則函數返回0。需要注意的是,GetLine函數不會在緩衝區中字符串的末尾加字符串結束符(NULL).

 

下列CWnd或CEdit類的成員函數可用來修改編輯框控件。

void SetWindowText( LPCTSTR lpszString );
CWnd的成員函數,可用來設置窗口的標題或控件中的正文。參數lpszString可以是一個CString對象,或是一個指向字符串的指針。

void SetSel( DWORD dwSelection, BOOL bNoScroll = FALSE );
void SetSel( int nStartChar, int nEndChar, BOOL bNoScroll = FALSE );
CEdit的成員函數,用來選擇編輯框中的正文。參數dwSelection的低位字說明了選擇開始處的字符索引,高位字說明了選擇結束處的字符索引。如果低位字爲0且高位字節爲-1,那麼就選擇所有的正文,如果低位字節爲-1,則取消所有的選擇.參數bNoScroll的值如果是FALSE,則滾動插入符並使之可見,否則就不滾動.參數nStartChar和nEndChar的含義與參數dwSelection的低位字和高位字相同.

void ReplaceSel( LPCTSTR lpszNewText, BOOL bCanUndo = FALSE );
CEdit的成員函數,用來將所選正文替換成指定的正文.參數lpszNewText指向用來替換的字符串.參數bCanUndo的值爲TRUE說明替換是否可以被撤消的.

 

在調用上述函數時,如果涉及的是一個多行編輯框,那麼除了LineLength和GetLine函數外,都要把回車和換行符考慮在內.例如,假設在編輯框中有如下幾行正文:

abcd

efg

ij

那麼字母"e"的字符索引是6而不是4,因爲"abcd"後面還有一對回車換行符.調用LineLength(7)會返回第二行的長度3.調用LineIndex(2)會得到11.調用LineFromChar(8)會返回1.如果沒有選擇任何正文,並且插入符在字母"e"上,那麼調用GetSel返回值的低位和高位字都是6.

通過分析上述函數,我們可以總結出一些查詢和設置編輯框的方法.

調用CWnd的成員函數GetWindowText和SetWindowText可以查詢和設置編輯框的整個正文,在上一章的Register程序中,我們就使用過這兩個函數.

如果想對多行編輯框逐行查詢,那麼應該先調用GetLineCount獲得總行數,然後再調用GetLine來獲取每一行的正文.下面一段代碼演示瞭如何對多行編輯框進行逐行查詢.

char buf[40];

int total=MyEdit.GetLineCount();

int i,length;

for(i=0;i<total;i++)

{

length=MyEdit.GetLine(i,buf,39);

buf[length]=0; //加字符串結束符

. . . . . .

}

可以利用LineIndex和LineFromChar來在字符索引和字符的行列座標之間相互轉換.下列代碼演示了在已知字符索引的情況下,如何獲得對應的行列座標:
int row,column;
row=MyEdit.LineFromChar(charIndex);
column=charIndex-MyEdit.LineIndex(row);
下列代碼演示了在已知字符的行列座標的情況下,如何獲得對應的字符索引:
int charIndex;
charIndex=MyEdit.LineIndex(row)+column;
不難看出字符索引與對應的行列座標的關係是:字符索引=LineIndex(行座標)+列座標.

對於選擇正文的查詢和設置,應該利用函數GetSel、SetSel和ReplaceSel.

可以利用GetSel和SetSel來查詢和設置插入符的位置.SetSel可以使編輯框滾動到插入符的新位置.
要獲取插入符的行列座標,可用下面的代碼實現:
MyEdit.SetSel(-1,0); //取消正文的選擇
int start,end,row,column;
MyEdit.GetSel(start,end); //start或end的值就是插入符的字符索引
row=MyEdit.LineFromChar(start); //獲取插入符的行座標
column=start-MyEdit.LineIndex(row); //獲取插入符的列座標
下面的代碼演示瞭如何把插入符移到指定的行和列:
MyEdit.SetSel(-1,0); //取消正文的選擇
int charIndex=MyEdit.LineIndex(row)+column;
MyEdit.SetSel(charIndex,charIndex);

可以利用ReplaceSel函數在 插入符處插入正文,典型的代碼如下所示:
MyEdit.SetSel(-1,0); //取消正文的選擇
MyEdit.ReplaceSel(“......”);

可以利用ReplaceSel清除編輯框中的正文,典型的代碼如下所示:
MyEdit.SetSel(0,-1); //選擇全部正文
MyEdit.ReplaceSel(“”);

在後面的小節中,讀者將會看到使用編輯框的例子.

.1.5 滾動條控件

滾動條(Scroll Bar)主要用來從某一預定義值範圍內快速有效地進行選擇.滾動條分垂直滾動條和水平滾動條兩種.在滾動條內有一個滾動框,用來表示當前的值.用鼠標單擊滾動條,可以使滾動框移動一頁或一行,也可以直接拖動滾動框.滾動條既可以作爲一個獨立控件存在,也可以作爲窗口、列表框和組合框的一部分.Windows 95的滾動條支持比例滾動框,即用滾動框的大小來反映頁相對於整個範圍的大小.Windows 3.x使用單獨的滾動條控件來調整調色板、鍵盤速度以及鼠標靈敏度,在Windows 95中,滾動條控件被軌道條取代(參見6.2.3)不提倡使用單獨的滾動條控件.

需要指出的是,從性質上劃分,滾動條可分爲標準滾動條和滾動條控件兩種.標準滾動條是由WS_HSCROLL或WS_VSCROLL風格指定的,它不是一個實際的窗口,而是窗口的一個組成部分(例如列表框中的滾動條),只能位於窗口的右側(垂直滾動條)或底端(水平滾動條).標準滾動條是在窗口的非客戶區中創建的.與之相反,滾動條控件並不是窗口的一個零件,而是一個實際的窗口,可以放置在窗口客戶區的任意地方,它既可以獨立存在,也可以與某一個窗口組合,行使滾動窗口的職能.由於滾動條控件是一個獨立窗口,因此可以擁有輸入焦點,可以響應光標控制鍵,如PgUp、PgDown、Home和End.

MFC的CScrollBar類封裝了滾動條控件.CScrollBar類的Create成員函數負責創建控件,該函數的聲明爲

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

參數dwStyle指定了控件的風格.rect說明了控件的位置和尺寸.pParentWnd指向父窗口,該參數不能爲NULL。nID則說明了控件的ID。如果創建成功,該函數返回TRUE,否則返回FALSE.

要創建一個普通的水平滾動條控件,應指定風格WS_CHILD|WS_VISIBLE|BS_HORZ.要創建一個普通的垂直滾動條控件,應指定風格WS_CHILD|WS_VISIBLE|BS_VERT.

主要的CScrollBar類成員函數如下所示:

int GetScrollPos( ) const;
該函數返回滾動框的當前位置.若操作失敗則返回0.

int SetScrollPos( int nPos, BOOL bRedraw = TRUE );
該函數將滾動框移動到指定位置.參數nPos指定了新的位置.參數bRedraw表示是否需要重繪滾動條,如果爲TRUE,則重繪之.函數返回滾動框原來的位置.若操作失敗則返回0.

void GetScrollRange( LPINT lpMinPos, LPINT lpMaxPos ) const;
該函數對滾動條的滾動範圍進行查詢.參數lpMinPos和lpMaxPos分別指向滾動範圍的最小最大值.

void SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw = TRUE );
該函數用於指定滾動條的滾動範圍.參數nMinPos和nMaxPos分別指定了滾動範圍的最小最大值.由這兩者指定的滾動範圍不得超過32767.當兩者都爲0時,滾動條將被隱藏.參數bRedraw表示是否需要重繪滾動條,如果爲TRUE,則重繪之.

BOOL GetScrollInfo( LPSCROLLINFO lpScrollInfo, UINT nMask );
該函數用來獲取滾動條的各種狀態,包括滾動範圍、滾動框的位置和頁尺寸.參數lpScrollInfo指向一個SCROLLINFO結構,該結構如下所示:
typedef struct tagSCROLLINFO {
UINT cbSize; //結構的尺寸(字節爲單位)
UINT fMask; /*說明結構中的哪些參數是有效的,可以是屏蔽值的組合, 如SIF_POS|SIF_PAGE,若爲SIF_ALL則整個結構都有效*/
int nMin; //滾動範圍最大值,當fMask中包含SIF_RANGE時有效
int nMax; //滾動範圍最小值,當fMask中包含SIF_RANGE時有效
UINT nPage; /*頁尺寸,用來確定比例滾動框的大小,當fMask中包含 SIF_PAGE時有效*/
int nPos; //滾動框的位置,當fMask中包含SIF_POS有效
int nTrackPos; /*拖動時滾動框的位置,當fMask中包含 SIF_TRACKPOS時有效,該參數只能查詢,不能設 置,最好不要用該參數來查詢拖動時滾動框的位置*/
} SCROLLINFO;
typedef SCROLLINFO FAR *LPSCROLLINFO;
參數nMask的意義與SCROLLINFO結構中的fMask相同.函數在獲得有效值後返回TRUE,否則返回FALSE.

BOOL SetScrollInfo( LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE );
該函數用於設置滾動條的各種狀態,一個重要用途是設定頁尺寸從而實現比例滾動框.參數lpScrollInfo指向一個SCROLLINFO結構,參數bRedraw表示是否需要重繪滾動條,如果爲TRUE,則重繪之.若操作成功,該函數返回TRUE,否則返回FALSE.

 

CWnd類也提供了一些函數來查詢和設置所屬的標準滾動條.這些函數與CScrollBar類的函數同名,且功能相同,但每個函數都多了一個參數,用來選擇滾動條.例如,CWnd:: GetScrollPos 的聲明爲

int GetScrollPos( int nBar ) const;
參數nBar用來選擇滾動條,可以爲下列值:
SB_HORZ //指定水平滾動條
SB_VERT //指定垂直滾動條

 

無論是標準滾動條,還是滾動條控件,滾動條的通知消息都是用WM_HSCROLL和WM_VSCROLL消息發送出去的.對這兩個消息的確省處理函數是CWnd::OnHScroll和CWnd::OnVScroll,它們幾乎什麼也不做.一般需要在派生類中對這兩個函數從新設計,以實現滾動功能.這兩個函數的聲明爲

afx_msg void OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar );

afx_msg void OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar );
參數nSBCode是通知消息碼,如表6.8所示.nPos是滾動框的位置,只有在nSBCode爲SB_THUMBPOSITION或SB_THUMBTRACK時,該參數纔有意義.如果通知消息是滾動條控件發來的,那麼pScrollBar是指向該控件的指針,如果是標準滾動條發來的,則pScrollBar爲NULL.

 

表6.8 滾動條的通知消息碼

消息

含義

SB_BOTTOM / SB_RIGHT(二者的消息碼是一樣的,因此可以混用,下同)

滾動到底端(右端).

SB_TOP / SB_LEFT

滾動到頂端(左端).

SB_LINEDOWN / SB_LINERIGHT

向下(向右)滾動一行(列).

SB_LINEUP / SB_LINELEFT

向上(向左)滾動一行(列).

SB_PAGEDOWN / SB_PAGERIGHT

向下(向右)滾動一頁.

SB_PAGEUP / SB_PAGELEFT

向上(向左)滾動一頁.

SB_THUMBPOSITION

滾動到指定位置.

SB_THUMBTRACK

滾動框被拖動.可利用該消息來跟蹤對滾動框的拖動.

SB_ENDSCROLL

滾動結束.

6.1.8小節的例子中,讀者將學會如何使用滾動條以及如何編寫自己的OnHScroll函數.

6.1.6 列表框控件

列表框主要用於輸入,它允許用戶從所列出的表項中進行單項或多項選擇,被選擇的項呈高亮度顯示.列表框具有邊框,並且一般帶有一個垂直滾動條.列表框分單選列表框和多重選擇列表框兩種.單選列表框一次只能選擇一個列表項,而多重選擇列表框可以進行多重選擇.對於列表項的選擇,微軟公司有如下建議:

單擊鼠標選擇一個列表項,單擊一個按鈕來處理選擇的項.

雙擊鼠標選擇一個列表項是處理選擇項的快捷方法.

 

列表框會向父窗口發送如表6.9所示的通知消息.

 

表6.9 列表框控件的通知消息

消息

含義

LBN_DBLCLK

用戶用鼠標雙擊了一列表項.只有具有LBS_NOTIFY的列表框才能發送該消息.

LBN_ERRSPACE

列表框不能申請足夠的動態內存來滿足需要.

LBN_KILLFOCUS

列表框失去輸入焦點.

LBN_SELCANCEL

當前的選擇被取消.只有具有LBS_NOTIFY的列表框才能發送該消息.

LBN_SELCHANGE

單擊鼠標選擇了一列表項.只有具有LBS_NOTIFY的列表框才能發送該消息.

LBN_SETFOCUS

列表框獲得輸入焦點.

WM_CHARTOITEM

當列表框收到WM_CHAR消息後,向父窗口發送該消息.只有具有LBS_WANTKEYBOARDINPUT風格的列表框纔會發送該消息.

WM_VKEYTOITEM

當列表框收到WM_KEYDOWN消息後,向父窗口發送該消息.只有具有LBS_WANTKEYBOARDINPUT風格的列表框纔會發送該消息.

 

 

MFC的CListBox類封裝了列表框.CListBox類的Create成員函數負責列表框的創建,該函數的聲明是

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

參數dwStyle指定了列表框控件的風格,如表6.10所示,dwStyle可以是這些風格的組合.rect說明了控件的位置和尺寸.pParentWnd指向父窗口,該參數不能爲NULL。nID則說明了控件的ID。如果創建成功,該函數返回TRUE,否則返回FALSE.

 

表6.10 列表框控件的風格

控件風格

含義

LBS_EXTENDEDSEL

支持多重選擇.在點擊列表項時按住Shift鍵或Ctrl鍵即可選擇多個 項.

LBS_HASSTRINGS

指定一個含有字符串的自繪式列表框.

LBS_MULTICOLUMN

指定一個水平滾動的多列列表框,通過調用CListBox::SetColumnWidth來設置每列的寬度.

LBS_MULTIPLESEL

支持多重選擇.列表項的選擇狀態隨着用戶對該項單擊或雙擊鼠標而翻轉.

LBS_NOINTEGRALHEIGHT

列表框的尺寸由應用程序而不是Windows指定.通常,Windows指定尺寸會使列表項的某些部分隱藏起來.

LBS_NOREDRAW

當選擇發生變化時防止列表框被更新,可發送WM_SETREDRAW來改變該風格.

LBS_NOTIFY

當用戶單擊或雙擊鼠標時通知父窗口.

LBS_OWNERDRAWFIXED

指定自繪式列表框,即由父窗口負責繪製列表框的內容,並且列表項有相同的高度.

LBS_OWNERDRAWVARIABLE

指定自繪式列表框,並且列表項有不同的高度.

LBS_SORT

使插入列表框中的項按升序排列.

LBS_STANDARD

相當於指定了WS_BORDER|WS_VSCROLL|LBS_SORT |LBS_NOTIFY.

LBS_USETABSTOPS

使列表框在顯示列表項時識別並擴展製表符(‘/t’),缺省的製表寬度是32個對話框單位.

LBS_WANTKEYBOARDINPUT

允許列表框的父窗口接收WM_VKEYTOITEM和WM_CHARTOITEM消息,以響應鍵盤輸入.

LBS_DISABLENOSCROLL

使列表框在不需要滾動時顯示一個禁止的垂直滾動條.

 

 

除了上表中的風格外,一般還要爲列表框控件指定WS_CHILD、WS_VISIBLE、WS_TABSTOP、WS_BORDER和WS_VSCROLL風格.要創建一個普通的單選擇列表框,應指定的風格爲WS_CHILD|WS_VISIBLE|WS_TABSTOP|LBS_STANDARD.要創建一個多重選擇列表框,應該在單選擇列表框風格的基礎上再加上 LBS_MULTIPLESEL或LBS_ EXTENDEDSEL.如果不希望列表框排序,就不能使用LBS_STANDARD風格.

對於用對話框模板編輯器創建的列表框控件,可以在控件的屬性對話框中指定表6.10中列出的控件風格。例如,在屬性對話框中選擇Sort項,相當與指定了LBS_SORT風格。

CListBox類的成員函數有數十個之多.我們可以把一些常用的函數分爲三類,在下面列出.需要說明的是,可以用索引來指定列表項,索引是從零開始的.

首先,CListBox成員函數提供了下列函數用於插入和刪除列表項.

int AddString( LPCTSTR lpszItem );
該函數用來往列表框中加入字符串,其中參數lpszItem指定了要添加的字符串.函數的返回值是加入的字符串在列表框中的位置,如果發生錯誤,會返回LB_ERR或LB_ERRSPACE(內存不夠).如果列表框未設置LBS_SORT風格,那麼字符串將被添加到列表的末尾,如果設置了LBS_SORT風格,字符串會按排序規律插入到列表中.

int InsertString( int nIndex, LPCTSTR lpszItem );
該函數用來在列表框中的指定位置插入字符串.參數nIndex給出了插入位置(索引),如果值爲-1,則字符串將被添加到列表的末尾.參數lpszItem指定了要插入的字符串.函數返回實際的插入位置,若發生錯誤,會返回LB_ERR或LB_ERRSPACE.與AddString函數不同,InsertString函數不會導致LBS_SORT風格的列表框重新排序.不要在具有LBS_SORT風格的列表框中使用InsertString函數,以免破壞列表項的次序.

int DeleteString( UINT nIndex );
該函數用於刪除指定的列表項,其中參數nIndex指定了要刪除項的索引.函數的返回值爲剩下的表項數目,如果nIndex超過了實際的表項總數,則返回LB_ERR.

void ResetContent( );
該函數用於清除所有列表項.

int Dir( UINT attr, LPCTSTR lpszWildCard );
該函數用來向列表項中加入所有與指定通配符相匹配的文件名或驅動器名.參數attr爲文件類型的組合,如表6.11所示.參數lpszWildCard指定了通配符(如*.cpp,*.*等).

 

表6.11 Dir函數attr參數的含義

含義

0x0000

普通文件(可讀寫的文件).

0x0001

只讀文件.

0x0002

隱藏文件.

0x0004

系統文件.

0x0010

目錄.

0x0020

文件的歸檔位已被設置.

0x4000

包括了所有與通配符相匹配的驅動器.

0x8000

排除標誌.若指定該標誌,則只列出指定類型的文件名,否則,先要列出普通文件,然後再列出指定的文件.

 

 

下列的CListBox成員函數用於搜索、查詢和設置列表框.

int GetCount( ) const;
該函數返回列表項的總數,若出錯則返回LB_ERR.

int FindString( int nStartAfter, LPCTSTR lpszItem ) const;
該函數用於對列表項進行與大小寫無關的搜索.參數nStartAfter指定了開始搜索的位置, 合理指定nStartAfter可以加快搜索速度,若nStartAfter爲-1,則從頭開始搜索整個列表.參數lpszItem指定了要搜索的字符串.函數返回與lpszItem指定的字符串相匹配的列表項的索引,若沒有找到匹配項或發生了錯誤,函數會返回LB_ERR.FindString函數先從nStartAfter指定的位置開始搜索,若沒有找到匹配項,則會從頭開始搜索列表.只有找到匹配項,或對整個列表搜索完一遍後,搜索過程纔會停止,所以不必擔心會漏掉要搜索的列表項.

int GetText( int nIndex, LPTSTR lpszBuffer ) const;
void GetText( int nIndex, CString& rString ) const;
用於獲取指定列表項的字符串.參數nIndex指定了列表項的索引.參數lpszBuffer指向一個接收字符串的緩衝區.引用參數rString則指定了接收字符串的CString對象.第一個版本的函數會返回獲得的字符串的長度,若出錯,則返回LB_ERR.

int GetTextLen( int nIndex ) const;
該函數返回指定列表項的字符串的字節長度.參數nIndex指定了列表項的索引.若出錯則返回LB_ERR.

DWORD GetItemData( int nIndex ) const;
每個列表項都有一個32位的附加數據.該函數返回指定列表項的附加數據,參數nIndex指定了列表項的索引.若出錯則函數返回LB_ERR.

int SetItemData( int nIndex, DWORD dwItemData );
該函數用來指定某一列表項的32位附加數據.參數nIndex指定了列表項的索引.dwItemData是要設置的附加數據值.

提示:列表項的32位附加數據可用來存儲與列表項相關的數據,也可以放置指向相關數據的指針.這樣,當用戶選擇了一個列表項時,程序可以從附加數據中快速方便地獲得與列表項相關的數據.

 

 

int GetTopIndex( ) const;
該函數返回列表框中第一個可見項的索引,若出錯則返回LB_ERR.

int SetTopIndex( int nIndex );
用來將指定的列表項設置爲列表框的第一個可見項,該函數會將列表框滾動到合適的位置.參數nIndex指定了列表項的索引.若操作成功,函數返回0值,否則返回LB_ERR.

提示:由於列表項的內容一般是不變的,故CListBox未提供更新列表項字符串的函數.如果要改變某列表項的內容,可以先調用DeleteString刪除該項,然後再用InsertString或AddString將更新後的內容插入到原來的位置.

下列CListBox的成員函數與列表項的選擇有關.

int GetSel( int nIndex ) const;
該函數返回指定列表項的狀態.參數nIndex指定了列表項的索引.如果查詢的列表項被選擇了,函數返回一個正值,否則返回0,若出錯則返回LB_ERR.

int GetCurSel( ) const;
該函數僅適用於單選擇列表框,用來返回當前被選擇項的索引,如果沒有列表項被選擇或有錯誤發生,則函數返回LB_ERR.

int SetCurSel( int nSelect );
該函數僅適用於單選擇列表框,用來選擇指定的列表項.該函數會滾動列表框以使選擇項可見.參數nIndex指定了列表項的索引,若爲-1,那麼將清除列表框中的選擇.若出錯函數返回LB_ERR.

int SelectString( int nStartAfter, LPCTSTR lpszItem );
該函數僅適用於單選擇列表框,用來選擇與指定字符串相匹配的列表項.該函數會滾動列表框以使選擇項可見.參數的意義及搜索的方法與函數FindString類似.如果找到了匹配的項,函數返回該項的索引,如果沒有匹配的項,函數返回LB_ERR並且當前的選擇不被改變.

int GetSelCount( ) const;
該函數僅用於多重選擇列表框,它返回選擇項的數目,若出錯函數返回LB_ERR.

int SetSel( int nIndex, BOOL bSelect = TRUE );
該函數僅適用於多重選擇列表框,它使指定的列表項選中或落選.參數nIndex指定了列表項的索引,若爲-1,則相當於指定了所有的項.參數bSelect爲TRUE時選中列表項,否則使之落選.若出錯則返回LB_ERR.

int GetSelItems( int nMaxItems, LPINT rgIndex ) const;
該函數僅用於多重選擇列表框,用來獲得選中的項的數目及位置.參數nMaxItems說明了參數rgIndex指向的數組的大小.參數rgIndex指向一個緩衝區,該數組是一個整型數組,用來存放選中的列表項的索引.函數返回放在緩衝區中的選擇項的實際數目,若出錯函數返回LB_ERR.

int SelItemRange( BOOL bSelect, int nFirstItem, int nLastItem );
該函數僅用於多重選擇列表框,用來使指定範圍內的列表項選中或落選.參數nFirstItem和nLastItem指定了列表項索引的範圍.如果參數bSelect爲TRUE,那麼就選擇這些列表項,否則就使它們落選.若出錯函數返回LB_ERR.

在6.1.8小節的例子中,讀者將會看到對列表框的測試.6.1.7 組合框控件

組合框把一個編輯框和一個單選擇列表框結合在了一起.用戶既可以在編輯框中輸入,也可以從列表框中選擇一個列表項來完成輸入.如上一章所提到的,組合框分爲簡易式(Simple)、下拉式(Dropdown)和下拉列表式(Drop List)三種.簡易式組合框包含一個編輯框和一個總是顯示的列表框。下拉式組合框同簡易式組合框類似,二者的區別在於僅當單擊下滾箭頭後列表框纔會彈出。下拉列表式組合框也有一個下拉的列表框,但它的編輯框是隻讀的,不能輸入字符。

Windows中比較常用的是下拉式和下拉列表式組合框,在Developer Studio中就大量使用了這兩種組合框.二者都具有佔地小的特點,這在界面日益複雜的今天是十分重要的.下拉列表式組合框的功能與列表框類似.下拉式組合框的典型應用是作爲記事列表框使用,既把用戶在編輯框中敲入的東西存儲到列表框組件中,這樣當用戶要重複同樣的輸入時,可以從列表框組件中選取而不必在編輯框組件中從新輸入.在Developer Studio中的Find對話框中就可以找到一個典型的下拉式組合框.

要設計一個記事列表框,應採取下列原則:

在創建組合框時指定CBS_DROPDOWNLIST風格.

要限制列表項的數目,以防止內存不夠.

如果在編輯框中輸入的字符串不能與列表框組件中的列表項匹配,那麼應該把該字符串插入到列表框中的0位置處.最老的項處於列表的末尾.如果列表項的數目超出了限制,則應把最老的項刪除.

如果在編輯框中輸入的字符串可以與列表框組件中的某一項完全匹配,則應該先把該項從列表的當前位置刪除,然後在將其插入道列表的0位置處.

 

組合框控件會向父窗口發送表6.12所示的通知消息.

 

表6.12 組合框控件的通知消息

消息

含義

CBN_CLOSEUP

組合框的列表框組件被關閉.簡易式組合框不會發出該消息.

CBN_DBLCLK

用戶在某列表項上雙擊鼠標.只有簡易式組合框纔會發出該消息.

CBN_DROPDOWN

組合框的列表框組件下拉.簡易式組合框不會發出該消息.

CBN_EDITCHANGE

編輯框的內容被用戶改變了。與CBN_EDITUPDATE不同,該消息是在編輯框顯示的正文被刷新後才發出的。下拉列表式組合框不會發出該消息.

CBN_EDITUPDATE

在編輯框準備顯示改變了的正文時發送該消息。下拉列表式組合框不會發出該消息.

CBN_ERRSPACE

組合框無法申請足夠的內存來容納列表項.

CBN_SELENDCANCEL

表明用戶的選擇應該取消.當用戶在列表框中選擇了一項,然後又在組合框控件外單擊鼠標時就會導致該消息的發送.

CBN_SELENDOK

用戶選擇了一項,然後按了回車鍵或單擊了下滾箭頭.該消息表明用戶確認了自己所作的選擇.

CBN_KILLFOCUS

組合框失去了輸入焦點.

CBN_SELCHANGE

用戶通過點擊或移動箭頭鍵改變了列表的選擇.

CBN_SETFOCUS

組合框獲得了輸入焦點.

 

 

MFC的CComboBox類封裝了組合框.需要指出的是,雖然組合框是編輯框和列表框的選擇,但是CComboBox類並不是CEdit類和CListBox類的派生類,而是CWnd類的派生類.

CComboBox的成員函數Create負責創建組合框,該函數的說明如下:

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

參數dwStyle指定了組合框控件的風格,如表6.10所示,dwStyle可以是這些風格的組合.rect說明的是列表框組件下拉後組合框的位置和尺寸.pParentWnd指向父窗口,該參數不能爲NULL。nID則說明了控件的ID。如果創建成功,該函數返回TRUE,否則返回FALSE.

提示:在用Create函數創建組合框時,參數rect說明的是包括列表框組件在內的組合框的位置和尺寸,而不是列表框組件隱藏時的編輯框組件尺寸.要設置編輯框組件的高度,可以調用成員函數SetItemHeight(-1,cyItemHeight),其中參數cyItemHeight指定了編輯框的高度(以像素爲單位).

表6.13 組合框的風格

控件風格

含義

CBS_AUTOHSCROLL

使編輯框組件具有水平滾動的風格.

CBS_DROPDOWN

指定一個下拉式組合框.

CBS_DROPDOWNLIST

指定一個下拉列表式組合框.

CBS_HASSTRINGS

指定一個含有字符串的自繪式組合框.

CBS_OEMCONVERT

使編輯框組件中的正文可以在ANSI字符集和OEM字符集之間相互轉換。這在編輯框中包含文件名時是很有用的。

CBS_OWNERDRAWFIXED

指定自繪式組合框,即由父窗口負責繪製列表框的內容,並且列表項有相同的高度.

CBS_OWNERDRAWVARIABLE

指定自繪式組合框,並且列表項有不同的高度.

CBS_SIIMPLE

指定一個簡易式組合框.

CBS_SORT

自動對列表框組件中的項進行排序.

CBS_DISABLENOSCROLL

使列表框在不需要滾動時顯示一個禁止的垂直滾動條.

CBS_NOINTEGRALHEIGHT

組合框的尺寸由應用程序而不是Windows指定.通常,由Windows指定尺寸會使列表項的某些部分隱藏起來.

 

 

CBS_SIMPLE、CBS_DROPDOWN和CBS_DROPDOWNLIST分別用來將組合框指定爲簡易式、下拉式和下拉列表式.一般還要爲組合框指定WS_CHILD、WS_VISIBLE、WS_TABSTOP、WS_VSCROLL和CBS_AUTOHSCROLL風格.如果要求自動排序,還應指定CBS_SORT風格.

對於用對話框模板編輯器創建的組合框控件,可以在控件的屬性對話框中指定上表中列出的控件風格。例如,在屬性對話框中選擇Dropdown,相當於指定了CBS_DROPDOWN.

CComboBox類的成員函數較多.其中常用的函數可粗分爲兩類,分別針對編輯框組件和列表框組件.可以想象,這些函數與CEdit類和CListBox類的成員函數肯定有很多類似之處,但它們也會有一些不同的特點.如果讀者能從"組合框是由編輯框和列表框組成"這一概念出發,就能夠很快的掌握CComboBox的主要成員函數.

事實上,絕大部分CComboBox的成員函數都可以看成是CEdit或CListBox成員函數的翻版.函數的功能,函數名,甚至函數的參數都是類似的.爲了方便學習,在下面列出CComboBox類的成員函數時,採用了與對應的CEdit或CListBox成員函數相比較的做法.在成員函數的列表中,分別列出了成員函數名,對應的CEdit或CListBox成員函數,以及二者之間的不同之處.不同之處是指函數的功能、參數以及返回值有什麼差別.

針對編輯框組件的主要成員函數如表6.14所示.該表的前三個函數實際上是CWnd類的成員函數,可用來查詢和設置編輯框組件.

 

表6.14 針對編輯框組件的CComboBox成員函數

成員函數名

對應的CEdit成員函數

與CEdit成員函數的不同之處

CWnd::GetWindowText

CWnd::GetWindowText

無.

CWnd::SetWindowText

CWnd::SetWindowText

無.

CWnd::GetWindowTextLength

CWnd::GetWindowTextLength

 

GetEditSel

GetSel的第一個版本

僅函數名不同.

SetEditSel

SetSel的第二個版本

函數名不同,且無bNoScroll參數.

Clear

Clear

無.

Copy

Copy

無.

Cut

Cut

無.

Paste

Paste

無.

 

 

與CListBox的成員函數類似,針對列表框組件的CComboBox成員函數也可以分爲三類.表6.15列出了用於插入和刪除列表項的成員函數,表6.16列出了用於搜索、查詢和設置列表框的成員函數,與列表項的選擇有關的成員函數在表6.17中列出.需要指出的是,如果這些函數出錯,則反回CB_ERR,而不是LB_ERR.另外,排序的組合框具有的是CBS_SORT風格,而不是LBS_SORT.

 

 

6.15 用於插入和刪除列表項的CComboBox成員函數

成員函數名

對應的CListBox成員函數

與CListBox成員函數的不同之處

AddString

AddString

無.

InsertString

InsertString

無.

DeleteString

DeleteString

無.

ResetContent

ResetContent

無.

Dir

Dir

無.

 

 

6.16 用於搜索、查詢和設置列表框的CComboBox成員函數

成員函數名

對應的CListBox成員函數

與CListBox成員函數的不同之處

GetCount

GetCount

無.

FindString

FindString

無.

GetLBText

GetText

僅函數名不同.

GetLBTextLen

GetTextLen

僅函數名不同.

GetItemData

GetItemData

無.

SetItemData

SetItemData

無.

GetTopIndex

GetTopIndex

無.

SetTopIndex

SetTopIndex

無.

 

 

表6.17 與列表項的選擇有關的CComboBox成員函數

成員函數名

對應的CListBox成員函數

與CListBox成員函數的不同之處

GetCurSel

GetCurSel

無.

SetCurSel

SetCurSel

新選的列表項的內容會被拷貝到編輯框組件中.

SelectString

SelectString

新選的列表項的內容會被拷貝到編輯框組件中.

 

 

另外,CComboBox的ShowDropDown成員函數專門負責顯示或隱藏列表框組件,該函數的聲明爲

void ShowDropDown( BOOL bShowIt = TRUE );

 

如果參數bShowIt的值爲TRUE,那麼將顯示列表框組件,否則,就隱藏之.該函數對簡易式組合框沒有作用.

 6.1.8 測試傳統控件的一個例子

現在讓我們編寫一個程序來測試一下上面介紹的一些傳統控件.該程序名爲CtrlTest,其界面如圖6.1所示.前面介紹的程序都是基於框架窗口的,而CtrlTest程序是一個基於對話框的應用程序,即以對話框作爲程序的主窗口.該程序主要對組合框、列表框、多行編輯框和滾動條控件進行了測試,其中:

Input組合框是一個記事列表框.在編輯框組件中輸入字符串,或從列表框組件中選擇以前輸入過的字符串,然後按Add按鈕,該字符串就會被加入到List列表框中.

List列表框是一個多重選擇列表框.該列表框具有LBS_EXTENDEDSEL風格,用戶可以單擊鼠標進行單項選擇,也可以按住Shift或Ctrl鍵後單擊鼠標來進行多重選擇.用戶可以按Delete鍵刪除列表框中選擇的項.

History of SELCHANGE多行編輯框.該編輯框用於跟蹤Input組合框的列表框組件發出的CBN_SELCHANGE通知消息,編輯框對該消息的響應是顯示XXXX selected,以表明用戶新選擇了一個列表項.讀者通過該編輯框可以瞭解組合框是在什麼情況下發送CBN_SELCHANGE通知消息的.按Clear按鈕將清除編輯框.

水平滾動條控件的滾動範圍是0 — 50.在滾動條的左邊有一個靜態正文控件用來動態反映當前滾動框的位置.

T6_1.tif (122740 bytes)

圖6.1 CtrlTest程序

 

首先,讓我們用AppWizard建立一個基於對話框的MFC應用程序.這一過程很簡單,先將新建的工程命名爲CtrlTest,然後在MFC AppWizard對話框的第一步中選擇Dialog based就行了.

AppWizard會自動建立一個用於應用程序主窗口的對話框模板IDD_CTRLTEST_DIALOG及其對應的對話框類CCtrlTestDlg.對該對話框的使用與普通對話框並沒有什麼不同,只不過在程序啓動後對話框會自動顯示出來,而當用戶關閉對話框後,應用程序也就終止了.如果讀者觀察CCtrlTestApp:: InitInstance函數就會發現,該函數調用DoModal來顯示一個CCtrlTestDlg對話框,並使m_pMainWnd指針指向CCtrlTestDlg對象,從而使該對話框成爲程序的主窗口.

接下來,需要設計IDD_CTRLTEST_DIALOG對話框模板.請讀者將該模板上除OK按鈕以外的控件都刪除掉,將OK按鈕的標題改爲Cl&ose,並去掉該按鈕的Default button(缺省按鈕)屬性.當用戶在對話框內按回車鍵時,會激活缺省按鈕,一般應該把用來確認用戶輸入操作的按鈕設計成缺省按鈕.在本例中,顯然應該把Add按鈕設計成缺省按鈕,而不是Close按鈕.這樣,用戶在Input組合框中輸入字符串後,按回車鍵就可以將該串加入到List列表框中.

請讀者根據圖6.1和表6.18,向IDD_CTRLTEST_DIALOG對話框模板中加入測試用的控件.

 

表6.18

控件類型 ID 標題(Caption) 其它屬性

靜態正文

缺省

Input:

缺省.

組合框

ID_COMBOBOX

 

去掉Sort屬性.

命令按鈕

IDC_ADD

&Add

選擇Default button屬性.

靜態正文

缺省

List:

缺省.

列表框

IDC_LISTBOX

 

在Selection欄中選擇Extended,並去掉Sort屬性.

命令按鈕

IDC_DELETE

&Delete

缺省.

靜態正文

缺省

History of SELCHANGE

缺省.

編輯框

IDC_MULTIEDIT

 

選擇Multi-line,Vertical scroll,AutoVScroll和Want return屬性.

命令按鈕

IDC_CLEAR

&Clear

缺省.

靜態正文

IDC_INDICATOR

缺省

缺省.

滾動條

IDC_SCROLLBAR

 

缺省.

 

 

接着,利用ClassWizard爲CCtrlTestDlg類加入成員變量,如表6.19所示,這些成員變量都是控件對象.

 

表6.19 CCtrlTestDlg類的成員變量

控件ID

變量類型

變量名

IDC_COMBOBOX

CComboBox

m_ComboBox

IDC_LISTBOX

CListBox

m_ListBox

IDC_MULTIEDIT

CEdit

m_MultiEdit

IDC_INDICATOR

CStatic

m_Indicator

IDC_SCROLLBAR

CScrollBar

m_ScrollBar

 

 

接下來,用ClassWizard爲CCtrlTestDlg類加入控件通知消息處理函數,如表6.20所示.

 

表6.20 CCtrlTestDlg的控件通知消息處理函數

Object IDS

Messages

Member functions

IDC_ADD

BN_CLICKED

OnAdd(缺省名)

IDC_DELETE

BN_CLICKED

OnDelete(缺省名)

IDC_CLEAR

BN_CLICKED

OnClear(缺省名)

IDC_COMBOBOX

CBN_SELCHANGE

OnSelchangeCombobox(缺省名)

CCtrlTestDlg

WM_HSCROLL

OnHScroll(缺省名)

 

最後,請讀者按清單6.1修改源代碼,限於篇幅,這裏僅列出需要手工修改的那一部分.

 

清單6.1 CCtrlTestDlg類的部分源代碼

// CtrlTestDlg.cpp : implementation file

//

 

#define MAX_HISTORY 5

. . . . . .

BOOL CCtrlTestDlg::OnInitDialog()

{

 

. . . . . .

// TODO: Add extra initialization here

 

m_ScrollBar.SetScrollRange(0,50);

m_Indicator.SetWindowText("0");

m_ComboBox.SetFocus(); //使組合框獲得輸入焦點

 

return FALSE; // 返回FALSE以表明爲某一控件設置了輸入焦點

}

 

void CCtrlTestDlg::OnAdd()

{

// TODO: Add your control notification handler code here

 

int i;

CString str;

m_ComboBox.GetWindowText(str);

m_ListBox.AddString(str);

i=m_ComboBox.FindString(-1,str);

if(i>=0)

{

m_ComboBox.DeleteString(i);

m_ComboBox.InsertString(0,str); //將匹配項移到0位置

}

else

{

m_ComboBox.InsertString(0,str);

if(m_ComboBox.GetCount()>MAX_HISTORY)

m_ComboBox.DeleteString(m_ComboBox.GetCount()-1); //刪除舊的項

}

}

 

void CCtrlTestDlg::OnClear()

{

// TODO: Add your control notification handler code here

 

m_MultiEdit.SetSel(0,-1);

m_MultiEdit.ReplaceSel("");

}

 

void CCtrlTestDlg::OnDelete()

{

// TODO: Add your control notification handler code here

 

int i,count;

int *pBuffer;

count=m_ListBox.GetSelCount();

if(count<=0)return;

pBuffer=new int[count];

m_ListBox.GetSelItems(count,pBuffer);

for(i=count-1;i>=0;i--) //倒序刪除選擇項

m_ListBox.DeleteString(pBuffer[i]);

delete pBuffer;

}

 

void CCtrlTestDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{

// TODO: Add your message handler code here and/or call default

 

int nScrollMin,nScrollMax,nScrollPos;

int nPageSize;

CString str;

if(&m_ScrollBar!=pScrollBar)return;

nScrollPos=m_ScrollBar.GetScrollPos();

m_ScrollBar.GetScrollRange(&nScrollMin,&nScrollMax);

nPageSize=(nScrollMax-nScrollMin)/10; //指定頁長

switch(nSBCode)

{

case SB_LEFT:

nScrollPos=nScrollMin;

break;

case SB_RIGHT:

nScrollPos=nScrollMax;

break;

case SB_LINELEFT:

nScrollPos-=1;

break;

case SB_LINERIGHT:

nScrollPos+=1;

break;

case SB_PAGELEFT:

nScrollPos-=nPageSize;

break;

case SB_PAGERIGHT:

nScrollPos+=nPageSize;

break;

case SB_THUMBPOSITION:

nScrollPos=nPos; //由參數nPos獲取滾動框的位置

break;

case SB_THUMBTRACK:

nScrollPos=nPos; //由參數nPos獲取滾動框的位置

break;

default:;

}

if(nScrollPos<nScrollMin)nScrollPos=nScrollMin;

if(nScrollPos>nScrollMax)nScrollPos=nScrollMax;

if(nScrollPos!=m_ScrollBar.GetScrollPos())

m_ScrollBar.SetScrollPos(nScrollPos); //設置滾動框的新位置

str.Format("%d",nScrollPos);

m_Indicator.SetWindowText(str); //更新靜態正文

 

CDialog::OnHScroll(nSBCode, nPos, pScrollBar);

}

 

void CCtrlTestDlg::OnSelchangeCombobox()

{

// TODO: Add your control notification handler code here

 

int length=m_MultiEdit.GetWindowTextLength();

CString str;

m_MultiEdit.SetSel(-1,0);

m_MultiEdit.SetSel(length,length); //移動插入符到編輯正文的末尾

m_ComboBox.GetLBText(m_ComboBox.GetCurSel(),str);

str+=" selected/r/n";

m_MultiEdit.ReplaceSel(str);

}

  在OnInitDialog成員函數中,對一些控件進行了初始化,包括設置滾動條的範圍,將靜態正文的顯示置爲 “0”,以及使組合框獲得輸入焦點.注意,缺省時,OnInitDialog返回TRUE,而新版的函數返回了FALSE.如果OnInitDialog返回TRUE,那麼Windows將使tab順序最靠前的可輸入控件獲得輸入焦點,如果返回FALSE,則表明在OnInitDialog函數中人爲地使某個控件獲得輸入焦點,函數返回後系統就不會再設置輸入焦點了.有時,只要合理的安排了控件的tab順序,就不必在OnInitDialog中人爲設置輸入焦點.

  當用戶點擊Add按鈕或按回車鍵後,成員函數OnAdd被調用.該函數將組合框的編輯框中的字符串加入到List列表框的末尾,並將該字符串存入到記事列表框中.這時函數會判斷,如果在記事列表中沒有匹配的項,則把字符串插入0位置,並在必要時刪除最老的列表項,在本例中,記事列表框最多可以容納5項;如果在記事列表中有匹配的項,那麼就把該項移到0位置.

  當用戶點擊Delete按鈕時,成員函數OnDelete被調用,該函數根據CComboBox::GetSelCount獲得選擇項的數目,並根據這個數目動態創建一個整型數組以存放選擇項的索引.然後,調用CComboBox::GetSelItems來獲取選擇項的索引.最後,把這些選擇項刪除.注意,這裏是倒序刪除的,如果按順序刪除,則會使選擇項的索引產生錯位.

  成員函數OnSelchangeCombobox是Input組合框的CBN_SELCHANGE消息的處理函數.該函數先把多行編輯框的插入符移到編輯正文的末尾,然後從插入符處加入一行形如"XXXX selected"的字符串,以表明用戶從記事列表框中新選擇了哪個列表項.值得一提的是,上一章的Register程序是用SetWindowText來在編輯正文中插入新的正文的,此方法有一個缺點,就是不能把插入符滾動到新修改過的地方.在本例中,插入編輯正文的方法是先調用CEdit::SetSel移動插入符到指定位置(必要時要滾動以使該位置可見),然後再調用CEdit::ReplaceSel插入新的正文,這樣做的好處是編輯框總是滾動到新修改過的地方,從而使得新修改過的地方總是可見的.

  對滾動條控件的測試是在OnHScroll成員函數中完成的.該函數是對話框也即父窗口對水平滾動條控件產生的WM_HSCROLL消息的處理函數.該函數負責移動滾動框並及時更新靜態正文的顯示以反映滾動框的當前位置.在函數的開頭,首先判斷是不是m_ScrollBar滾動條發來的消息,這是因爲可能會有幾個滾動條控件.在該函數中有一個大的switch分枝語句,用來獲取滾動框的新位置.需要指出的是,對於SB_THUMBPOSITION和SB_THUMBTRACK這兩種情況,應該從OnHScroll函數的nPos參數中獲取滾動框的新位置.對於SB_THUMBTRACK,不要企圖用CScrollBar::GetScrollPos來獲取滾動框的新位置,因爲該函數不能正確返回拖動時的滾動框位置.另一個要注意的問題是Windows本身不會自動地使滾動框移動到新位置上,所以需要在OnHScroll中調用CScrollBar::SetScrollPos來移動滾動框.

 

6.2 新的Win32控件

  從Windows 95和Windows NT 3.51版開始,Windows提供了一些先進的Win32控件.這些新控件彌補了傳統控件的某些不足之處,並使Windows的界面豐富多彩且更加友好.MFC的新控件類封裝了這些控件,新控件及其對應的控件類如表6.21所示.表6.21 新的Win32控件及其控件類

控件名

功能

對應的控件類

動畫(Animate)

可播放avi文件.

CAnimateCtrl

熱鍵(Hot Key)

使用戶能選擇熱鍵組合.

CHotKeyCtrl

列表視圖(List View)

能夠以列表、小圖標、大圖標或報告格式顯示數據.

CListCtrl

進度條(Progress Bar)

用於指示進度.

CProgressCtrl

滑尺(Slider)

也叫軌道條(Trackbar),用戶可以移動滑尺來在某一範圍中進行選擇.

CSliderCtrl

旋轉按鈕(Spin Button)

有時被稱爲上下控件.有一對箭頭按鈕,用來調節某一值的大小.

CSpinButtonCtrl

標籤(Tab)

用來作爲標籤使用.

CTabCtrl

樹形視圖(Tree View)

以樹狀結構顯示數據.

CTreeCtrl

本節將主要介紹列表視圖、樹形視圖、進度條、旋轉按鈕和滑尺控件,動畫控件將在第十二章介紹.6.2.1 Win32控件的通知消息

  較之傳統的Windows 3.x控件,新的Win32控件更加複雜和先進.在新控件發送通知消息的同時,往往還需要附加一些數據來描述控件的狀態.傳統的WM_COMMAND消息通知機制顯然不能完成這一任務,因爲WM_COMMAND消息的wParam和lParam已經被佔滿了(見6.1.1),無法容納新的數據.

  在Win32中,採用新的WM_NOTIFY消息來實現新控件的消息通知機制.在該消息的wParam中含有控件的ID,lParam中則有一個指針,這個指針指向一個結構.這個結構要麼是NMHDR結構,要麼是一個以NMHDR結構作爲第一個成員的擴充結構.通過NMHDR結構及其擴充結構可以傳遞附加數據.從理論上講,可以通過擴充結構傳送任意多的數據.需要指出的是,由於NMHDR結構是擴充結構的第一個成員,因此lParam中的指針即可以認爲是指向NMHDR結構的,也可以認爲指向包含NMHDR結構的擴充結構的.

NMHDR結構如下所示:

typedef struct tagNMHDR {

HWND hwndFrom; //控件窗口的句柄

UINT idFrom; //控件的ID

UINT code; //控件的通知消息碼

} NMHDR;

一個典型的擴充結構如下所示,該結構用於列表視圖控件的LVN_KEYDOWN通知消息.

typedef struct tagLV_KEYDOWN {

NMHDR hdr; //NMHDR結構作爲第一個成員

WORD wVKey;

UINT flags;

} LV_KEYDOWN;

有些控件通知消息是所有Win32控件共有的,這些消息在表6.22中列出.

 

表6.22 Win32控件共有的通知消息

通知消息碼

含義

NM_CLICK

用戶在控件上單擊鼠標左鍵.

NM_DBLCLK

用戶在控件上雙擊鼠標左鍵.

NM_RCLICK

用戶在控件上單擊鼠標右鍵.

NM_RDBLCLK

用戶在控件上雙擊鼠標右鍵.

NM_RETURN

用戶在控件上按回車鍵.

NM_SETFOCUS

控件獲得輸入焦點.

NM_KILLFOCUS

控件失去輸入焦點.

NM_OUTOFMEMORY

內存不夠.

 

 

WM_NOTIFY的消息映射由宏ON_NOTIFY負責,該消息映射宏具有如下形式:

ON_NOTIFY( wNotifyCode, id, memberFxn )

參數wNotifyCode說明了通知消息碼,參數id是控件的ID,第三個參數則是消息處理函數名.消息處理函數應該按下面的形式聲明,其中參數pNotifyStruct指向NMHDR及其擴充結構,參數result指向一個處理結果.

afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result );

利用ClassWizard可以很方便地加入WM_NOTIFY消息映射及其處理函數,一個典型的WM_NOTIFY消息映射如下所示,其中LVN_KEYDOWN是IDC_LIST1列表視圖控件發出的通知消息.

ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )

消息處理函數OnKeydownList1的定義如下面所示.在函數中ClassWizard自動把pNHHDR指針強制轉換成LV_KEYDOWN型並賦給pLVKeyDow指針,這樣,在函數中可通過這兩個指針訪問LV_KEYDOWN擴充結構及其所含的NMHDR結構.另外,在函數返回時,ClassWizard自動將處理結果賦0值.

void CMyDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)

{

LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;

// TODO: Add your control notification handler

// code here

*pResult = 0;

}

可以利用ON_NOTIFY_RANGE宏把多個ID連續的控件發出的相同消息映射到同一個處理函數上,具體形式如下,其中參數id和idLast分別說明明瞭一組連續的控件ID中的頭一個和最後一個ID.

ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )

相應的消息處理函數應按下面的形式聲明,與普通的WM_NOTIFY消息處理函數相比,該函數多了一個參數id用來說明發送通知消息的控件ID.

afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );

ClassWizard不支持ON_NOTIFY_RANGE宏,所以需要手工建立消息映射和消息處理函數.6.2.2 旋轉按鈕控件

旋轉按鈕(Spin Button)有時也被稱爲上下控件(Up-Down Control).Windows 95控制面板中的日期/時間程序中就有兩個典型的旋轉按鈕,如圖6.2所示.旋轉按鈕由兩個箭頭按鈕組成,用戶在箭頭按鈕上單擊鼠標可以在某一範圍內增加或減少某一個值.旋轉按鈕一般不會單獨存在,而是和編輯框或靜態正文組成一個多部件控件來共同顯示和控制某一個值,用戶可以用旋轉按鈕修改編輯框中的數字,也可以直接在編輯框中修改.例如,在圖6.2中,在旋轉按鈕的左測有一個編輯框,用戶可以在編輯框中直接輸入新的年份,也可以用旋轉按鈕來增減編輯框中的年份.通常,把與旋轉按鈕在一塊的編輯框或靜態正文稱爲"夥伴"(buddy).

T6_2.tif (164162 bytes)

圖6.2 日期/時間程序中的旋轉按鈕

MFC的CSpinButtonCtrl類封裝了旋轉按鈕的功能.CSpinButtonCtrl的成員函數Create負責創建控件,該函數的聲明爲

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

參數dwStyle是如表6.23所示的各種控件風格的組合.

 

表6.23 旋轉按鈕控件的風格

控件風格

含義

UDS_HORZ

指定一個水平旋轉按鈕.若不指定該風格則創建一個垂直的旋轉按鈕.

UDS_WRAP

當旋轉按鈕增大到超過最大值時,自動重置爲最小值,當減小至低於最小值時,自動重置爲最大值.

UDS_ARROWKEYS

當用戶按下向下或向上箭頭鍵時,旋轉按鈕值遞增或遞減.

UDS_SETBUDDYINT

旋轉按鈕將自動更新夥伴控件中顯示的數值,如果夥伴控件能接受輸入,則可在夥伴控件中輸入新的旋轉按鈕值.

UDS_NOTHOUSANDS

夥伴控件中顯示的數值每隔三位沒有千位分隔符.

UDS_AUTOBUDDY

自動使旋轉按鈕擁有一個夥伴控件.

UDS_ALIGNRIGHT

旋轉按鈕在夥伴控件的右側.

UDS_ALIGNLEFT

旋轉按鈕在夥伴控件的左側.

 

 

除上表的風格外,一般還要爲旋轉按鈕指定WS_CHILD和WS_VISIBLE風格.創建一個有夥伴的垂直旋轉按鈕控件,一般應指定的風格爲WS_CHILD|WS_VISIBLE|UDS_AUTOBUDDY| UDS_SETBUDDYINT.對於用對話框模板創建的旋轉按鈕控件,可以在控件的屬性對話框中指定上表中列出的控件風格。例如,在屬性對話框中選擇Auto buddy,相當於指定了UDS_AUTOBUDDY風格.

在對話框模板中,可以方便地爲旋轉按鈕指定一個夥伴控件.首先,應該在旋轉按鈕控件的屬性對話框中選擇Auto buddy和Set buddy integer屬性,並在Alignment欄中選擇Left或Right,然後就可以確定夥伴控件了.需要指出的是,旋轉按鈕並不是把離它最近的控件作爲夥伴的.夥伴的選擇是以tab順序爲參照的,夥伴控件的tab順序必需緊挨着按鈕控件且比它小.例如,如果某一控件的tab順序是3,而旋轉按鈕的tab順序是4,則不論這兩個控件距離有多遠,在程序運行時,旋轉按鈕都會自動與該控件結合在一起,形成夥伴關係.

提示:在本章的開頭說過,用ClassWizard無法爲Win32控件創建數據變量.但我們可以爲旋轉按鈕的夥伴控件(如編輯框)創建一個數據變量,該變量可看成是旋轉按鈕的數據變量.

通過CSpinButtonCtrl的成員函數,可以對旋轉按鈕進行查詢和設置:

用GetRange和SetRange來查詢和設置旋轉按鈕值的範圍,缺省時值的範圍是1-100.這兩個函數的聲明爲
void GetRange( int &lower, int& upper ) const;
void SetRange( int nLower, int nUpper );
第一個參數是最小值,該值不能小於UD_MINVAL,第二個參數是最大值,該值不能大於UD_MAXVAL.值的範圍不能超過UD_MAXVAL.

用GetPos和SetPos來查詢和設置旋轉按鈕的當前值.函數的聲明爲
int GetPos( ) const;
int SetPos( int nPos );

用GetBase和SetBase來查詢和設置旋轉按鈕值的計數制.函數的聲明爲
UINT GetBase( ) const;
int SetBase( int nBase );
如果參數nBase是10,則夥伴控件中顯示的數值是十進制的,如果nBase是16,則是十六進制的.

用GetBuddy和SetBuddy來查詢和設置旋轉按鈕的夥伴.上面已講了在對話框模板中設置夥伴控件的方法,如果是用Create手工創建旋轉按鈕,則可以用SetBuddy來設置夥伴.函數的聲明爲
CWnd* GetBuddy( ) const;
CWnd* SetBuddy( CWnd* pWndBuddy );
參數pWndBuddy是指向夥伴控件對象的CWnd型指針.

可以用GetAccel和SetAccel來查詢和設置旋轉按鈕的加速值.在平時,在旋轉按鈕上按一下只會增/減一個單位,而當按住按鈕超過一定時間時,遞增或遞減的幅度將會加大到指定的加速值,從而加快了增減的速度.如果對缺省的加速值不滿意,可以用SetAccel設置新的加速值.可以有一套以上的加速值.函數的聲明爲
UINT GetAccel( int nAccel, UDACCEL* pAccel ) const;
BOOL SetAccel( int nAccel, UDACCEL* pAccel );
參數nAccel指定了UDACCEL結構數組的大小.參數pAccel指向一個UDACCEL結構數組.UDACCEL結構含有加速值的信息,其定義如下
typedef struct {

int nSec; //加速值生效需要的時間(以秒爲單位)

int nInc; //加速值

} UDACCEL;

旋轉按鈕常被認爲是一個簡化的滾動條.除了表6.22列出的通知消息外,旋轉按鈕特有的滾動通知消息是通過WM_HSCROLL和WM_VSCROLL消息發出的.消息處理函數OnHScroll或OnVScroll分別用來處理水平或垂直旋轉按鈕的事件通知.由於夥伴控件中的內容會自動隨旋轉按鈕變化,所以旋轉按鈕的通知消息意義不大.如果非要處理通知消息,一個典型的OnVscroll函數如下所示:

void CMyDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{

CSpinButtonCtrl* pSpin=(CSpinButtonCtrl*)pScrollBar;

int nPosition;

if(pSpin= =&m_Spin) //判斷是否是該旋轉按鈕發來的消息

{

nPosition=m_Spin.GetPos( ); //獲取旋轉按鈕的當前值

. . . . . .}

. . . . . .}

6.2.3 滑尺控件

滑尺(Slider)有時也被稱作軌道條(Trackbar),在軌道條中有一個滑尺,在軌道條上通常會標有刻度,用戶通過移動滑尺,可以在一個指定的範圍內選擇一個不精確的值.軌道條可用來調節一個模擬量,也可以用來在一些離散值中進行選擇.在Windows 95中,大量使用了軌道條控件,例如,在控制面板中的鍵盤和鼠標設置程序中就使用了軌道條控件,如圖6.3所示.軌道條不僅接受鼠標輸入,也可以接受象左右箭頭鍵、PgUp和PgDown這樣的鍵盤輸入.

T6_3.tif (182219 bytes)

圖6.3 鼠標設置程序中的軌道條控件

與選擇按鈕不同,軌道條是一種模糊型的輸入控件,用戶不需要進行精確的選擇,只要大致調整一下大小就行了.軌道條的這種特性非常符合人的行爲習慣,因而在有些情況下是很有用,例如,對於音量的調節,顯然用軌道條比用旋轉按鈕更符合人的日常習慣.

軌道條的滑尺的移動具有離散性.例如,如果指定軌道條的範圍是5,那麼滑尺只能在包括軌道條兩端在內的6個均勻的位置上移動.當然,如果範圍很大,則用戶就感覺不出是離散的了.

軌道條控件與傳統的滾動條控件有很多相似之處,實際上,前者是對後者的一種改進.除了表6.22列出的通知消息外,軌道條控件是依靠WM_HSCROLL和WM_VSCROLL來發送與滑尺有關的通知消息的,並且通知消息與滾動條極爲相似.通知消息包括TB_BOTTOM、TB_LINEDOWN、TB_LINEUP、TB_TOP、TB_PAGEDOWN、TB_PAGEUP、TB_ENDTRACK、TB_THUMBPOSITION、TB_THUMBTRACK.對照滾動條的通知消息,讀者不難明白這些消息碼的含義.其中前四個消息只有在用鍵盤移動滑尺時纔會發出,最後兩個消息只有在用鼠標拖動滑尺時纔會發出.與滾動條不同的是,Windows會自動把滑尺移動到新位置上.

MFC的CSliderCtrl類封裝了軌道條.CSliderCtrl類的Create成員函數負責控件的創建,該函數的聲明爲

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

參數參數dwStyle是如表6.24所示的各種控件風格的組合.

 

表6.24 軌道條控件的風格

控件風格

含義

TBS_HORZ

指定一個水平軌道條.該風格是默認的.

TBS_VERT

指定一個垂直軌道條.

TBS_AUTOTICKS

在範圍設定後,自動爲軌道條加上刻度.

TBS_NOTICKS

軌道條無刻度.

TBS_BOTTOM

在水平軌道條的底部顯示刻度,可與TBS_TOP一起使用.

TBS_TOP

在水平軌道條的頂部顯示刻度,可與TBS_BOTTOM一起使用.

TBS_RIGHT

在垂直軌道條的右側顯示刻度,可與TBS_LEFT一起使用.

TBS_LEFT

在垂直軌道條的左側顯示刻度,可與TBS_RIGHT一起使用.

TBS_BOTH

在軌道條的上下部或左右兩側都顯示刻度.

TBS_ENABLESELRANGE

在軌道條中顯示一個選擇範圍.

 

 

除上表的風格外,一般還要爲軌道條指定WS_CHILD和WS_VISIBLE風格.要創建一個具有刻度的水平軌道條,一般應指定風格爲WS_CHILD|WS_VISIBLE|TBS_HORZ| TBS_AUTOTICKS.對於用對話框模板創建的軌道條控件,可以在控件的屬性對話框中指定上表中列出的控件風格。例如,在屬性對話框中選擇Autoticks,相當於指定了TBS_AUTOTICKS風格.

通過調用CSliderCtrl類的成員函數,可以對軌道條進行查詢和設置:

用GetRange和SetRange來查詢和設置軌道條的範圍,缺省的範圍是0-100.函數的聲明爲
void GetRange( int& nMin, int& nMax ) const;
void SetRange( int nMin, int nMax, BOOL bRedraw = FALSE );
參數nMin和nMax分別是最小和最大值,參數bRedraw爲TRUE時將重繪控件.

用GetPos和SetPos來查詢和設置軌道條的當前值.函數的聲明爲
int GetPos( ) const;
void SetPos( int nPos );

用GetLineSize和SetLineSize來查詢和設置在按一下左箭頭鍵或右箭頭鍵時滑尺的移動量,該移動量的缺省值是1個單位.函數的聲明爲
int GetLineSize( ) const;
int SetLineSize( int nSize );

用GetPageSize和SetPageSize來查詢和設置滑尺的塊移動量,塊移動量是指當按下PgUp或PgDown鍵時滑尺的移動量.函數的聲明爲
int GetPageSize( ) const;
int SetPageSize( int nSize );

用SetTicFreq設置軌道條的刻度的頻度.缺省的頻度是每個單位都有一個刻度,在範圍較大時,爲了使刻度不至於過密,需要調用該函數設置一個合理的頻度.函數的聲明爲
void SetTicFreq( int nFreq );
參數nFreq說明了兩個刻度之間的間隔.

用函數SetTic來在指定位置設置刻度.Windows自動顯示的刻度是均勻的,利用該函數可以人爲設置不均勻的刻度,該函數的聲明爲
BOOL SetTic( int nTic );

用函數ClearTics來清除所有的刻度.該函數的聲明爲
void ClearTics( BOOL bRedraw = FALSE );

 

6.2.4 進度條控件

進度條(Progress Bar)的用途是向用戶顯示程序的進度.進度條是Win32控件中最簡單的控件,只需少數設置即可.Windows 95中使用進度條的一個例子是磁盤掃描(ScanDisk)程序,如圖6.4所示.進度條顯示的數據是不精確的,它是一種模糊型的輸出控件.

T6_4.tif (172432 bytes)

圖6.4 磁盤掃描程序中的進度條

MFC的CProgressCtrl類封裝了進度條控件.該類的Create成員函數負責創建控件,該函數的聲明爲

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

進度條沒有專門的風格,所以參數dwStyle只能指定普通的窗口風格,一般只需指定WS_CHILD|WS_VISIBLE就可以了.

CProgressCtrl類提供了少量的成員函數用來設置進度條:

void SetRange( int nLower, int nUpper );
該函數用來設置進度條的範圍.參數nLow和nUpper分別指定了最小和最大值,缺省時進度條的範圍是0-100.

int SetPos( int nPos );
用來設置進度條的當前進度.函數返回的是進度條的前一個進度.

int StepIt( );
使進度增加一個步長,步長值是由SetStep函數設置的,缺省的步長值是10.函數返回進度條的前一個進度.

int SetStep( int nStep );
用來設置步長值.函數返回原來的步長值.

 

6.2.5 樹形視圖控件

樹形視圖(Tree View)是一種特殊的列表,它能以樹形分層結構顯示數據.在Windows 95的資源管理器(Windows Explorer)的左側窗口中就有一個用於顯示目錄的典型的樹形視圖,如圖6.5所示.在樹形視圖中,每個表項顯示一個標題(Label),有時還會顯示一幅圖象,圖象和標題分別提供了對數據的形象和抽象描述.通過圖6.5可以看出,樹形視圖可以很清楚的顯示出數據的分支和層次關係.由此可見,樹形視圖非常適合顯示象目錄,網絡結構等這樣的複雜數據.傳統的列表框不能分層顯示數據,因此樹形視圖可以看作是對列表框的一種重要改進.

T6_5.tif (269628 bytes)

圖6.5 資源管理器中的樹形視圖和列表視圖


樹形視圖是一種複雜的控件,它的複雜性體現在數據項之間具有分支和層次關係.例如,如果要向樹形視圖中加入新的項,則必需描述出該項與樹形視圖中已有項的相互關係,而不可能象往列表框中加入新項那樣,調用一下AddString就完事了.另外,樹形視圖可以在每一項標題的左邊顯示一幅圖象,這使控件顯得更加形象生動,但同時也增加了控件的複雜程度.

在討論如何使用樹形視圖控件以前,有必要先介紹一下與該控件有關的一些數據類型:

HTREEITEM型句柄.Windows用HTREEITEM型句柄來代表樹形視圖的一項,程序通過HTREEITEM句柄來區分和訪問樹形視圖的各個項.

TV_ITEM結構.該結構用來描述一個表項,它包含了表項的各種屬性,其定義如下

typedef struct _TV_ITEM

{ tvi

UINT  mask; /*包含一些屏蔽位(下面的括號中列出)的組合,用來表明結構的哪些成員是有效的*/

HTREEITEM hItem; //表項的句柄(TVIF_HANDLE)

UINT state; //表項的狀態(TVIF_STATE)

UINT stateMask; //狀態的屏蔽組合(TVIF_STATE)

LPSTR pszText; //表項的標題正文(TVIF_TEXT)

int cchTextMax; //正文緩衝區的大小(TVIF_TEXT)

int iImage; //表項的圖象索引(TVIF_IMAGE)

int iSelectedImage; //選中的項的圖象索引(TVIF_SELECTEDIMAGE)

int cChildren; /*表明項是否有子項(TVIF_CHILDREN),爲1則有,爲0則沒有*/

LPARAM lParam; //一個32位的附加數據(TVIF_PARAM)

} TV_ITEM, FAR *LPTV_ITEM;

如果要使樹形視圖的表項顯示圖象,需要爲樹形視圖建立一個位圖序列,這時,iImage說明表項顯示的圖象在位圖序列中的索引,iSelectedImage則說明了選中的表項應顯示的圖象,在繪製圖標時,樹形視圖可以根據這兩個參數提供的索引在位圖序列中找到對應的位圖.lParam可用來放置與表項相關的數據,這常常是很有用的.state和stateMask的常用值在表6.25中列出,其中stateMask用來說明要獲取或設置哪些狀態.

 

表6.25 樹形視圖表項項的常用狀態

狀態

對應的狀態屏蔽

含義

TVIS_SELECTED

同左

項被選中.

TVIS_EXPANDED

同左

項的子項被展開.

TVIS_EXPANDEDONCE

同左

項的子項曾經被展開過.

TVIS_CUT

同左

項被選擇用來進行剪切和粘貼操作.

TVIS_FOCUSED

同左

項具有輸入焦點.

TVIS_DROPHILITED

同左

項成爲拖動操作的目標.

 

 

TV_INSERTSTRUCT結構.在向樹形視圖中插入新項時要用到該結構,其定義爲

typedef struct _TV_INSERTSTRUCT {

HTREEITEM hParent; //父項的句柄

HTREEITEM hInsertAfter; //說明應插入到同層中哪一項的後面

TV_ITEM item;

} TV_INSERTSTRUCT;

如果hParent的值爲TVI_ROOT或NULL,那麼新項將被插入到樹形視圖的最高層(根位置).hInsertAfter的值可以是TVI_FIRST、TVI_LAST或TVI_SORT,其含義分別是將新項插入到同一層中的開頭、最後或排序插入.

NM_TREEVIEW結構.樹形視圖的大部分通知消息都會附帶指向該結構的指針以提供一些必要的信息.該結構的定義爲

typedef struct _NM_TREEVIEW { nmtv

NMHDR hdr; //標準的NMHDR結構

UINT action; //表明是用戶的什麼行爲觸發了該通知消息

TV_ITEM itemOld; //舊項的信息

TV_ITEM itemNew; //新項的信息

POINT ptDrag; //事件發生時鼠標的客戶區座標

} NM_TREEVIEW;

TV_KEYDOWN結構.提供與鍵盤事件有關的信息.該結構的定義爲

pedef struct _TV_KEYDOWN { tvkd

NMHDR hdr; //標準的NMHDR結構

WORD wVKey; //虛擬鍵盤碼

UINT flags; //爲0

} TV_KEYDOWN;

TV_DISPINFO結構.提供與表項的顯示有關的信息.該結構的定義爲

typedef struct _TV_DISPINFO { tvdi

NMHDR hdr;

TV_ITEM item;

} TV_DISPINFO;

MFC的CTreeCtrl類封裝了樹形視圖.該類的Create成員函數負責控件的創建,該函數的聲明爲

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

其中參數dwStyle是如表6.26所示的控件風格的組合.

 

表6.26 樹形視圖的風格

控件風格

含義

TVS_HASLINES

在父項與子項間連線以清楚地顯示結構.

TVS_LINESATROOT

只在根部畫線.

TVS_HASBUTTONS

顯示帶有"+"或"-"的小方框來表示某項能否被展開或已展開.

TVS_EDITLABELS

用戶可以編輯表項的標題.

TVS_SHOWSELALWAYS

即使控件失去輸入焦點,仍顯示出項的選擇狀態.

TVS_DISABLEDRAGDROP

不支持拖動操作.

 

 

除上表的風格外,一般還要指定WS_CHILD和WS_VISIBLE窗口風格.對於用對話框模板創建的樹形視圖控件,可以在控件的屬性對話框中指定上表中列出的控件風格。例如,在屬性對話框中選擇Has buttons,相當於指定了TVS_HASBUTTONS風格.

CTreeCtrl類提供了大量的成員函數.對於常用的函數,這裏結合實際應用的需要,介紹如下:

向樹形視圖中插入新的表項.首先應提供一個TV_INSERTSTRUCT結構並在該結構中對插入項進行描述.如果要在樹形視圖中顯示圖象,則應該先創建一個CImageList對象並使該對象包含一個位圖序列,然後調用SetImageList爲樹形視圖設置位圖序列.然後調用InsertItem函數把新項插入到樹形視圖中.函數的聲明爲

CImageList* SetImageList( CImageList * pImageList, int nImageListType );
參數pImageList指向一個CImageList對象,參數nImageListType一般應爲TVSIL_NORMAL.

HTREEITEM InsertItem( LPTV_INSERTSTRUCT lpInsertStruct );
參數lpInsertStruct指向一個TV_INSERTSTRUCT結構.函數返回新插入項的句柄.

用DeleteItem來刪除指定項,用DeleteAllItems刪除所有項.函數的聲明爲
BOOL DeleteItem( HTREEITEM hItem );
BOOL DeleteAllItems( );
操作成功則函數返回TRUE,否則返回FALSE.

樹形視圖控件會根據用戶的輸入自動展開或摺疊子項.但有時需要在程序中展開或摺疊指定項,則應該調用Expand,該函數的聲明爲
BOOL Expand( HTREEITEM hItem, UINT nCode );
參數hItem指定了要展開或摺疊的項.參數nCode是一個標誌,指定了函數應執行的操作,它可以是TVE_COLLAPSE(摺疊)、TVE_COLLAPSERESET(摺疊並移走所有的子項)、TVE_EXPAND(展開)或TVE_TOGGLE(在展開和摺疊狀態之間翻轉).

要查詢或設置選擇項,應調用GetSelectedItem或SelectItem.函數的聲明爲
HTREEITEM GetSelectedItem( );
BOOL SelectItem( HTREEITEM hItem );

要對指定的項查詢或設置,可調用GetItem和SetItem.用這兩個功能強大的函數,幾乎可以查詢和設置項的所有屬性,包括表項的正文、圖像及選擇狀態.函數的聲明爲
BOOL GetItem( TV_ITEM* pItem );
BOOL SetItem( TV_ITEM* pItem );
參數pItem是指向TV_ITEM結構的指針,函數是通過該結構來查詢或設置指定項的,在調用函數前應該使該結構的hItem成員有效以指定表項.CTreeCtrl還提供了一系列函數可完成GetItem和SetItem的部分功能,其中GetItemState、GetItemText、GetItemData、GetItemImage和ItemHasChildren函數用於查詢,SetItemState、SetItemText、SetItemData和SetItemImage函數用於設置.

在使用樹形視圖控件時,一個經常遇到的問題是對於一個已知表項,如何找到與該項有某種關係的項,例如,父項、子項、兄弟項、下一個或前一個可見的項.利用功能強大的GetNextItem函數,可以解決這個問題.該函數也可以用來搜索具有某種狀態的表項.GetNextItem在遍歷樹形視圖時是很有用的,它的聲明爲
HTREEITEM GetNextItem( HTREEITEM hItem, UINT nCode );
參數hItem指定了一個項.參數nCode是一個標誌,標明瞭與指定項的關係,nCode可以是如表6.27所示的各種標誌.如果找到相關的項,函數返回該項的句柄,否則函數返回NULL.

 

表6.27 關係標誌

標誌

含義

TVGN_CARET

返回當前的選擇項.

TVGN_CHILD

返回指定表項的子項.

TVGN_DROPHILITE

返回拖動操作的目標項.

TVGN_FIRSTVISIBLE

返回第一個可見項.

TVGN_NEXT

返回指定項的下一個兄弟項(Sibling Item).

TVGN_NEXTVISIBLE

返回指定項的後一個可見項.

TVGN_PARENT

返回指定項的父項.

TVGN_PREVIOUS

返回指定項的前一個兄弟項.

TVGN_PREVIOUSVISIBLE

返回指定項的前一個可見項.

TVGN_ROOT

返回位於最高層(根位置)的第一個表項.

 

 

CTreeCtrl類提供了一系列的成員函數來完成GetNextItem的某一項功能,包括GetRootItem、GetFirstVisibleItem、GetNextVisibleItem、GetPrevVisibleItem、GetChildItem、GetNextSiblingItem、GetPrevSiblingItem、GetParentItem、GetSelectedItem和GetDropHilightItem.

 

除了表6.22列出的控件消息外,樹形視圖控件還會發送自己特有的通知消息,其中常用的有下面這幾個:

TVN_SELCHANGING和TVN_SELCHANGED.在用戶改變了對錶項的選擇時,控件會發送這兩個消息.消息會附帶一個指向NM_TREEVIEW結構的指針,程序可從該結構中獲得必要的信息.兩個消息都會在該結構的itemOld成員中包含原來的選擇項的信息,在itemNew成員中包含新選擇項的信息,在action成員中表明是用戶的什麼行爲觸發了該通知消息(若是TVC_BYKEYBOARD則表明是鍵盤,若是TVC_BYMOUSE則表明是鼠標,若是TVC_UNKNOWN則表示未知).兩個消息的不同之處在於,如果TVN_SELCHANGING的消息處理函數返回TRUE,那麼就阻止選擇的改變,如果返回FALSE,則允許改變.

TVN_KEYDOWN.該消息表明了一個鍵盤事件.消息會附帶一個指向TV_KEYDOWN結構的指針,通過該結構程序可以獲得按鍵的信息.

TVN_BEGINLABELEDIT和TVN_ENDLABELEDIT.分別在用戶開始編輯和結束編輯項的標題時發送.消息會附帶一個指向TV_DISPINFO結構的指針,程序可從該結構中獲得必要的信息.在前者的消息處理函數中,可以調用GetEditControl成員函數返回一個指向用於編輯標題的編輯框的指針,如果處理函數返回FALSE,則允許編輯,如果返回TRUE,則禁止編輯.在後者的消息處理函數中,TV_DISPINFO結構中的item.pszText指向編輯後的新標題,如果pszText爲NULL,那麼說明用戶放棄了編輯,否則,程序應負責更新項的標題,這可以由SetItem或SetItemText函數來完成.

 

樹形視圖控件還可以支持拖放操作,限於篇幅,這裏就不作介紹了.6.2.6 列表視圖控件

  列表視圖(List View)用來成列地顯示數據.在Windows 95的資源管理器的右側窗口中就有一個典型的列表視圖,如圖6.5所示.列表視圖的表項通常包括圖標(Icon)和標題(Label)兩部分,它們分別提供了對數據的形象和抽象描述.列表視圖控件是對傳統的列表框的重大改進,它能夠以下列四種格式顯示數據.讀者可以在資源管理器中的視圖(View)菜單中切換列表視圖的顯示格式,來看看四種格式的不同之處.

  • 大圖標格式(Large Icons).可逐行顯示多列表項,圖標的大小可由應用程序指定,通常是32×32像素,在圖標的下面顯示標題.

  • 小圖標格式(Small Icons).可逐行顯示多列表項,圖標的大小可由應用程序指定,通常是16×16像素,在圖標的右面顯示標題.表項以行的方式組織.

  • 列表格式(List).與小圖標格式類似.不同之處在於表項是逐列多列顯示的.

  • 報告格式(Report或Details).每行僅顯示一個表項,在標題的左邊顯示一個圖標,表項可以不顯示圖標而只顯示標題.表項的右邊可以附加若干列子項(Subitem),子項只顯示正文.在控件的頂端還可以顯示一個列表頭用來說明各列的類型.列表視圖的報告格式很適合顯示報表(如數據庫報表).

 

在討論如何使用列表視圖控件以前,顯向讀者介紹一下與該控件有關的一些數據類型:

LV_COLUMN結構.該結構僅用於報告式列表視圖,用來描述表項的某一列.要想向表項中插入新的一列,需要用到該結構.LV_COLUMN結構的定義爲

typedef struct _LV_COLUMN {

UINT mask; //屏蔽位的組合(見下面括號),表明哪些成員是有效的.

int fmt; /*該列的表頭和子項的標題顯示格式(LVCF_FMT).可以是LVCFMT_CENTER、LVCFMT_LEFT或LVCFMT_RIGHT*/

int cx; //以像素爲單位的列的寬度(LVCF_FMT)

LPTSTR pszText; //指向存放列表頭標題正文的緩衝區(LVCF_TEXT)

int cchTextMax; //標題正文緩衝區的長度(LVCF_TEXT)

int iSubItem; //說明該列的索引(LVCF_SUBITEM)

} LV_COLUMN;

LV_ITEM結構.該結構用來描述一個表項或子項,它包含了項的各種屬性,其定義爲

typedef struct _LV_ITEM {

UINT mask; //屏蔽位的組合(見下面括號),表明哪些成員是有效的

int iItem; //從0開始編號的表項索引(行索引)

int iSubItem; /*從1開始編號的子項索引(列索引),若值爲0則說明該成員無效,結構描述的是一個表項而不是子項*/

UINT state; //項的狀態(LVIF_STATE)

UINT stateMask; //項的狀態屏蔽

LPTSTR pszText; //指向存放項的正文的緩衝區(LVIF_TEXT)

int cchTextMax; //正文緩衝區的長度(LVIF_TEXT)

int iImage; //圖標的索引(LVIF_IMAGE)

LPARAM lParam; // 32位的附加數據(LVIF_PARAM)

} LV_ITEM;

其中lParam成員可用來存儲與項相關的數據,這在有些情況下是很有用的.state和stateMask的值如表6.28所示,stateMask用來說明要獲取或設置哪些狀態.

 

表6.28 列表視圖的狀態

狀態

對應的狀態屏蔽

含義

LVIS_CUT

同左

項被選擇用來進行剪切和粘貼操作.

LVIS_DROPHILITED

同左

項成爲拖動操作的目標.

LVIS_FOCUSED

同左

項具有輸入焦點.

LVIS_SELECTED

同左

項被選中.

NM_LISTVIEW結構.該結構用於存儲列表視圖的通知消息的有關信息,大部分列表視圖的通知消息都會附帶指向該結構的指針.NM_LISTVIEW的定義爲

typedef struct tagNM_LISTVIEW {

NMHDR hdr; //標準的NMHDR結構

int iItem; //表項的索引,若爲-1則無效

int iSubItem; //子項的索引,若爲0則無效

UINT uNewState; //項的新狀態

UINT uOldState; //項原來的狀態

UINT uChanged; /*取值與LV_ITEM的mask成員相同,用來表明哪些狀態發生了變化*/

POINT ptAction; //事件發生時鼠標的客戶區座標

LPARAM lParam; //32位的附加數據

} NM_LISTVIEW;

LV_DISPINFO結構.該結構包含了與項的顯示有關的信息,其定義爲

typedef struct tagLV_DISPINFO {

NMHDR hdr;

LV_ITEM item;

} LV_DISPINFO;

LV_KEYDOWN結構.該結構包含一些與鍵盤有關的信息,其定義爲

typedef struct tagLV_KEYDOWN {

NMHDR hdr;

WORD wVKey; //虛擬鍵盤碼

UINT flags; //總爲0

} LV_KEYDOWN;

MFC的CListCtrl類封裝了列表視圖控件.該類的Create函數負責創建控件,函數的聲明爲

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

 

其中參數dwStyle是如表6.29所示的控件風格的組合.

表6.29 列表視圖的風格

控件風格

含義

LVS_ALIGNLEFT

當顯示格式是大圖標或小圖標時,標題放在圖標的左邊.缺省情況下標題放在圖標的下面.

LVS_ALIGNTOP

當顯示格式是大圖標或小圖標時,標題放在圖標的上邊.

LVS_AUTOARRANGE

當顯示格式是大圖標或小圖標時,自動排列控件中的表項.

LVS_EDITLABELS

用戶可以修改標題.

LVS_ICON

指定大圖標顯示格式.

LVS_LIST

指定列表顯示格式.

LVS_NOCOLUMNHEADER

在報告格式中不顯示列的表頭.

LVS_NOLABELWRAP

當顯示格式是大圖標時,使標題單行顯示.缺省時是多行顯示.

LVS_NOSCROLL

列表視圖無滾動條.

LVS_NOSORTHEADER

報告列表視圖的表頭不能作爲排序按鈕使用.

LVS_OWNERDRAWFIXED

由控件的擁有者負責繪製表項.

LVS_REPORT

指定報告顯示格式.

LVS_SHAREIMAGELISTS

使列表視圖共享圖像序列.

LVS_SHOWSELALWAYS

即使控件失去輸入焦點,仍顯示出項的選擇狀態.

LVS_SINGLESEL

指定一個單選擇列表視圖.缺省時可以多項選擇.

LVS_SMALLICON

指定小圖標顯示格式.

LVS_SORTASCENDING

按升序排列表項.

LVS_SORTDESCENDING

按降序排列表項.

 


除上表的風格外,一般還要指定WS_CHILD和WS_VISIBLE窗口風格.風格組合WS_CHILD| WS_VISIBLE|LVS_REPORT|LVS_AUTOARRANGE|LVS_EDITLABLES|LVS_SINGLESEL將指定一個自動排列的、可編輯標題的、單選擇報告式列表視圖控件.要指定大圖標、小圖標或列表式的列表視圖控件,則應該把LVS_REPORT換成LVS_ICON、LVS_SMALLICON或LVS_LIST.

對於用對話框模板創建的列表視圖控件,可以在控件的屬性對話框中指定上表中列出的控件風格。例如,在屬性對話框的Styles頁的View欄中選擇Icon,相當於指定了LVS_ICON風格.

CListCtrl類提供了大量的成員函數.在這裏,我們結合實際應用來介紹一些常用的函數:

列的插入和刪除.在以報告格式顯示列表視圖時,一般會顯示一列表項和多列子項.在初始化列表視圖時,先要調用InsertColumn插入各個列,該函數的聲明爲
int InsertColumn( int nCol, const LV_COLUMN* pColumn );
其中參數nCol是新列的索引,參數pColumn指向一個LV_COLUMN結構,函數根據該結構來創建新的列.若插入成功,函數返回新列的索引,否則返回-1.
要刪除某列,應調用DeleteColumn函數,其聲明爲
BOOL DeleteColumn( int nCol );

表項的插入.要插入新的表項,應調用InsertItem.如果要顯示圖標,則應該先創建一個CImageList對象並使該對象包含用作顯示圖標的位圖序列.然後調用SetImageList來爲列表視圖設置位圖序列.函數的聲明爲

int InsertItem( const LV_ITEM* pItem );
參數pItem指向一個LV_ITEM結構,該結構提供了對錶項的描述.若插入成功則函數返回新表項的索引,否則返回-1.

CImageList* SetImageList( CImageList* pImageList, int nImageList );
參數pImageList指向一個CImageList對象,參數nImageList用來指定圖標的類型,若其值爲LVSIL_NORMAL,則位圖序列用作顯示大圖標,若值爲LVSIL_SMALL,則位圖序列用作顯示小圖標.可用該函數同時指定一套大圖標和一套小圖標.

要刪除某表項,應調用DeleteItem,要刪除所有的項,應調用DeleteAllItems.一旦表項被刪除,其子項也被刪除.函數的聲明爲
BOOL DeleteItem( int nItem );
BOOL DeleteAllItems( );

調用GetItemText和SetItemText來查詢和設置表項及子項顯示的正文.SetItemText的一個重要用途是對子項進行初始化.函數的聲明爲
int GetItemText( int nItem, int nSubItem, LPTSTR lpszText, int nLen ) const;
CString GetItemText( int nItem, int nSubItem ) const;
BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );
其中參數nItem是表項的索引(行索引),nSubItem是子項的索引(列索引),若nSubItem爲0則說明函數是針對表項的.參數lpszText指向正文緩衝區,參數nLen說明了緩衝區的大小.第二個版本的GetItemText返回一個含有項的正文的Cstring對象.

調用GetItem和SetItem來查詢和設置.用這兩個功能強大的函數,幾乎可以查詢和設置指定項的所有屬性,包括正文、圖標及選擇狀態.函數的聲明爲
BOOL GetItem( LV_ITEM* pItem ) const;
BOOL SetItem( const LV_ITEM* pItem );
參數pItem是指向LV_ITEM結構的指針,函數是通過該結構來查詢或設置指定項的,在調用函數前應該使該結構的iItem或iSubItem成員有效以指定表項或子項.CListCtrl還提供了一系列函數可完成GetItem和SetItem的部分功能,其中GetItemState、GetItemText和GetItemData函數用於查詢,SetItemState、SetItemText和SetItemData函數用於設置.

要查詢表項的數目,應該調用GetItemCount,其聲明爲 int GetItemCount( );

要尋找與指定表項項相關的表項,或尋找具有某種狀態的表項,應該調用GetNextItem.該函數的一個重要用處是搜索被選擇的表項.函數的聲明爲
int GetNextItem( int nItem, int nFlags ) const;
參數nItem是指定項的索引,參數nFlags是如表6.30所示的標誌,用來指定查詢的關係.函數返回搜索到的表項的索引,若未找到則返回-1.

表6.30 關係標誌

標誌

含義

LVNI_ABOVE

返回位於指定表項上方的表項.

LVNI_ALL

缺省標誌,返回指定表項的下一個表項(以索引爲序).

LVNI_BELOW

返回位於指定表項下方的表項.

LVNI_TOLEFT

返回位於指定表項左邊的表項.

LVNI_TORIGHT

返回位於指定表項右邊的表項.

LVNI_DROPHILITED

返回拖動操作的目標表項.

LVNI_FOCUSED

返回具有輸入焦點的表項.

LVNI_SELECTED

返回被選擇的表項.

 

 

要對錶項進行排列、排序和搜索,可分別調用Arrange、SortItems和FindItems函數來完成.

有時需要在列表視圖創建後動態地改變其顯示格式,例如,資源管理器中的列表視圖就可以在四中顯示格式之間切換.改變顯示格式其實就是改變列表視圖的風格,要改變控件的風格,應先調用::GetWindowLong獲取控件原來的風格,並對其進行修改,然後調用::SetWindowLong設置新的風格.這兩個函數不是成員函數,而是Windows API函數,用來查詢和設置窗口的風格.

 

除了表6.22列出的控件消息外,列表視圖控件還會發送自己特有的通知消息,其中常用的有下面這幾個:

LVN_ITEMCHANGING和LVN_ITEMCHANGED.當列表視圖的狀態發生變化時,會發送這兩個通知消息.例如,當用戶選擇了新的表項時,程序就會收到這兩個消息.消息會附帶一個指向NM_LISTVIEW結構的指針,消息處理函數可從該結構中獲得狀態信息.兩個消息的不同之處在於,前者的消息處理函數如果返回TRUE,那麼就阻止選擇的改變,如果返回FALSE,則允許改變.

LVN_KEYDOWN.該消息表明了一個鍵盤事件.消息會附帶一個指向LV_KEYDOWN結構的指針,通過該結構程序可以獲得按鍵的信息.

LVN_BEGINLABELEDIT和LVN_ENDLABELEDIT.分別在用戶開始編輯和結束編輯標題時發送.消息會附帶一個指向LV_DISPINFO結構的指針.在前者的消息處理函數中,可以調用GetEditControl成員函數返回一個指向用於編輯標題的編輯框的指針,如果處理函數返回FALSE,則允許編輯,如果返回TRUE,則禁止編輯.在後者的消息處理函數中,LV_DISPINFO結構中的item.pszText指向編輯後的新標題,如果pszText爲NULL,那麼說明用戶放棄了編輯,否則,程序應負責更新表項的標題,這可以由SetItem或SetItemText函數來完成.

 

列表視圖控件還可以支持拖放操作,這裏就不詳細介紹了.6.2.7 測試新型Win32控件的一個例子

本小節將向讀者提供一個測試Win32控件的例子.測試程序名爲Ctrl32,其界面如圖6.6所示,該程序對前面介紹的五種Win32控件均進行了測試:

  • 對樹形視圖的測試着重演示瞭如何在各個層次上加入表項以及如何使表項顯示圖象,表項在平常狀態下和選中狀態下將顯示不同的圖象.

  • 對列表視圖的測試包括如何生成一個報告式列表視圖,如何在四個顯示格式間切換以及如何使表項顯示圖標.讀者可以在列表視圖下面的下拉列表式組合框選擇不同的顯示格式.

  • 軌道條的測試包括初始化及WM_HSCROLL消息的處理.進度條的進度將會隨着滑尺位置的改變而改變.

  • 演示瞭如何爲旋轉按鈕指定夥伴控件以及旋轉按鈕的初始化.

T6_6.tif (129420 bytes)

圖6.6 Ctrl32測試程序

 

請讀者按下列步驟操作:

用AppWizard建立一個基於對話框的MFC應用程序,程序名爲Ctrl32.

插入兩個位圖(Bitmap)資源,其ID分別是IDB_BITMAP1和IDB_BITMAP2,樹形視圖和列表視圖將這用兩幅位圖來爲表項顯示圖象.兩個位圖的尺寸分別爲64×16和128×32.每個位圖都包含4個子圖,每個子圖中都有一個顏色不同的矩形或圓,請按圖6.7和表6.31繪製.兩個位圖的子圖都是一樣的,只不過大小不同,一個是16×16,一個是32×32.

T6_7.tif (129504 bytes)

圖6.7 用於樹形視圖和列表視圖的位圖表6.31

子圖的形狀

前景色

背景色

矩形

藍色

黃色

矩形

紅色

淡藍色

藍色

黃色

紅色

淡藍色

打開IDD_CTRL32_DIALOG對話框模板資源,清除OK按鈕外的所有控件並把OK按鈕的標題改爲C&lose.將對話框的大小調整爲300×150,然後按圖6.6和表6.32加入控件,並按表6.32用ClassWizard爲CCtrl32Dlg類加入成員變量,注意,這些成員變量都是控件對象.

 

表6.32

控件類型

控件ID

需設置的屬性

對應的控件對象名

樹形視圖

缺省

選擇Has buttons、Has lines、Lines at root和Edit labels.

m_Tree

列表視圖

缺省

選擇Report格式、Auto arrange、Edit lables

m_List

軌道條

缺省

選擇Tick marks和Autoticks.

m_Slider

進度條

缺省

缺省

m_Progress

組合框

缺省

選擇Drop List類型,不選擇Sort.加入4個表項:Icon、Small icon、List和Report.

m_ListBox

編輯框

缺省

缺省.要注意其tab順序比旋轉按鈕小1.

 

旋轉按鈕

缺省

選擇Auto buddy和Set buddy integer

m_Spin

 

 

在CCtrl32Dlg類的定義處爲改類加入下面兩個成員,這兩個CImageList對象用來向樹形視圖和列表視圖提供位圖序列.
CImageList m_SmallImageList;
CImageList m_LargeImageList;
用ClassWizard爲CCtrl32Dlg類加入如表6.33所示的消息處理函數.其中OnHScroll函數用來處理軌道條的通知消息,OnSelchangeCombo用來切換列表視圖的顯示格式.

 

表6.33 CCtrl32Dlg類的控件通知消息處理函數

Object IDS

Messages

Member functions

CCtrl32Dlg

WM_HSCROLL

OnHScroll

IDC_COMBO1

CBN_SELCHANGE

OnSelchangeCombo

 

 

最後,請讀者按清單6.2修改程序.

 

清單6.2 CCtrl32Dlg類的部分源代碼

// Ctrl32Dlg.cpp : implementation file

 

. . . . . .

 

char *szLabel[2]={"Rectangle","Circle"};

char *szColumn[3]={"Shape","Fore color","Back color"};

char *szData[4][3]=

{{"Rectangle","Blue","Yellow"},

{"Rectangle","Red","Blue"},

{"Circle","Blue","Yellow"},

{"Circle","Red","Blue"}};

DWORD nStyle[4]={LVS_ICON,LVS_SMALLICON,LVS_LIST,LVS_REPORT};

 

BOOL CCtrl32Dlg::OnInitDialog()

{

CDialog::OnInitDialog();

. . . . . .

// TODO: Add extra initialization here

 

//初始化旋轉按鈕

m_Spin.SetRange(0,200);

m_Spin.SetPos(0);

//初始化軌道條

m_Slider.SetRange(0,20);

m_Slider.SetTicFreq(2);

m_Slider.SetLineSize(2);

m_Slider.SetPageSize(4);

m_Slider.SetPos(0);

//初始化進度條

m_Progress.SetRange(0,20);

m_Progress.SetPos(0);

//創建位圖序列,用於樹形視圖和列表視圖顯示圖像

m_SmallImageList.Create(IDB_BITMAP1,16,0,FALSE); //16*16的位圖序列

m_LargeImageList.Create(IDB_BITMAP2,32,0,FALSE); //32*32的位圖序列

//初始化樹形視圖

TV_INSERTSTRUCT tvInsert;

HTREEITEM hItem;

int i,j;

char buffer[20];

 

m_Tree.SetImageList(&m_SmallImageList,TVSIL_NORMAL);

 

tvInsert.item.mask=TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;

for(i=0;i<2;i++)

{

tvInsert.hParent=NULL; //指定該項位於最高層

tvInsert.hInsertAfter=TVI_LAST;

tvInsert.item.pszText=szLabel[i];

tvInsert.item.iImage=i*2; //指定表項顯示的圖象

tvInsert.item.iSelectedImage=i*2+1; //指定選擇狀態下應顯示的圖象

hItem=m_Tree.InsertItem(&tvInsert);

for(j=0;j<3;j++)

{

tvInsert.hParent=hItem; //指定該項爲子項

tvInsert.hInsertAfter=TVI_SORT;

sprintf(buffer,"%s%d",szLabel[i],j);

tvInsert.item.pszText=buffer;

m_Tree.InsertItem(&tvInsert);

}

}

//初始化列表視圖

LV_COLUMN lvc;

LV_ITEM lvi;

 

m_List.SetImageList(&m_SmallImageList,LVSIL_SMALL);

m_List.SetImageList(&m_LargeImageList,LVSIL_NORMAL);

m_ComboBox.SelectString(-1,"Report");

 

lvc.mask=LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;

lvc.fmt=LVCFMT_LEFT;

for(i=0;i<3;i++) //插入各列

{

lvc.pszText=szColumn[i];

if(i==0)

lvc.cx=m_List.GetStringWidth(szColumn[0])+50;

else

lvc.cx=m_List.GetStringWidth(szColumn[i])+15;

lvc.iSubItem=i;

m_List.InsertColumn(i,&lvc);

}

lvi.mask=LVIF_TEXT|LVIF_IMAGE;

lvi.iSubItem=0;

for(i=0;i<4;i++) //插入表項

{

lvi.pszText=szData[i][0];

lvi.iItem=i;

lvi.iImage=i;

m_List.InsertItem(&lvi);

for(j=1;j<3;j++)

m_List.SetItemText(i,j,szData[i][j]);

}

 

return TRUE; // return TRUE unless you set the focus to a control

}

 

void CCtrl32Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{

// TODO: Add your message handler code here and/or call default

 

CSliderCtrl* pSlider=(CSliderCtrl*)pScrollBar;

//判斷是否是m_Slider軌道條發送的消息

if(&m_Slider!=pSlider) return;

m_Progress.SetPos(m_Slider.GetPos());

CDialog::OnHScroll(nSBCode, nPos, pScrollBar);

}

 

void CCtrl32Dlg::OnSelchangeCombo()

{

// TODO: Add your control notification handler code here

 

long lStyle;

lStyle=GetWindowLong(m_List.GetSafeHwnd(),GWL_STYLE);

//清除所有與顯示格式有關的風格標誌

lStyle&=~(LVS_ICON|LVS_SMALLICON|LVS_LIST|LVS_REPORT);

lStyle|=nStyle[m_ComboBox.GetCurSel()];

//設置新的風格

SetWindowLong(m_List.GetSafeHwnd(),GWL_STYLE,lStyle);

m_List.Invalidate(); //刷新

}

  對控件的初始化工作在OnInitDialog中完成.函數中使用的各種結構和函數在前面均介紹過,並不難懂.唯一需要說明的是CImageList對象的使用.CImageList對象用來存儲多個大小相同的圖象,如果程序中要用到大量的尺寸相同的位圖或圖標,可以用CImageList對象把它們組織成圖象序列來使用,通過指定序列的索引,可以獲得序列中的圖象.樹形視圖和列表視圖均使用CImageList對象來設置圖象序列.在OnInitDialog函數中,調用了CImageList::Create來創建一個圖象序列.該函數的聲明爲

BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask );

  參數nBitmapID是位圖資源的ID,在該位圖中包含了一些尺寸相同的子圖,參數cx說明了序列中每幅圖象的寬度(以像素爲單位),參數nGrow說明了當對象包含的圖象序列增大時應預留的空位個數,參數crMask如果爲TRUE,則說明圖象序列中包含屏蔽圖象.

  在函數中調用了CTreeCtrl::SetImageList和CListCtrl::SetImageList來爲樹形視圖和列表視圖設置圖象序列.注意列表視圖對象m_List設置了兩個圖象序列,分別用於小圖標和大圖標顯示格式.在插入表項時,只要指定了圖象序列的索引,表項就可以顯示相應的圖象.

  OnHScroll函數負責處理軌道條發出的通知消息,函數根據軌道條的當前位置設置進度條的進度.OnSelchangeCombo函數響應用戶對下拉列表式組合框的選擇,函數先調用CWnd::GetWindowLong獲取列表視圖原來的風格,然後調用CWnd::SetWindowLong設置新的風格(注意調用CWnd::GetSafeHwnd可以獲得窗口的句柄),最後調用CWnd::Invalidate刷新列表視圖.

  Ctrl32程序只是對常用的Win 32控件進行了一些基本的測試。Visual C++提供了一個全面測試Win 32控件的MFC例子cmnctrls(在samples / mfc / general / cmnctrls目錄下),有興趣的讀者可以研究一下。

 

6.3 技術總結

  在上一章和本章中,讀者已經接觸和使用了各種控件。這些控件雖然五花八門,但它們卻具有一些共同的特點。本節的目的就是討論這些共同點,以使讀者能在概念上更好地理解控件。

6.3.1 所有的控件都是窗口

  確切地說,所有的控件都是子窗口。控件窗口都具有WS_CHILD風格,它們總是依附於某一個父窗口。所有MFC的控件類都是基本窗口類CWnd的直接或間接派生類,這意味着可以調用CWnd類的某些成員函數來查詢和設置控件。常用於控件的CWnd成員函數在表6.34列出,這些函數對所有的控件均適用。

表6.34 常用於控件的CWnd成員函數

函數名

用途

ShowWindow

調用ShowWindow(SW_SHOW)顯示窗口,調用ShowWindow(SW_HIDE)則隱藏窗口.

EnableWindow

調用EnableWindow(TRUE)允許窗口,調用EnableWindow(FALSE)則禁止窗口.一個禁止的窗口呈灰色顯示且不能接受用戶輸入.

DestroyWindow

刪除窗口.

MoveWindow

改變窗口的位置和尺寸.

SetFocus

使窗口具有輸入焦點.

  例如,如果想把一個編輯框控件隱藏起來,可以用下面這行代碼完成.

m_MyEdit.ShowWindow(SW_HIDE);

6.3.2 控件的創建方法

  控件的創建有自動和手工兩種常用方法.

  控件的自動創建是通過向對話框模板中添加控件實現的.到目前爲止,讀者所使用的控件都是用這種方法創建的.當調用對話框類的DoModal和Create顯示對話框時,框架會根據對話框模板資源提供的控件信息自動地創建控件.這種方法的優點是方便直觀,用戶可以在對話框模板編輯器的控件面板中選擇控件,可以在對話框模板中調整控件的位置和大小,還可以通過屬性對話框設置控件的風格.

  手工創建控件是一種比較專業的方法,包括下面兩步:

構建一個控件對象.通常的做法是把控件對象嵌入到父窗口(如對話框)對象中,即以成員變量的形式定義一個控件對象.這樣,在構建父窗口對象時,控件對象會被自動構建.程序也可以用new操作符創建控件對象,但要注意MFC的控件對象不具有自動清除的功能,因此需要在關閉父窗口時用delete操作符刪除控件對象(參見5.4.2).

調用控件對象的Create成員函數創建控件.一般來說,如果要在對話框中創建控件,那麼應該在OnInitDialog函數中調用Create,如果要在非對話框窗口中創建控件,則應該在OnCreate函數中調用Create.

  清單6.3是一個手工創建控件的實例.

清單6.3 控件的手工創建

#define ID_EXTRA_EDIT 100

 

class CMyDialog : public CDialog

{

protected:

CEdit m_edit; // Embedded edit object

public:

virtual BOOL OnInitDialog();

};

. . . . . .

BOOL CMyDialog::OnInitDialog()

{

CDialog::OnInitDialog();

CRect rect(85, 110, 180, 210);

 

m_edit.Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP |

ES_AUTOHSCROLL | WS_BORDER, rect, this, ID_EXTRA_EDIT);

m_edit.SetFocus();

return FALSE;

}

  不難看出,控件的手工創建是在程序中通過控件對象完成的,與對話框模板無關.在Create函數中,需要提供控件的風格,控件的尺寸和位置,控件的ID等信息.手工創建實際上是一種動態創建過程,程序可以在任何時侯根據需要來創建,不一定非要在OnInitDialog中進行.

  控件並不是對話框所專有的,某些非對話框窗口也可以擁有控件.如果要在象工具條或狀態欄這樣的非對話框窗口中創建控件,就必需用手工創建方法.自動創建由於要依賴對話框模板,因此只適用於對話框.

6.3.3 訪問控件的方法

  控件是一種交互的工具,應用程序需要通過某種方法來訪問控件以對其進行查詢和設置.訪問控件有四種方法:

  1. 利用對話框的數據交換功能訪問控件.這種方法適用於自動創建的控件.先用ClassWizard爲對話框類加入與控件對應的數據成員變量,然後在適當的時侯調用UpdateData,就可以實現對話框和控件的數據交換.這種方法只能交換數據,不能對控件進行全面的查詢和設置,而且該方法不是針對某個控件,而是針對所有參與數據交換的控件.另外,對於新型的Win32控件,不能用ClassWizard創建數據成員變量.因此,該方法有較大的侷限性.

  2. 通過控件對象來訪問控件.控件對象對控件進行了封裝,它擁有功能齊全的成員函數,用來查詢和設置控件的各種屬性.通過控件對象來訪問控件無疑是最能發揮控件功能的一種方法,但這要求程序必需創建控件對象並使該對象與某一控件相連.對於自動創建的控件,可利用ClassWizard方便地創建與控件對應的控件對象.對於手工創建的控件,因爲控件本身就是通過控件對象創建的,所以不存在這一問題.

  3. 利用CWnd類的一些用於管理控件的成員函數來訪問控件.這些函數已在表5.5列出.只要向這些函數提供控件的ID,就可以對該控件進行訪問.使用這些函數的好處是無需創建控件對象,就可以對控件的某些常用屬性進行查詢和設置.該方法對自動和手工創建的控件均適用.

  4. 用CWnd::GetDlgItem訪問控件.該函數根據參數說明的控件ID,返回指定控件的一個CWnd型指針,程序可以把該指針強制轉換成相應的控件類指針,然後通過該指針來訪問控件.該方法對自動和手工創建的控件均適用.在上一章中就曾經使用過這種方法,讀者可參見5.3.6.其實該方法與通過控件對象來訪問控件的方法在本質上是一樣的,在表5.5中亦包括GetDlgItem函數,但爲了強調其重要性,這裏把它單獨列爲一種方法.

 

6.3.4 控件及控件對象的刪除

  當關閉父窗口時,控件會被自動刪除,因此在一般情況下不必操心刪除問題.如果由於某種需要想手工刪除控件,可以調用CWnd::DestroyWindow來完成.

  對於控件對象的刪除,有兩種情況.若控件對象是以成員變量的形式創建的,那麼該對象將會隨着父窗口對象的刪除而被刪除,因此在程序中無需操心.若控件對象是用new操作符在堆中創建的,則必需在關閉父窗口時用delete操作符刪除對象,這是因爲所有MFC的控件類都是非自動清除的(參見5.4.2).

6.3.5 控件通知消息

  傳統控件和Win32 控件採用了不同的通知消息機制,請參見6.1.1和6.2.1。

 

6.4 在非對話框窗口中使用控件

  控件並不是對話框獨有的,事實上,很多非對話框窗口都可以使用控件.比較典型的應用是在表單視圖、工具條和狀態欄中使用控件.

6.4.1 在表單視圖中使用控件

  MFC提供了一個名爲CFormView的特殊視圖類,我們稱其爲表單視圖.表單視圖是指用控件來輸入和輸出數據的視圖,用戶可以方便地在表單視圖中使用控件.表單視圖具有對話框和滾動視圖的特性,它使程序看起來象是一個具有滾動條的對話框.在有些情況下,用表單視圖比用普通視圖更符合用戶的需要,例如,在向數據庫輸入數據時,顯然用表單的形式可以更習慣些.

  用AppWizard可以方便地創建基於表單視圖的應用程序,只要在MFC AppWizard對話框的第六步先選擇CView,然後在Base class欄中選擇CFormView,AppWizard就會創建一個基於CFormView的應用程序.

  讀者可以按上述方法建立一個名爲Test的應用程序.在Test工程的資源中,讀者會發現一個ID爲IDD_TEST_DIALOG的對話框模板,該對話框模板可供用戶放置和安排控件.在程序運行時,框架根據該對話框模板創建CFormView對象,並根據模板的信息在表單視圖中自動創建控件.與設計對話框類相類似,用戶可以用ClassWizard爲表單視圖類加入與控件對應的成員變量,可以調用UpdateData在控件和成員變量之間交換數據,但對控件的初始化工作是在OnInitialUpdate函數而不是在OnInitDialog函數中進行的.

  基於表單視圖的應用程序與基於對話框的應用程序都是在應用程序中直接使用控件,但二者有很多不同之處.基於對話框的應用程序是用一個對話框來作爲程序的主窗口的,因而程序的主窗口的特性與對話框類似,如窗口的大小不能改變,程序沒有菜單條、工具條和狀態欄等.基於表單視圖的應用程序仍然是基於Doc/View框架結構的(見七、八、九章),只是視圖被換成了表單視圖,也就是說,應用程序的窗口可以改變大小,程序有菜單條、工具條和狀態欄,且程序仍然可以Dov/View運行機制來處理文檔.

  表單視圖比較簡單,這裏就不舉例說明了.在第十章,讀者會看到使用表單視圖的例子.

6.4.2 在工具條和狀態欄中使用控件

  一個專業的程序常常會在工具條和狀態欄中加入一些控件以方便用戶的使用.例如,在Developer Studio的工具條中就有不少組合框,而在狀態欄中則常常會顯示一個進度條來表明工作的進度.

  如果讀者想在自己程序的工具條和狀態欄中加入控件,則需要掌握一些技巧.在本小節,我們將結合一個具體實例來演示這些技巧.例程的名爲CtrlInBar,其界面如圖6.8所示.可以看出,該程序在工具條中創建了一個組合框,在狀態欄中創建了一個進度條.

T6_8.tif (174388 bytes)

圖6.8 CtrlInBar程序

  現在讓我們開始工作.首先,請讀者用AppWizard建立一個名爲CtrlInBar的單文擋MFC應用程序,然後,請按清單6.4修改源代碼.注意在程序中編寫了一個CToolBar類的派生類CMyToolBar,以及一個CStatusBar的派生類CMyStatusBar,這兩個類與CMainFrame類在同一模塊中.

清單6.4 在工具條和狀態欄中創建控件的有關代碼

// MainFrm.h : interface of the CMainFrame class

//

/////////////////////////////////////////////////////////////////////////////

#define IDC_MYCOMBO 100

class CMyToolBar : public CToolBar

{

public:

CComboBox m_ComboBox;

BOOL CreateComboBox(int nIndex);

};

 

#define ID_INDICATOR_PROGRESS 100

class CMyStatusBar : public CStatusBar

{

public:

CProgressCtrl m_Progress;

int m_nProgressPane;

BOOL CreateProgressCtrl(int nPane);

 

afx_msg void OnSize(UINT nType, int cx, int cy);

DECLARE_MESSAGE_MAP()

};

 

class CMainFrame : public CFrameWnd

{

 

. . . . . .

protected: // control bar embedded members

CMyStatusBar m_wndStatusBar;

CMyToolBar m_wndToolBar;

 

. . . . . .

};

 

// MainFrm.cpp : implementation of the CMainFrame class

//

 

. . . . . .

static UINT indicators[] =

{

ID_SEPARATOR, // status line indicator

 

ID_SEPARATOR

};

 

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

return -1;

 

if (!m_wndToolBar.Create(this) ||

!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

{

TRACE0("Failed to create toolbar/n");

return -1; // fail to create

}

 

if (!m_wndStatusBar.Create(this) ||

!m_wndStatusBar.SetIndicators(indicators,

sizeof(indicators)/sizeof(UINT)))

{

TRACE0("Failed to create status bar/n");

return -1; // fail to create

}

 

// TODO: Remove this if you don't want tool tips or a resizeable toolbar

m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |

CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

 

 

m_wndToolBar.CreateComboBox(0); //在工具條的最左邊創建組合框

m_wndStatusBar.CreateProgressCtrl(1); //在第二個窗格創建進度條

 

m_wndToolBar.m_ComboBox.AddString("Item1");

m_wndToolBar.m_ComboBox.AddString("Item2");

m_wndToolBar.m_ComboBox.AddString("Item3");

m_wndToolBar.m_ComboBox.AddString("Item4");

m_wndStatusBar.m_Progress.SetRange(0,200);

m_wndStatusBar.m_Progress.SetPos(100);

 

 

. . . . . .

}

 

 

/////////////////////////////////////////////////////////////////////////////

//CMyToolBar

 

//參數nIndex是按鈕的索引,函數將在該按鈕的左側創建組合框

BOOL CMyToolBar::CreateComboBox(int nIndex)

{

if(m_ComboBox.GetSafeHwnd()) //防止重複創建

return FALSE;

 

CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();

TBBUTTON button;

CRect rect;

button.fsStyle=TBSTYLE_SEP;

ToolBarCtrl.InsertButton(nIndex,&button); //插入空位

ToolBarCtrl.InsertButton(nIndex,&button);

ToolBarCtrl.InsertButton(nIndex,&button);

//設置空位的寬度(處於中間的空位用來容納組合框)

SetButtonInfo(nIndex+1,IDC_MYCOMBO,TBBS_SEPARATOR,100);

SetButtonInfo(nIndex, ID_SEPARATOR, TBBS_SEPARATOR, 12);

SetButtonInfo(nIndex+2, ID_SEPARATOR, TBBS_SEPARATOR, 12);

GetItemRect(nIndex+1, &rect); //獲取中間空位的座標

rect.top = 3;

rect.bottom = rect.top + 100;

if (!m_ComboBox.Create(CBS_DROPDOWNLIST|WS_VISIBLE|WS_TABSTOP,

rect, this, IDC_MYCOMBO))

return FALSE;

m_ComboBox.SetItemHeight(-1,15); //設置編輯框組件的高度

return TRUE;

}

 

 

/////////////////////////////////////////////////////////////////////////////

//CMyStatusBar

 

 

BEGIN_MESSAGE_MAP(CMyStatusBar, CStatusBar)

ON_WM_SIZE()

END_MESSAGE_MAP()

 

//參數nPane是窗格的索引,函數將在該窗格內創建進度條控件

BOOL CMyStatusBar::CreateProgressCtrl(int nPane)

{

if(m_Progress.GetSafeHwnd()) //防止重複創建

return FALSE;

 

//設置該窗格的寬度爲200

SetPaneInfo(nPane,GetItemID(nPane),SBPS_NORMAL,200);

CRect rect(0,0,1,1);

if(!m_Progress.Create(WS_CHILD|WS_VISIBLE,rect,this,

ID_INDICATOR_PROGRESS))

return FALSE;

m_nProgressPane=nPane;

return TRUE;

}

 

void CMyStatusBar::OnSize(UINT nType, int cx, int cy)

{

CStatusBar::OnSize(nType, cx, cy);

if(m_Progress.GetSafeHwnd()==NULL) return;

 

CRect rect;

GetItemRect(m_nProgressPane,&rect);

m_Progress.MoveWindow(rect); //調整控件的位置和尺寸

}

  CMyToolBar類可以在工具條中指定按鈕的左邊放置一個下拉列表式組合框,並在組合框的兩端留出空位.該類的CreateComboBox成員負責創建組合框,參數nIndex是工具條按鈕的索引,需注意的是工具條的一個空位也要佔有一個索引.在CreateComboBox中,主要調用了下列函數:

調用CToolBar::GetToolBarCtrl返回一個CToolBarCtrl對象.從4.0版開始,CToolBar類是在新控件類CToolBarCtrl類的基礎上實現的,後者具有更強大的功能.例如CToolBarCtrl提供了CToolBar沒有的InsertButton成員函數.

調用CToolBarCtrl::InsertButton在nIndex索引處插入三個空位.

調用CToolBar::SetButtonInfo設置空位的寬度,其中中間的空位有100像素寬,用來容納組合框.

調用CToolBar::GetItemRect獲得中間空位的座標.

調用CComboBox::Create函數創建組合框.注意rect對象說明的是包括列表框在內的組合框的尺寸.

調用CComboBox::SetItemHeight設置編輯框組件的高度.

 

  CMyStatusBar類可以在指定的狀態欄窗格中放置一個進度條.該類的CreateProgressCtrl成員負責創建進度條,參數nPane是窗格的索引.在該函數中主要調用了下列函數:

調用CStatusBar::SetPaneInfo設置窗格的寬度爲200.在調用該函數時,先調用CStatusBar::GetItemID返回窗格的ID.

調用CProgressCtrl::Create創建控件.

  大家可能會奇怪,CProgressCtrl::Create創建的控件只有1×1大小.這是由於在調用該函數創建控件時,狀態欄的大小往往並未確定.這時如果調用CStatusBar::GetItemRect,只能得到0座標,而不能得到正確的窗格座標,所以程序只好先創建一個1×1的控件.

  工具條中按鈕和控件的尺寸及其相對於工具條的位置不會隨外界因素髮生變化,而狀態欄則不同,當用戶改變了框架窗口的寬度時,狀態欄的寬度也會隨之改變,並且它會重新調整各窗格的大小和位置,此時如果不及時調整進度條的座標,那麼進度條與所在窗格之間將發生錯位.調整進度條的大小和位置的工作由CMyStatusBar::OnSize函數完成.當窗口的尺寸發生改變後,窗口會收到WM_SIZE消息,OnSize是WM_SIZE消息的處理函數.在CMyStatusBar::OnSize函數中,先調用CStatusBar::GetItemRect獲得進度條所在窗格的座標,然後調用CWnd::MoveWindow來調整進度條控件的座標.

  在窗口形成時,也會收到WM_SIZE消息,這時OnSize函數可以及時調整進度條的大小和位置.

 

6.5 設計新的控件類

  雖然Windows提供了大量的控件,但不一定總能滿足用戶的需要.有時,用戶需要一些有特殊功能的控件.例如,有時希望編輯框控件只能接受數字輸入,當用戶輸入非數字字符時,編輯框控件會發出聲響來提醒用戶.在這種情況下,標準的CEdit類就無能爲力了.

  當控件無法滿足需要時,用戶可以從原來的控件類派生一個新類.通過合理地設計派生類,可以修改控件的行爲和屬性以達到用戶的要求.利用ClassWizard的強大功能,讀者可以方便地創建和設計控件類的派生類.

6.5.1 創建標準控件類的派生類

  這個任務可以用ClassWizard完成,其具體步驟如下:

按Ctrl+W進入ClassWizard.

單擊Add Class按鈕並選擇New...菜單項,則打開Create New Class對話框.

在Create New Class對話框的Name欄中輸入派生類的名字,在Base class欄中選擇一個標準的控件類做爲基類,然後按Create按鈕即可.

6.5.2 利用MFC的控件通知消息反射機制完善派生類的功能

  創建好派生類後,接下來的任務就是修改新類的代碼以完善其功能.例如,爲新類添加必要的成員變量,提供新的成員函數以及消息處理函數等等.其中爲控件添加消息處理函數是最重要的,因爲這直接關係着控件新功能的實現.

  與控件有關的消息包括控件本身接收的消息和發給父窗口的通知消息兩種.利用ClassWizard可以方便地爲派生類創建這兩類消息的處理函數.讀者也許會感到奇怪,控件通知消息不是由父窗口處理的嗎,難道控件本身也有機會處理通知消息?

  答案是肯定的.從4.0版開始,MFC提供了一種消息反射機制(Message Reflection),可以把控件通知消息反射回控件.具體地講,當父窗口收到控件通知消息時,如果父窗口有該消息的處理函數,那麼就由父窗口處理該消息,如果父窗口不處理該消息,則框架會把該消息反射給控件,這樣控件就有機會處理該消息了.由此可見,新的消息反射機制並不破壞原來的通知消息處理機制.

  消息反射機制爲控件提供了處理通知消息的機會,這在有些情況下是很有用的.例如,如果派生類想改變控件的背景色,就需要處理WM_CTLCOLOR通知消息.大多數控件在需要重繪時,會向父窗口發送WM_CTLCOLOR消息,父窗口在處理該消息時會返回一個刷子用來畫控件的背景.如果按傳統的方法,由父窗口來處理這個消息,則加重了控件對象對父窗口的依賴程度,每當使用這樣一個新控件時,都要在父窗口中提供控件的WM_CTLCOLOR消息處理函數,這顯然違背了面向對象的原則.若由控件自己處理WM_CTLCOLOR消息,則使得控件對象具有更大的獨立性,而父窗口也可以省去一些不必要的工作.

  讀者可以在自己的程序中用ClassWizard創建一個CEdit類的派生類試試.在派生類的消息列表中,在有些消息前面有一個"="符號,這表明這些消息是可以反射的通知消息.讀者可以按照通常的方法創建反射消息的處理函數.

6.5.3 利用SubclassDlgItem函數動態連接控件和控件對象

  要在程序中創建新設計的控件,顯然不能用自動創建的辦法,因爲對話框模板對新控件的特性一無所知.程序可以用手工方法創建控件,在調用派生類的Create函數時,派生類會調用基類的Create函數創建控件.用Create函數創建控件是一件比較麻煩的工作,程序需要爲函數指定一大堆的控件風格以及控件的座標和ID.特別是控件的座標,沒有經驗的程序員很難確切地安排控件的位置和大小,往往需要反覆調整.利用MFC的CWnd::SubclassDlgItem提供的動態連接功能,可以避免Create函數的許多麻煩,該函數大大簡化了在對話框中創建派生控件的過程.

  大家知道,在用手工方法創建控件時,先要構建一個控件對象,然後再用Create函數在屏幕上創建控件窗口,也就是說,控件的創建工作是由控件對象完成的.動態連接的思路則不同,SubclassDlgItem可以把對話框中已有的控件與某個窗口對象動態連接起來,該窗口對象將接管控件的消息處理,從而使控件具有新的特性.SubclassDlgItem函數的聲明爲

BOOL SubclassDlgItem( UINT nID, CWnd* pParent );

 

  參數nID是控件的ID,pParent是指向父窗口的指針.若連接成功則函數返回TRUE,否則返回FALSE.

  綜上所述,要在程序中使用派生控件,應該按下面兩步進行:

在對話框模板中放置好基類控件.

在對話框類中嵌入派生控件類的對象.

在OnInitDialog中調用SubclassDlgItem將派生類的控件對象與對話框中的基類控件相連接,則這個基類控件變成了派生控件.

 

  例如,如果要在對話框中使用新設計的編輯框控件,應先在對話框模板中的合適位置放置一個普通的編輯框,然後,在OnInitDialog函數中按下面的方式調用SubclassDlgItem即可:

BOOL CMyDialog::OnInitDialog()

{

CDialog::OnInitDialog();

m_MyEdit.SubclassDlgItem(IDC_MYEDIT, this);

return TRUE;

}

 

 

發佈了4 篇原創文章 · 獲贊 0 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章