使用Windows標準控件程序員
咱們在前面曾提到過,控件是一些行爲標準化了的窗口,通常用於對話框或其它窗口中充當與用戶交互的元素。在Visual C++中,可使用的控件分紅三類:編程
(1) Windows標準控件安全
Windows標準控件由Windows操做系統提供,在Windows 95中還提供了一些新增的控件。全部這些控件對象都是可編程的,咱們可使用Visual C++提供的對話框編輯器把它們添加到對話框中。Microsoft基礎類庫(MFC)提供了封裝這些控件的類,它們列於表6.1。數據結構
表6.1 Windows標準控件框架
控件編輯器 |
MFC類ide |
描述函數 |
動畫工具 |
CAnimateCtrloop |
顯示連續的AVI視頻剪輯 |
按鈕 |
CButton |
用來產生某種行爲的按鈕,以及複選框、單選鈕和組框 |
組合框 |
CComboBox |
編輯框和列表框的組合 |
編輯框 |
CEdit |
用於鍵入文本 |
標題頭 |
CHeaderCtrl |
位於某一行文本之上的按鈕,可用來控制顯示文件的寬度 |
熱鍵 |
CHotKeyCtrl |
用於經過按下某一組合鍵來很快的執行某些經常使用的操做 |
圖象列表 |
CImageList |
一系列圖象(典型狀況下是一系列圖標或位圖)的集合。圖象列表自己不是一種控件,它經常是和其它控件一塊兒工做,爲其它控件提供所用的圖象列表 |
列表 |
CListCtrl |
顯示文本及其圖標列表的窗口 |
列表框 |
CListBox |
包括一系列字符串的列表 |
進度 |
CProgressCtrl |
用於在一較長操做中提示用戶所完成的進度 |
多格式文本編輯 |
CRichEditCtrl |
提供可設置字符和段落格式的文本編輯的窗口 |
滾動條 |
CScrollBar |
爲對話框提供控件形式的滾動條 |
滑塊 |
CSliderCtrl |
包括一個有可選標記的滑塊的窗口 |
旋轉按鈕 |
CSpinButtonCtrl |
提供一對可用於增減某個值的箭頭 |
靜態文本 |
CStatic |
經常使用於爲其它控件提供標籤 |
狀態條 |
CStatusBarCtrl |
用於顯示狀態信息的窗口,同MFC類CStatusBar相似 |
續表6.1
控件 |
MFC類 |
描述 |
選項卡 |
CTabCtrl |
在選項卡對話框或屬性頁中提供具備相似筆記本中使用的分隔標籤的外觀的選項卡 |
工具條 |
CToolBarCtrl |
具備一系列命令生成按鈕的窗口,同MFC類CToolBar相似 |
工具提示 |
CToolTipCtrl |
一個小的彈出式窗口,用於提供對工具條按鈕或其它控件功能的簡單描述 |
樹 |
CTreeCtrl |
用於顯示一系列的項的繼承結構 |
前面提到過,在MFC中,類CWnd是全部窗口類的基類,很天然的,它也是全部控件類的基類。Windows標準控件在如下環境下提供:
(2) ActiveX控件
ActiveX控件可用於對話框中,也可用於HTML文檔中。這種控件過去被稱爲OLE控件。本書將在專門的章節中來說述關於ActiveX控件的知識。這裏僅指出ActiveX控件使用了與標準控件徹底不一樣的接口和實現方法。
(3) 其它MFC控件類
除了Windows標準控件和本身編寫的或者來自於第三方軟件開發商的ActiveX控件之外,MFC還提供了另外三種控件,它們由下面的三個類進行封裝:
在本章咱們僅講述第一類控件,即Windows標準控件。所涉及的內容包括各個控件的使用及相應的技巧。
第一節 使用對話框編輯器和ClassWizard
對於大多數Windows標準控件,咱們通常都使用對話框編輯器來將它們添加到對話框中。
圖6. 1 在ResourceView中選擇對話框
IDD_DIALOGDEMO_DIALOG
圖6. 2 控件的Properties對話框
圖6. 3 對話框編輯器的Controls工具窗口
在下面的過程當中,咱們將一個編輯框控件添加到在第四章建立的基於對話框的MFC框架應用程序的主對話框窗口中。
1. 首先,在Workspace窗口的ResourceView選項內雙擊DialogDemo resources\Dialog節點下的IDD_DIALOGDEMO_DIALOG圖標。上面的操做如圖所示。
2. 用鼠標選中標有「要作……」的靜態文本控件。右擊鼠標,從上下文菜單中選擇Properties,打開如圖6.2所示的對話框,在Caption文本框中輸入新的控件文本:「在下面的文本框中輸入一些字符」,而後將靜態文本控件拖動到對話框的左上角。
3. 從Controls工具窗口(如圖6.3所示,若是在你的資源編輯器中看不到該工具窗口,能夠在工具條上右擊鼠標,從上下文菜單中選擇Controls)中選擇編輯控件圖標 ,在對話框中繪製一個編輯框控件,如圖6.4所示。
在該編輯框控件的Properties窗口的General選項卡中輸入其ID爲IDC_EDIT。而後在Styles選項卡下將Multiline複選框劃上勾,並消除Auto HScroll複選框前的勾。
4. 右擊該編輯框控件,從上下文菜單中選擇ClassWizard命令,打開ClassWizard對話框,該對話框看起來如圖6.5所示。
圖6. 4 向對話框中添加一個編輯框控件
圖6. 5 ClassWizard對話框
單擊Member Variables選項卡,確信在Project處選擇了DialogDemo,在Class name處選擇了CDialogDemoDlg。如今咱們爲剛纔添加的編輯框控件IDC_EDIT添加一個數據映射入口。在Control IDs處選擇IDC_EDIT,單擊右邊的Add Viable按鈕。打開如圖6.6所示的對話框。
在Member variable name處連接變量名m_strEdit (這裏m表示該變量爲類CDialogDemoDlg的一個成員變量,str代表其類型爲字符串,即類CString),在Category下拉列表中選擇Value (另外一種選擇是Control,兩種選擇的不一樣將在後面的內容中講述),在Variable type下拉列表中選擇CString (還有其它不少數據類型可供選擇,但因爲這裏編輯框中的內容爲一字符串,所以CString是最恰當的選擇)。單擊OK關閉對話框。
圖6. 6 爲控件映射添加成員變量
5. 檢查一下如今的ClassWizard對話框(圖6.7)與圖6.5相比有何不一樣。在圖6.7所示的對話框中下方的Maximum characters文本框中輸入50。由字面意思能夠很容易猜出其含義,即將編輯框IDC_EDIT中可能的最長字符串的大小限制爲50。單擊OK關閉對話框。
圖6. 7 使用ClassWizard設置數據驗證方案
6. 從Workspace窗口的ClassView中雙擊類CDialogDemoDlg的OnInitDialog成員函數,使用下面的代碼來代替位於語句
return TRUE;
前的// TODO註釋:
m_strEdit="您好! 請在這裏輸入一些字符串。";
UpdateData(FALSE);
7. 在ClassView中雙擊類CDialogDemoApp的InitInstance成員函數,使用下面的代碼來找替位於選擇支
if (nResponse == IDOK)
下的//TODO註釋:
AfxMessageBox(dlg.m_strEdit);
而後將同一成員函數中的下面的代碼行刪掉(或註釋掉):
m_pMainWnd = &dlg;
8. 編譯並運行該應用程序。顯示如圖6.8所示的對話框。
圖6. 8 示例程序DialogDemo的運行結果
在圖6.8所示的文本框中輸入一些字符,單擊「肯定」。隨即彈出如圖6.9所示的消息框。該消息框複述了用戶在圖6.8所示的對話框中的輸入。咱們還發現,在圖6.8所示的對話框中,當輸入字符串達到必定的長度以後,咱們不能夠再輸入更多的字符,這是咱們在前面設置了Maximum characters爲50的結果。
圖6. 9 以消息框的形式反饋輸入的字符串
下面咱們來看在上面的步驟中都完成了什麼。首先咱們使用資源編輯器向對話框模板中添加這些標準控件,這一步的概念很清晰,所以並不難理解。
而後,咱們打開了所繪製的編輯框的Properties (屬性)對話框。先將其控件ID設置爲IDC_EDIT。這時若是打開頭文件Resource.h,就會發現宏IDC_EDIT被定義爲常量1001。不過,事實上在不少狀況下咱們並不須要關心每一控件的ID的具體值,而只須要記住相應的助記符。對於這裏的編輯框控件,咱們只須要記住IDC_EDIT便可,而不須要關心它等於1001。接着,咱們在Styles選項卡中設置了Multiline屬性,同時清除了Auto HScroll屬性,二者共同做用使用得編輯框IDC_EDIT支持多行文本,而且如文本行的長度超過編輯框寬度時自動回行。
下面的步驟是最重要的一步,咱們動用了功能強大的工具ClassWizard。首先,咱們將編輯框與一個CString對象相關聯,這使用了一種被稱爲Dialog Data Exchange (DDX)的機制。在這種機制中,咱們先在處理函數OnInitDialog或對話框類的構造函數中對對話框對象的成員變量進行初始化,在對話框顯示以前,框架的DDX機制將成員變量的值傳遞給對話框中的控件。這個過程在成員函數DoModal或Create被調用的過程當中發生。類CDialog中對OnInitDialog成員函數的默認實現調用了類CWnd成員函數UpdateData來初始化對話框中的控件。這時咱們就能夠看到前面的第6步還可在具備下面的幾種變通方案:
1. 將代碼行
m_strEdit="您好! 請在這裏輸入一些字符串。";
移到對基類的OnInitDialog成員函數的調用以前,即位於下面的代碼以前:
CDialog::OnInitDialog();
2. 將代碼
m_strEdit="您好! 請在這裏輸入一些字符串。";
移到類CDialogDemoDlg的構造函數中。
對於上面的兩種方法,與前面第6步中使用的方法相比,咱們沒有必要調用類CWnd的成員函數UpdateData。由於該函數在類CDialog的成員函數OnInitDialog中將被調用。
這三種方法之間並無明確的優劣之分,在不少狀況下,它們分別適用於不一樣的場合。
這裏咱們說一下成員函數UpdateData。該函數帶有一個布爾類型的參數,若是該參數爲FALSE,函數UpdateData將成員變量的值傳遞給對話框的變量;而若是該參數爲TRUE,函數UpdateData將進行相反的過程。
若是用戶單擊了對話框中ID爲IDOK的按鈕,或者以TRUE爲參數調用函數UpdateData,DDX機制從控件中將值傳遞到成員變量,同時對話框數據驗證(dialog data validation,DDV)機制根據設定的驗證規則驗證全部數據項。
在數據交換的過程當中,成員函數UpdateData先建立一個CDataExchange對象,而後調用對話框對類CDialog成員函數DoDataExchange的重載版本。該CDataExchange對象將做爲成員函數DoDataExchange的一個參數,該參數定義了數據交換的上下文。
在DoDataExchange中,咱們爲每個數據成員指定了一個對DDX函數的調用。每個函數定義了基於由成員函數UpdateData所提供的CDataExchange參數所肯定的上下文而進行的雙向數據交換。
下面的代碼摘自實現文件DialogDemo.cpp中對函數DoDataExchange的定義:
void CDialogDemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDialogDemoDlg)
DDX_Text(pDX, IDC_EDIT, m_strEdit);
DDV_MaxChars(pDX, m_strEdit, 50);
//}}AFX_DATA_MAP
}
在兩行註釋//{{AFX_DATA_MAP和//}}AFX_DATA_MAP之間的代碼部分稱做數據映射。函數DDX_Text使用CString對象m_strEdit與ID爲IDC_EDIT的編輯框控件相關聯。函數DDV_MaxChars設置與編輯框控件IDC_EDIT相關聯CString對象m_strEdit的最大長度爲50。
須要注意的是,若是用戶在模式對話框中單擊了「取消」(Cancel)按鈕,DoModal函數將返回值IDCANCEL,在這種狀況下,在對話框和對話框對象以前的數據交換不會發生。
因爲這個緣由,若是DoModal函數返回了值IDOK,咱們可使用下面的代碼來複述用戶在對話框中所輸入的值:
AfxMessageBox(dlg.m_strEdit);
這基於下面的一個事實:
類CWinThread的數據成員m_pMainWnd有一個有用的特徵,若是由該成員所引用的窗口被關閉的話,MFC庫將自動的終止CWinThread對象所表明的線程。這樣,若是咱們將指向dlg的指針賦予了成員變量m_pMainWnd,那麼,不管咱們單擊了「確認」仍是「取消」,應用程序的主線程都將被自動終止,以後的代碼固然不會獲得執行。而在本示例中,咱們但願在對話框被關閉後程序繼續運行(即彈出一個消息重述用戶所輸入的內容),所以不該該將dlg對象的指針賦予成員變量m_pMainWnd,從而須要將前面的代碼從函數OnInitDialog中刪除。
第二節 全部窗口類的基類:CWnd
在MFC中類CWnd是一個很重要的類,它封裝了Windows窗口句柄HWND。在Windows編程中,窗口句柄惟一的標識了一個窗口。然而,儘管類CWnd的對象和窗口句柄之間有着如此緊密的聯繫,但二者並非等同的概念。CWnd對象經過類CWnd的構造函數和析構函數建立和消毀,而Windows窗口是Windows內部的一種數據結構,在類CWnd中,它經過Create成員函數建立,經過其析構函數消毀。除此以外,成員函數DestroyWindow能夠消毀Windows窗口,而不須要消毀CWnd對象。
傳統的Windows應用程序中,消息是經過一個稱做窗口過程(window procedure,一般具備WndProc之類的函數名)的回調函數來處理的。這種方式在MFC中仍然使用,但爲CWnd類及其消息映射所隱藏。在類CWnd中,Windows通知消息會被自動的經過消息映射傳遞到類CWnd中合適的OnMessage成員函數(這裏OnMessage是指這些函數具備的以On爲前綴的函數名,如OnPaint和前面接觸到的OnInitDialog等)進行處理。一般咱們都在類CWnd的派生類中重載須要處理的特定消息所對應的OnMessage成員函數。除了直接從CWnd派生新的窗口類之外,咱們更傾向於從MFC中定義的其它類,如CFrameWnd、CMIDFrameWnd、CMDIChileWnd、CView和CDialog以及CButton之類的控件類派生新的窗口類。在MFC中定義的這些類自己也是從CWnd派生的。
一般咱們使用兩個步驟來建立一個窗口:首先,調用類CWnd的構造函數來構造一個CWnd對象,而後調用其成員函數Create來建立窗口並將該窗口與所建立的CWnd對象相關聯。
當用戶終止該窗口時,消毀與之相關聯的CWnd對象,或者調用CWnd對象的成員函數DestroyWindow刪除窗口並消毀其數據結構。
大多數以HWND爲參數的Win32 API函數都已做爲類CWnd的成員函數進行了封裝,事實上,不少時候咱們經過類Wnd的派生類調用的成員函數並非由派生類自己所提供的,而是在類CWnd中進行定義的。下面咱們分類給出在CWnd類中定義的各種成員函數。完整而詳盡的說明每個成員函數在本書中是不現實的,這裏咱們僅給出對每個成員函數的簡短說明,以便讀者在編程時可以很快的查找到所需的函數,這時再去查找有關於該函數的詳細的說明就不是一件困難的事了。
1. 類CWnd的數據成員(表6.2):
表6. 2 類CWnd的數據成員
數據成員 |
描述 |
m_hWnd |
與該CWnd對象相關聯的Windows窗口句柄(HWND) |
2. 構造函數/析構函數(表6.3):
表6. 3 類CWnd的構造函數和析構函數
成員函數 |
得到圖標句柄 |
SetIcon |
設置句柄爲一指定圖標 |
GetWindowContextHelpId |
得到幫助上下文標識符 |
SetWindowContextHelpId |
設置幫助上下文標識符 |
ModifyStyle |
修改當前窗口樣式 |
ModifyStyleEx |
修改當前窗口的擴展樣式 |
5. 窗口大小和位置函數(表6.6):
表6. 6 類CWnd的窗口大小和位置成員函數
成員函數 |
描述 |
GetWindowPlacement |
得到顯示狀態和窗口的正常、最小化和最大化位置 |
SetWindowPlacement |
設置顯示狀態和窗口的正常、最小化和最大化位置 |
GetWindowRgn |
得到窗口的窗口區域的拷貝 |
SetWindowRgn |
設置窗口區域 |
IsIconic |
判斷窗口是否被最小化(圖標化) |
IsZoomed |
判斷窗口是否被最大化 |
MoveWindow |
改變窗口的位置和度量 |
SetWindowPos |
改變子窗口、彈出式窗口或頂層窗口的大小、位置和順序 |
ArrangeIconicWindows |
排列全部最小化的子窗口 |
BringWindowToTop |
將CWnd對象放到覆蓋窗口棧的頂部 |
GetWindowRect |
得到CWnd對象的屏幕座標 |
GetClientRect |
得到CWnd對象客戶區的度量 |
6. 窗口訪問函數:
表6. 7 類CWnd的窗口訪問成員函數
成員函數 |
描述 |
ChildWindowFromPoint |
判斷包含指定點的子窗口 |
FindWindow |
返回由其窗口名稱和窗口類標識的窗口的句柄 |
GetNextWindow |
返回窗口管理器列表中的下一個(或上一個)窗口 |
GetOwner |
返回指向CWnd對象的全部者的指針 |
續表6.7
成員函數 |
描述 |
SetOwner |
改變CWnd對象的全部者 |
GetTopWindow |
返回屬於CWnd對象的第一個子窗口 |
GetWindow |
返回與當前窗口有指定關係的窗口 |
GetLastActivePopup |
判斷由CWnd對象全部的彈出窗口中最近激活的窗口 |
IsChild |
判斷CWnd對象是否爲一個子窗口 |
GetParent |
若是存在的話,得到CWnd對象的父窗口 |
GetSafeOwner |
得到給定窗口的安全的全部者 |
SetParent |
改變父窗口 |
WindowFromPoint |
標識包括給定點的窗口 |
GetDlgItem |
從指定的對話框得到標準符爲指定ID的控件 |
GetDlgCtrlID |
若是CWnd爲一子窗口,返回其ID值 |
SetDlgCtrlID |
當CWnd對象爲一子窗口(不只指對話框中的控件)時,爲其指定控件ID或窗口ID |
GetDescendantWindow |
檢查全部下級窗口(descendant window)並返回具備指定ID的窗口 |
GetParentFrame |
得到CWnd對象的父框架窗口 |
SendMessageToDescendants |
發送一條消息到窗口的全部下級窗口 |
GetTopLevelParent |
得到窗口的頂層父窗口 |
GetTopLevelOwner |
得到窗口的頂層全部者窗口 |
GetParentOwner |
返回指向子窗口的父窗口的指針 |
GetTopLevelFrame |
得到窗口的頂層框架窗口 |
UpdateDialogControls |
用來更新對話框按鈕或其它控件的狀態 |
UpdateData |
初始化對話框或從對話框中獲取數據 |
CenterWindow |
相對於父窗口使窗口居中 |
7. 更新和繪製函數(表6.8)
表6. 8 類CWnd的更新和繪製函數
成員函數 |
描述 |
BeginPaint |
爲重繪操做準備CWnd對象 |
EndPaint |
標記重繪操做的結束 |
續表6.8
成員函數 |
描述 |
|
在指定的設備上下文繪製當前窗口 |
PrintClient |
在指定的設備上下文(一般是打印機)繪製全部窗口 |
LockWindowUpdate |
禁止或從新容許繪製指定的窗口 |
UnlockWindowUpdate |
解除CWnd::LockWindowUpdate對窗口的鎖定 |
GetDC |
得到客戶區的顯示上下文 |
GetDCEx |
得到客戶區的顯示上下文,並在繪製過程當中容許裁剪 |
RedrawWindow |
在客戶區中更新指定的矩形或區域 |
GetWindowDC |
得到整個窗口的顯示上下文,包括標題條,菜單和滾動條 |
ReleaseDC |
釋放客戶區或窗口設備上下文,並使其可爲其它程序所使用 |
UpdateWindow |
更新客戶區 |
SetRedraw |
決定在CWnd對象中的改變是否被重繪 |
GetUpdateRect |
得到徹底覆蓋CWnd對象的更新區域的最小矩形座標 |
GetUpdateRgn |
得到CWnd對象的更新區域 |
Invalidate |
使用整個客戶區無效 |
InvalidateRect |
經過將給定矩形添加到當前更新區域來使包括在給定矩形內的客戶區無效 |
InvalidateRgn |
經過將給定區域添加到當前更新區域來使包括在給定區域內的客戶區無效 |
ValidateRect |
經過將給定矩形從當前更新區域中移出來使包括在給定矩形內的客戶區有效 |
ValidateRgn |
經過將給定區域從當前更新區域中移出來使包括在給定區域內的窗戶區有效 |
ShowWindow |
顯示或隱藏窗口 |
IsWindowVisible |
判斷窗口是否可見 |
ShowOwnedPopups |
顯示或隱藏窗口擁有的全部彈出式窗口 |
EnableScrollBar |
容許或禁止滾動條上的一個或兩個箭頭 |
8. 座標映射函數(表6.9)
表6. 9 類CWnd的座標映射函數
成員函數 |
描述 |
MapWindowPoints |
從CWnd對象的座標空間映射一系列點到另外一窗口的座標空間 |
續表6.9
成員函數 |
描述 |
ClientToScreen |
轉換給定點的客戶座標或顯示矩形到屏幕座標 |
ScreenToClient |
轉換給定點的屏幕座標或顯示矩形到客戶座標 |
9. 窗口文本函數(表6.10)
表6. 10 類CWnd的窗口文本函數
成員函數 |
描述 |
SetWindowText |
設置窗口文本或標題條(若是有的話)爲指定文本 |
GetWindowText |
得到窗口文本或標題條 |
GetWindowTextLength |
返回窗口文本或標題條的長度 |
SetFont |
設置當前字體 |
GetFont |
得到當前字體 |
10. 滾動函數(表6.11)
表6. 11 類CWnd的滾動成員函數
成員函數 |
描述 |
GetScrollPos |
得到滾動框的當前位置 |
GetScrollRange |
拷貝給定滾動框中滾動塊的當前最大和最小位置 |
ScrollWindow |
滾動客戶區的內容 |
ScrollWindowEx |
滾動客戶區內容。與ScrollWindowEx相似,但具備一些附加特性 |
GetScrollInfo |
得到關於某一滾動條的由SCROLLINFO結構維護的信息 |
GetScrollLimit |
得到滾動條的限制 |
SetScrollInfo |
設置關於滾動條的信息 |
SetScrollPos |
設置滾動條的當前位置,並在指定的狀況下重繪滾動條以反映新的位置 |
SetScrollRange |
設置給定滾動條的最小和最大位置值 |
ShowScrollBar |
顯示或隱藏滾動條 |
EnableScrollBarCtrl |
容許或禁止兄弟滾動條控件 |
GetScrollBarCtrl |
返回兄弟滾動條控件 |
RepositionBars |
在客戶區中對控件條重定位 |
11. 拖放函數(表6.12)
表6. 12 類CWnd的拖放成員函數
成員函數 |
描述 |
DragAcceptFiles |
使窗口能夠接受文件拖放 |
12. 插入符函數(表6.13)
表6. 13 類CWnd的插入符成員函數
成員函數 |
描述 |
CreateCaret |
新的插入符形狀,並得到該插入符的全部權 |
CreateSolidCaret |
建立方塊形狀的插入符,並得到該插入符的全部權 |
CreateGrayCaret |
建立變灰方塊形狀的插入符,並得到該插入符的全部權 |
GetCaretPos |
得到插入符當前位置的客戶座標 |
SetCaretPos |
移動插入符到指定的位置 |
HideCaret |
隱藏插入符 |
ShowCaret |
在插入符的當前位置顯示插入符 |
13. 對話框項函數(表6.14)「
表6. 14 類CWnd的對話框項函數
成員函數 |
描述 |
CheckDlgButton |
在按鈕控件前放置選中標記或清除按鈕控件的選中標記 |
CheckRadioButton |
選中指定的單選鈕並清除指定給中其它全部單選鈕的選中標記 |
GetCheckedRadioButton |
返回一組按鈕中當前選中單選鈕的ID |
DlgDirList |
使用文件或目錄列表填充一列表框 |
DlgDirListComboBox |
使用文件或目錄列表填充一組合框的列表框 |
DlgDirSelect |
從一列表框中得到當前選擇 |
DlgDirSelectComboBox |
從一組合框的列表框中得到當前選擇 |
GetDlgItemInt |
將給定對話框中某一控件的文本轉換爲一個整數值 |
GetDlgItemText |
得到與某一控件相關聯的標題或文本 |
GetNextDlgGroupItem |
查找同一組中的下一個(或前一個)控件 |
續表6.14
成員函數 |
描述 |
GetNextDlgTabItem |
查找在指定控件以前(或以後)的第一個具備WS_TABSTOP樣式的控件 |
IsDlgButtonChecked |
判斷一個按鈕控件是否選中 |
IsDialogMessage |
判斷一個給定消息是否影響非模態對話框,若是是,處理該消息 |
SendDlgItemMessage |
向指定的控件發送一條消息 |
SetDlgItemInt |
使某一控件的文本爲某一給定整數值 |
SetDlgItemText |
設置指定對話框中某一控件的標題或文本 |
SubclassDlgItem |
將一個Windows控件與CWnd對象相關聯,並使其經過CWnd對象的消息映射傳遞消息 |
ExecuteDlgInit |
初始化對話框資源 |
RunModalLoop |
爲一模態窗口獲取、翻譯或發送消息 |
ContinueModal |
使一窗口繼續保持模態 |
EndModalLoop |
結束某一窗口的模態狀態 |
14. 數據綁定函數(表6.15):
表6. 15 類CWnd的數據綁定成員函數
成員函數 |
描述 |
BindDefaultProperty |
將調用對象的默認簡單綁定屬性(該屬性在類型庫中標記)綁定至相關聯的數據源控件的遊標 |
BindProperty |
將數據綁定控件的遊標綁定屬性綁定至數據源控件,並使用MFC綁定管理器註冊綁定關係 |
GetDSCCursor |
得到指向由數據源控件的數據源、用戶名、密碼和SQL屬性定義的底層遊標的指針 |
15. 菜單函數(表6.16)
表6. 16 類CWnd的菜單成員函數
成員函數 |
描述 |
GetMenu |
得到指向指定菜單的指針 |
SetMenu |
設置菜單爲指定的菜單 |
DrawMenuBar |
重繪菜單條 |
GetSystemMenu |
容許應用程序訪問控制菜單以進行復制和修改 |
續表6.16
成員函數 |
描述 |
HiliteMenuItem |
加亮頂層菜單項或移去頂層菜單項的加亮顯示 |
16. 工具提示函數(表6.17)
表6. 17 類CWnd的工具提示函數
成員函數 |
描述 |
EnableToolTip |
容許工具提示控件 |
CancelToolTip |
禁止工具提示控件 |
FilterToolTipMessage |
得到對話框中與某一控件相關聯的標題或文本 |
OnToolHitTest |
判斷一個點是否在指定工具的綁定矩形內,並得到該工具的信息 |
17. 計時器函數(表6.18)
表6. 18 類CWnd的計時器成員函數
成員函數 |
描述 |
SetTimer |
安裝系統計時器,計時器觸發時發送WM_TIMER消息 |
KillTimer |
消除系統計時器 |
18. 提示函數(表6.19)
表6. 19 類CWnd的提示成員函數
成員函數 |
描述 |
FlashWindow |
閃爍窗口一次 |
MessageBox |
建立並顯示一個包括應用程序提供的消息和標題的窗口 |
19. 窗口消息函數(表6.20)
表6. 20 類CWnd的窗口消息成員函數
成員函數 |
描述 |
GetCurrentMessage |
返回窗口正在處理的消息的指針。僅當在一個OnMessage消息處理函數中調用該成員函數。 |
Default |
調用默認窗口過程,該過程提供對全部應用程序未處理的消息的默認處理 |
PreTranslateMessage |
由CWinApp使用,在窗口消息被髮送到TranslateMessage和DispatchMessage以前對其進行過濾 |
續表6.20
成員函數 |
描述 |
SendMessage |
將一條消息發送到CWnd對象,直至該對象處理該消息以後才返回 |
PostMessage |
將一條消息放入程序的消息隊列,不等待窗口處理該消息就當即返回 |
SendNotifyMessage |
將指定消息發送到窗口,並儘量快的返回,這依賴於調用線程如何建立窗口 |
20. 剪貼板函數(表6.21)
表6. 21 類CWnd的剪貼板函數
成員函數 |
描述 |
ChangeClipboardChain |
從剪貼板查看器鏈中移去CWnd對象 |
SetClipboardViewer |
添到CWnd對象到窗口鏈,這些窗口當剪貼板內容改變時會收到通知 |
OpenClipboard |
打開剪貼板。其它程序僅當Windows CloseClipboard函數被調用時才能夠更改剪貼板 |
GetClipboardOwner |
得到剪貼板的當前擁有者的指針 |
GetOpenClipboardWindow |
得到指向當前打開剪貼板的窗口的指針 |
GetClipboardViewer |
得到指向剪貼板查看器鏈中第一個窗口的指針 |
21. OLE控件函數(表6.22)
表6. 22 類CWnd的OLE控件函數
成員函數 |
描述 |
SetProperty |
設置OLE控件屬性 |
OnAmbientProperty |
實現環境屬性值 |
GetControlUnknown |
得到指向一未知OLE控件的指針 |
GetProperty |
得到一OLE控件的屬性 |
InvokeHelper |
調用OLE控件方法或屬性 |
22. 可重載函數(表6.23)
表6. 23 類CWnd的可重載成員函數
成員函數 |
描述 |
WindowProc |
爲CWnd對象提供一個窗口過程。默認的窗口過程經過消息映射發送消息 |
DefWindowProc |
調用默認窗口過程,該過程提供應用程序未處理的全部窗口消息的默認處理 |
PostNcDestroy |
在窗口被消毀後由OnNcDestroy函數調用 |
OnNotify |
由框架調用以通知父窗口某一事件在某一控件中發生或者該控件須要信息 |
OnChildNotify |
由父窗口調用以給通知控件一個響應控件通知的機會 |
DoDataExchange |
用於對話框數據交換和驗證。由UpdateData調用 |
其他函數包括對各類窗口消息的消息處理函數,這些函數爲數衆多,這裏咱們限於篇幅再也不一一介紹。類CWnd中定義的消息處理函數幾乎都具備一致的命名方式,其格式爲前綴On再加上相應的消息名,如WM_PAINT消息的處理函數在類CWnd中被命名爲OnPaint。所以,只需知道所需處理的消息,就能夠很快的推知該消息的處理函數名。
第三節 按鈕
在本節中要講述的實際包括四種控件:下壓按鈕、單選鈕、複選框和組框,它們之間不管在外觀仍是在使用上都有較大的差別。在MFC中之因此使用一個類CButton來封裝這四種不一樣控件純粹出於歷史的緣由。這使得一些使用過Visual Basic之類的編程工具的程序員可能會有一點混淆,但相信只須要很短的時間就能夠習慣這一點轉變。
下面咱們分別講述這四種按鈕控件:
6.3.1 下壓按鈕
在基於對話框的應用程序中,下壓按鈕是最多見的控件之一,如圖6.10所示。
圖6. 10 下壓按鈕
下面的步驟講述如何向對話框中添加下壓按鈕控件。
1. 在ResourceView中雙擊須要添加下壓按按鈕控件的對話框模板,Developer Studio將在資源編輯器中打開該對話框模板。如圖6.11所示。
2. 在圖6.3所示的控件工具窗口中選擇圖標 ,直接使用鼠標在對話框中繪製出一個下壓按鈕。
3. 右擊所繪製的下壓按鈕,選擇Properties命令打開其屬性對話框,設置下壓按鈕的各項屬性。下面詳細描述這些屬性的含義:
圖6. 11 在資源編輯器中打開一對話框模板
圖6. 12 在對話框中繪製下壓按鈕控件
通常屬性:
ID: |
在頭文件中定義的符號。類型:符號、整數或用引號括起來的字符串 |
Caption: |
控件標籤文本。若是在標題中的某個字母前加上了「&」符號,該字母在顯示時將被加上下劃線,相應的「&」符不會被顯示。在運行直接按下加有下劃線的字母同單擊按鈕具備一樣的效果。默認狀況下,資源編輯器對按鈕標題的命名依賴於控件的類型,如Button一、Button2等。 |
Visible: |
決定當應用程序第一次運行時控件是否可見。類型:布爾值 默認值爲真 |
Disabled: |
決定當對話框建立時該控件是否顯示爲禁止狀態。類型:布爾值 默認值爲假 |
Group: |
指定一組控件中的第一個控件。在同組控件中用戶可使用箭頭鍵在控件之間移動。以tab order爲序,在該控件以後的全部該屬性值爲False的控件將被視爲同一組控件,直到趕上Group屬性標記爲True的控件爲止。類型:布爾值 默認值爲假 |
Tabstop: |
決定用戶是否可使用TAB鍵來定位到該控件。類型:布爾值 默認值爲假 |
HelpID: |
爲控件指定一個幫助標識符。該標識符基於相應的資源標識符。類型:布爾值 默認值爲假 |
樣式:
Default button: |
該屬性爲真時,控件將做爲對話框中的默認按鈕,默認按鈕在對話框第一次顯示時具備粗的黑邊,用戶在對話框中按下ENTER鍵至關於單擊該按鈕。一個對話框中只容許有一個默認按鈕。類型:布爾值 默認值爲假 |
Owner draw: |
建立一個自繪按鈕。使用自繪按鈕能夠定製按鈕的外觀。使用自繪按鈕須要重載下面的兩個函數或其中之一:CWnd::OnDrawItem和CButton::OnDraw。 |
Icon: |
在按鈕顯示時使用一個圖標來代替文本。類型:布爾值 默認值爲假 該按鈕樣式爲Windows 95中新引入的按鈕樣式 |
Bitmap: |
在按鈕顯示時使用位圖來代替文本。類型:布爾值 默認值爲假 該樣式爲Windows 95中新引入的樣式 |
Multi-line: |
當按鈕文本太長時使用多行迴繞的方式進行顯示。類型:布爾值 默認值爲假 |
Notify: |
按鈕控件被單擊或雙擊時通知父窗口。類型:布爾值 默認值爲真 |
Flat: |
使用平面外觀代替按鈕默認的三維外觀。類型:布爾值 默認值爲假 |
Horizontal alignment: |
設置按鈕標題文本的對齊方式(左對齊、右對齊、居中對齊或使用默認位置) |
Vertical alignment: |
設置按鈕標題文本的對齊方式(向上對齊、向下對齊、居中對齊或使用默認位置) |
擴展樣式
Client edge: |
使按鈕看起來有下凹的感受。類型:布爾值 默認值爲假 |
Static edge: |
在按鈕邊緣建立邊框。類型:布爾值 默認值爲假 |
Modal frame: |
提供一個三維框架 |
Transparent: |
使控件透明。位於透明窗口下面的窗口不會被該窗口所覆蓋。具備透明樣式的窗口僅當全部底層兄弟窗口完成更新以後纔會收到WM_PAINT消息。類型:布爾值 默認什爲假 |
Accept files: |
是否接受文件拖放。若是在控件上放下文件時,控件將接收到WM_DROPFILES消息。類型:布爾值 默認值爲假 |
No parent notify: |
指定子窗口不向父窗口發送WM_PARENTNOTIFY消息。類型:布爾值 默認值爲假 |
Right aligned text: |
指定文本爲右對齊。類型:布爾值 默認值爲假 |
Right-to-left reading order: |
使用從右向左的閱讀方式來顯示文本。主要用於希伯來語系和阿拉伯語等。類型:布爾值 默認值爲假 |
咱們通常只處理按鈕控件一種通知消息:BN_CLICKED,該消息表示用戶單擊了該按鈕控件。按鈕控件的另一種通知消息是BN_DOUBLECLICKED,它表示用戶雙擊了按鈕控件,可是通常狀況下咱們不須要處理下壓按鈕的雙擊事件。
圖6. 13 ClassWizard對話框:Message Maps選項卡
下面咱們介紹如何爲下壓按鈕的單擊事件添加消息處理函數和消息映射,這裏咱們假設所添加的下壓按鈕ID爲IDC_CLICKHERE,標題文本爲「單擊這裏(&C)」,其他屬性使用默認設置。
第一種方法以下:
1. 在資源編輯器右擊按鈕IDC_CLICKHERE,選擇「ClassWizard」,打開如圖6.13所示的窗口,單擊Message Maps選項卡。
確信在Project處選擇的工程爲當前工程,Class name處爲當前對話框模板所對應的類。Object IDs列表框中給出了當前對話框類中的全部對象標識符,從中選擇IDC_CLICKHERE,即咱們剛纔添加的下壓按鈕,這裏,在右邊的Message列表框中給出了當前對象的消息,這裏即BN_CLICKED和BN_DOUBLECLICKED,從中選擇BN_CLICKED (它表明了按鈕的單擊事件),而後單擊右邊的Add Function按鈕(注意:Add Function按鈕僅當已選擇了某一消息時纔會出現)。
圖6. 14 決定是否須要更改命令處理函數名
圖6. 15 爲控件通知消息添加處理函數
2. 在隨後出現的對話框(如圖6.14所示)中選擇是否須要更改命令處理函數的函數名。ClassWizard的默認函數名聽從於下面的命令協議:
前綴On + 控件ID中除去IDC_前綴的剩餘部分
這裏咱們接受默認的命令處理函數名OnClickhere。
3. 新添加的命令處理函數OnClickhere已經出如今圖6.13所示的對話框中的下面的Member functions部分。同時,Edit Code按鈕得到輸入焦點。單擊該按鈕,ClassWizard將在Developer Studio的代碼編輯器窗口中打開函數OnClickhere,並高亮度顯示下面的// TODO註釋:
// TODO: Add your control notification handler code here
咱們使用下面的代碼來替換上面的// TODO註釋:
MessageBox
("您剛纔單擊了按鈕 IDC_CLICKHERE, 所以相應的命令處理函數 OnClickhere 被調用!");
第二種方法:
1. 在資源編輯器中右擊按鈕IDC_CLICKHERE,選擇Events命令,打開如圖6.15所示的對話框:
2. 在Class or object to handle列表框中選擇IDC_CLICKHERE,而後在New Windows messages/events列表框中選擇BN_CLICKED,單擊右邊的Add and Edit,餘下的步驟同第一種方法的第2步開始相同。
這時編譯並運行上面的程序,單擊標籤爲「單擊這裏」的下壓按鈕,彈出如圖所示的消息框。
圖6. 16 程序PushButton的運行結果
下面咱們來看相應的消息映射。
首先,在類CPushButtonDlg的定義中添加了消息處理函數OnClickhere的原型:
afx_msg void OnClickhere();
函數OnClickhere的聲明被放進了兩行註釋分隔符//{{AFX_MSG(CPushButtonDlg)和//}}AFX_MSG之間。前面咱們提到過,ClassWizard將由它定義的消息處理函數的聲明放入這兩行註釋分隔符之間。
下面咱們來看相應的消息映射入口。它位於實現文件PushButtonDlg.cpp中的兩個宏BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之間:
ON_BN_CLICKED(IDC_CLICKHERE, OnClickhere)
其中第一個參數IDC_CLICKHERE爲控件的標識符,第二個參數OnClickhere爲相應的消息處理函數。
一旦弄清楚了由ClassWizard添加這些代碼,咱們就能夠手動的添加命令消息處理函數的消息映射。可是,從上面的過程當中咱們能夠很明顯的看出一點,使用ClassWizard來完成這一點要簡單得多。
下面咱們介紹與下壓按鈕控件有關的幾個技巧:
(1) 在運行過程當中改變下壓按鈕的標題文本
有時候咱們須要在程序運行的過程當中改變按鈕的標題文本。典型的,咱們可能須要根據用戶所輸入的數據來決定按鈕上應該寫些什麼。咱們到前面去看一下表6.14,看一看有什麼成員函數能夠完成這種功能。
很好,類CWnd的成員函數SetDlgItemText能夠由窗口或對話框全部的控件的標題文本。其原型以下:
void SetDlgItemText( int nID, LPCTSTR lpszString );
其中nID爲控件標識符(ID),lpszString爲控件的新標題文本。
成員函數SetDlgItemText事實上是向控件發送一條WM_SETTEXT消息,該消息的wParam參數必須爲0,而lParam爲指向窗口標題文本字符串的指針。
所以,SetDlgItemText等價於下面的函數調用:
CWnd::SendDlgItemMessage(nID, WM_SETTEXT, 0, LPARAM(lpszString));
或
::SendDlgItemMessage(GetSafeHwnd(), nID, WM_SETTEXT, 0, LPARAM(lpszString));
好比說,咱們用以將下面的代碼添加到OnClickhere中對MessageBox的調用以後:
SetDlgItemText(IDC_CLICKHERE,"此按鈕已被單擊過.");
(2) 使用按鈕無效(或有效)
假設咱們在上面的例子中但願用戶只能單擊按鈕IDC_CLICKHERE一次。那麼,按鈕IDC_CLICKHERE被單擊一次以後應該變灰,以禁止用戶再次單它。這能夠經過下面的步驟來實現:
首先調用對話框對象的成員函數GetDlgItem (該成員函數在類CWnd中定義),該成員函數得到一個指向對話框中的控件的CWnd指針,而後再經過該指針調用控件對象的成員函數EnableWindow (該成員函數在類CWnd中定義)。該成員函數容許或禁止調用它的CWnd對象對應窗口。整個過程可使用一行語句來實現,以下所示:
GetDlgItem(IDC_CLICKHERE)->EnableWindow(FALSE);
其中GetDlgItem函數以控件的ID爲參數,返回值的類型爲CWnd *,若是須要經過該指針調用在類CButton所定義的成員函數,可使用強制類型轉換。EnableWindow以一個布爾值爲參數,該參數爲真時表示容許該窗口接受鼠標和鍵盤輸入,爲假時禁止該窗口接受鼠標和鍵簽署輸入。這裏再一次強調,控件自己也是一種窗口。
將上面的代碼放到命令處理函數OnClickhere的最後,這樣,在單擊一次按鈕「單擊這裏」以後,對話框如圖6.17所示。
圖6. 17 處於禁止狀態的控鈕控件
此外,若是使用了ClassWizard爲按鈕創建了對話框的成員變量的數據映射,則能夠經過對話框中的成員變量直接操縱控件。在本例中,若是咱們已將下壓按鈕映射爲類型爲CButton的成員變量m_bnClickhere,則能夠經過下面成員函數調用設置按鈕的容許狀態:
m_bnClickhere->EnableWindow(FALSE);
(3) 使按鈕得到輸入焦點
具備輸入焦點的窗口將會獲得全部的鍵盤輸入消息。咱們能夠經過類CWnd的成員函數GetFocus來使對話框中的控件得到輸入焦點。
試將下面的代碼加到消息處理函數OnInitDialog的return語句前:
m_bnClickhere.SetFocus();
或
GetDlgItem(IDC_CLICKHERE)->SetFocus();
編譯並運行程序。很是奇怪,輸入焦點並無被設置到下壓按鈕「單擊這裏」上。依然是按鈕「肯定」擁有當前輸入焦點。
請注意這樣的事實:
將下面的代碼
return TRUE;
修改成
return FALSE;
這時再編譯並運行程序,則輸入焦點將被正常地設置到下壓按鈕「單擊這裏」上。這時按下空格鍵至關於在按鈕「單擊這裏」上單擊鼠標左鍵。
(4) 使用圖形代替文本
在一些應用程序,尤爲是一些多媒體應用程序中,咱們但願按鈕的外觀看起來更加的美觀,好比說咱們但願使用多變的圖形代替單調乏味的純文本。對於通常的按鈕控件,咱們可使用兩種方法來在按鈕中使用圖形來代替文本。
第一種方法是使用圖標來代替文本。下面的示例說明了這種用法:
1. 使用資源編輯器或其它工做編輯一個圖標資源,其ID爲IDI_CLICKHERE,圖案如圖6.18所示。
2. 在但願使用圖標圖案的按鈕控件的Properties屬性框在Styles選項卡中設置Icon屬性爲真。並按圖6.19修改對話框及其中控件的大小。
3. 在類CPushButtonDlg的消息處理成員函數OnInitDialog中添加下面的代碼。這些代碼應該在對基類的OnInitDialog成員函數的調用以後。
圖6. 18 圖標IDI_CLICKHERE
圖6. 19 爲使用圖標按鈕修改
對話框中控件的大小
HICON hIcon=AfxGetApp()->LoadIcon(IDI_CLICKHERE);
m_bnClickhere.SetIcon(hIcon);
編譯該應用程序,運行結果如圖6.20所示。
圖6. 20 在按鈕中使用圖標的示例
這時單擊按鈕Click Here,圖標圖案會有向右和向下下壓的效果。
第二種方法是使用位圖來代替文本。步驟以下:
圖6. 21 位圖資源IDB_CLICKHERE
1. 向工程資源中添加如圖6.21的位圖資源,其ID爲IDB_CLICKHERE。
2. 在但願使用位圖圖案的按鈕控件的Properties屬性框在Styles選項卡中設置Bitmap屬性爲真。咱們注意到Icon屬性和Bitmap屬性是互斥的,即選擇一屬性的同時也清除了另外一屬性。並按圖6.19修改對話框及其中控件的大小。同時參考最終運行結果(如圖6.22)修改對話框及其按鈕的大小。
3. 在OnInitDialog成員函數中添加以下的代碼:
HBITMAP hBitmap=LoadBitmap(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDB_CLICKHERE));
m_bnClickhere.SetBitmap(hBitmap);
在上面的代碼中,咱們使用Win32 API函數LoadBitmap (注意它不是類CWinApp的成員函數)來加載位圖資源IDB_CLICKHERE,從而得到位圖句柄hBitmap,最後以該句柄爲參數調用類CButton的成員函數SetBitmap。
編譯並運行上面的程序,獲得如圖6.22所示的運行結果。
圖6. 22 在按鈕中使用位圖的示例
1. 選擇Insert菜單下的Resource命令,打開如圖所示的對話框。
圖6. 23 插入新的資源
2. 從中單擊Import按鈕,從位圖文件Clickhere.bmp中輸入資源。注意在文件類型中選擇「All files (*.*)」。
Developer Studio將彈出如圖6.24所示的警告對話框。該對話框代表位圖資源已被正確添加。但因爲使用了多於256色的顏色數,所以該資源不能夠在資源編輯器中打開。
圖6. 24 試圖添加使用了多於256色的位圖資源時的警告消息框
3. 按正常的方法將所添加的位圖資源的ID修改成IDB_CLICKHERE。必要時從新編輯資源文件或工程。
6.3.2 位圖按鈕
位圖按鈕是由MFC提供的幾種附加控件之一。在前一節的過程當中,咱們可使用一個位圖來代替文本做爲下壓按鈕的標籤。而在位圖按鈕中,咱們可使用多達四個位圖來分別表明按鈕處於四種不一樣的狀態(凸起、按下、得到焦點或被禁止)下的顯示。並且,使用位圖按鈕還能夠去除掉使人討厭的按鈕黑邊。而使用位圖按鈕並不複雜,可是相比起標準的按鈕控件(它由Windows自身所提供)而言有一些特殊。下面的過程描述了位圖按鈕的使用,它們在MFC中使用類CBitmapButton封裝。
1. 使用AppWizard建立新的基於對話框的MFC工程BitmapButton。
2. 使用資源編輯器繪製一個標準按鈕,將其ID設爲IDC_CLICKHERE,標題文本設爲CLICKHERE,而後在Styles選項卡中將Owner draw屬性設置爲真。
3. 向工程中添加四個位圖資源。
"CLICKHEREU" |
"CLICKHERED" |
"CLICKHEREF" |
"CLICKHEREX" |
圖6. 25 位圖按鈕IDC_CLICKHERE所使用的四個位圖資源
所添加的四個位圖資源的ID的設置取決於在第一步中的標題文本的設置:按鈕未按下去時使用的位圖添加了後綴"U";按鈕按下去時使用的位圖添加了後綴"D";按鈕擁有焦點時使用的位圖添加了後綴"F";按鈕被禁止時使用的位圖添加了後綴"X"。須要注意的是,因爲這些位圖資源的ID爲字符串,所以在使用屬性對話框設置其ID時必定要加了雙引號,不然資源編輯器會將該ID值看做表明一個整型量的符號。
4. 在對話框類CBitmapButtonDlg(這裏咱們沿用上一節中的示例程序)中添加類型爲CBitmapButton的新的成員變量m_bnClickhere。
5. 在OnInitDialog成員函數中的return語句前添加下面的代碼:
m_bnClickhere.AutoLoad(IDC_CLICKHERE, this);
CRect rect1,rect2;
CButton *pClickhere=(CButton*)GetDlgItem(IDC_CLICKHERE);
GetClientRect(&rect1);
pClickhere->GetWindowRect(&rect2);
ScreenToClient(&rect2);
pClickhere->MoveWindow(rect2.left,(rect1.Height()-rect2.Height())/2,
rect2.Width(),rect2.Height());
其中第一個參數IDC_CLICKHERE是位圖按鈕的資源ID,第二個參數爲指向該位圖按鈕的父窗口的CWnd對象的指針,這裏即類CBitmapButtonDlg的this指針。類CBitmapButton的成員函數AutoLoad完成如下幾步工做:
(1) 將按鈕與CBitmapButton對象相關聯;
(2) 自動加載按鈕所使用的位圖,條件是這些位圖資源知足步驟2中的命名約定;
(3) 自動改變控件的大小以適合所加載的位圖資源。
接下來的幾行代碼將位圖按鈕在對話框中進行垂直居中。首先類CWnd的成員函數GetClientRect返回了對話框的客戶區矩形,接着,類CWnd的成員函數GetWindowRect返回了控件IDC_CLICKHERE的窗口矩形,而後使用類CWnd的成員函數ScreenToClient將rect2由屏幕座標轉換爲對話框的客戶座標,這是由於類CWnd的成員函數MoveWindow在移動子窗口時將使用父窗口的客戶區座標,而不是使用屏幕座標。
6. 按圖6.26添加下面的下壓按鈕IDC_DISABLE,將其標題設置爲「禁止使用(&X)」。
圖6. 26 位圖按鈕示例程序對話框的設計
7. 將全部下壓按鈕的Tab stop屬性(位於General選項卡中)設置爲真。並按圖6.26調整各控件的大小位置。其中按鈕CLICKHERE的大小的可有可無的,咱們只須要保證對話框左邊是否有足夠的空間來顯示按鈕所使用的位圖便可。
8. 爲按鈕IDC_DISABLE的BN_CLICKED命令編寫下面的命令處理函數:
void CBitmapButtonDlg::OnDisable()
{
CButton *pClickhere=(CButton*)GetDlgItem(IDC_CLICKHERE);
static int bIsEnabled=pClickhere->IsWindowEnabled();
if (bIsEnabled)
{
pClickhere->EnableWindow(FALSE);
SetDlgItemText(IDC_DISABLE,"容許使用(&E)");
}
else
{
pClickhere->EnableWindow(TRUE);
SetDlgItemText(IDC_DISABLE,"禁止使用(&X)");
}
bIsEnabled=!bIsEnabled;
}
上面的代碼實現兩個功能,即當位圖按鈕的狀態爲容許時,單擊按鈕IDC_DISABLE將其狀態設置爲不容許;在相反的狀態下,單擊按鈕IDC_DISABLE將其狀態設置爲容許。因爲實現該過程的代碼比較簡單,所以咱們在這裏不做詳細的講述。
編譯並運行上面的示例程序,其結果如圖6.27所示。
反覆單擊位圖按鈕和禁止使用按鈕,以觀察位圖按鈕在不一樣狀態下的外觀的改變。還可使用TAB鍵改變按鈕的輸入焦點,以觀察位圖按鈕得到輸入焦點和失去輸入焦點時的不一樣外觀。
圖6. 27 位圖按鈕示例程序
6.3.3 組框
組框也是一種按鈕控件。它經常用來在視覺上將控件(典型狀況下是一系列的單選鈕和複選框)進行分組,從而使對話框中的各個控件看起來比較有條理。
圖6. 28 組框(Group box)控件
相對於其它控件來講,組框的使用很是之簡單。這裏咱們須要強調的是,組框僅僅是在視覺上將控件進行分組,事實上控件在編程上的分組依賴於其Group屬性的設置。
組框也能夠發送BN_CLICKED和BN_DOUBLECLICKED命令消息。可是在般狀況下咱們都不對這些命令做響應。此外,組框也能夠設置Icon或Bitmap屬性(注意它們之間的互斥的),即咱們可使用圖標或位圖來代替默認狀況下的文本。可是在絕大多數狀況下,咱們僅使用純文原本做爲組框的標題。
與前面講述的下壓按鈕相似,咱們一樣可使用SetDlgItemText成員函數來設置組框控件的標題文本。此外,咱們還可使用GetDlgItem來得到與組框控件相關聯的CWnd對象的指針,而後經過該指針調用成員函數SetWindowText來實現一樣的功能。因爲在程序中經常不須要頻繁的操縱組框控件,所以大多數狀況下咱們不須要爲組框控件進行成員變量的映射,但這種方法是徹底能夠的。
對於如何將控件進行分組的方法在講述單選鈕和複選框時再做介紹。
6.3.4 單選鈕
圖6. 29 單選鈕示例程序
單選鈕用來表示一系列的互斥選項,這些互斥選項經常被分紅若干個組。下面的示例程序說明了單選鈕的使用。
1. 建立新的基於對話框的MFC應用程序,將工程名設置爲RadioButton。
2. 按圖6.29繪製應用程序的主對話框。其中在Control工具箱中單選鈕對應的圖標是 ,組框控件對應的圖標是 。
3. 單擊Layout菜單下的Tab Order命令,按圖6.30的順序單擊各控件以設置控件的TAB鍵順序(Tab Order)。
4. 確信全部控件的Group屬性都被設置爲假。分別單擊組框「性別」和組框「年齡」,將其Group屬性設置爲真。
圖6. 30 設置控件的Tab Order
以Tab Order爲序,從Group屬性爲真的控件開始(包括該控件),到下一個Group屬性的真的控件結束(不包括該控件),全部的這些控件將組成一個組。對於單選鈕,同一組內同時只能有(也應該有)一個處於被選中的狀態。當其中一個控件被置於選中狀態時,同組的其它單選鈕應該清除其選中狀態。對於由資源編輯器生成的單選鈕控件,在默認狀況由Windows自動處理同組控件之間的互斥關係。
下面咱們簡述一下特定於單選鈕的一些屬性及其含義,這些屬性被列於Styles選項卡內:
Auto: |
在具備Auto屬性的狀況下,當用戶單擊了同一組的某個單選鈕時,其他單選鈕的選中屬性被自動清除。當在一組單選鈕中使用Dialog Data Exchange時,該屬性必須被設置爲True。類型:布爾值 默認值:真 |
Left text: |
將單選鈕的標題文本顯示於圓形標記的左邊。類型:布爾值 默認值:假 |
Push-like: |
使一個複選框、三態複選框或單選項具備相似於下壓按鈕的外觀和行爲。該按鈕在選中時顯示爲凸起,在不被選中時顯示爲凹下(參見圖。類型:布爾值 默認值:假 |
Notify: |
決定在默認狀況下當單選鈕被單擊或雙擊時向父窗口發送通知消息。類型:布爾值 默認值:真 |
圖6. 31 具備Pusk-like樣式的單選鈕
5. 將性別框內的兩個控件的ID按從上到下的順序設置爲IDC_SEX1和IDC_SEX2;將年齡框內的兩個控件的ID按從上到下的順序設置爲IDC_AGE一、IDC_AGE二、IDC_AGE三、IDC_AGE4和IDC_AGE5。
在程序運行時能夠調用CButton的成員函數SetCheck設置單選鈕的選中狀態。該成員函數帶有一個類型爲整型的參數,該參數爲0表示清除選中按鈕的選中狀態,參數爲1表示設置選中按鈕的選中狀態。
上面的代碼將致使年齡組中的第一個按鈕和第五個按鈕在對話框第一次顯示時同時處於選中狀態。這是應該避免的。所以,若是咱們經過代碼改變了單選鈕的選中狀態,必定要記得同時清除同組的其它單選鈕的選中狀態。
對於單個的單選鈕,咱們能夠調用類CButton的成員函數GetCheck,該函數的返回值爲0、1或2,分別表明按鈕處理未選中狀態、選中狀態或中間狀態(對三態複選框而言)。可是,對於對話框中的單選鋸而言,咱們更感興趣於同一組單選鈕中哪個被選中,所以,調用類CWnd的成員函數GetCheckedRadioButton要更爲方便。該成員函數原型以下:
int GetCheckedRadioButton( int nIDFirstButton, int nIDLastButton );
第一個參數nIDFirstButton是同一組中的第一個單選鈕控件的ID,nIDLastButton是同一組中最後一個單選鈕控件的ID。成員函數GetCheckedRadioButton返回指定組中第一個所選中的單選鈕(在正常狀況下僅應當有一個單鈕被選中)的ID,若是沒有按鈕被選中,則返回0。
這裏須要注意的是,成員函數GetCheckedRadioButton被沒有要求兩個參數nIDFirstButton和nIDLastButton所指定的控件必定位於同一組中。
同時,這也說明一點,即便用GetCheck一個一個控件的檢查各單選鈕的選中狀態要安全得多。
下面咱們來完成應用程序RadioButton。
首先,使用ClassWizard重載類CDialog的OnOK成員函數,方法是重載ID爲IDOK的按鈕的BN_CLICKED命令處理函數。由ClassWizard生成的默認重載形式以下:
void CRadioBoxDlg::OnOK()
{
// TODO: 在此添加附加的驗證
CDialog::OnOK();
}
這裏特定的代碼來替代前面的// TODO註釋後獲得以下的程序代碼:
void CRadioBoxDlg::OnOK()
{
// 暫時隱藏主對話框
ShowWindow(SW_HIDE);
UINT nSex=GetCheckedRadioButton(IDC_SEX1,IDC_SEX2); // 得到性別選擇
UINT nAge=GetCheckedRadioButton(IDC_AGE1,IDC_AGE5); // 得到年齡選擇
CString msg="性別: "; // 保存輸出消息字符串
// 根據用戶的選擇生成消息串
// 添加性別信息
switch (nSex)
{
case IDC_SEX1:
msg+="男\n";
break;
case IDC_SEX2:
msg+="女\n";
break;
default:
break;
}
// 添加年齡信息
msg+="年齡: ";
switch (nAge)
{
case IDC_AGE1:
msg+="18 歲如下";
break;
case IDC_AGE2:
msg+="18 - 25 歲";
break;
case IDC_AGE3:
msg+="25 - 35 歲";
break;
case IDC_AGE4:
msg+="35 - 45 歲";
break;
case IDC_AGE5:
msg+="45 歲以上";
break;
default:
break;
}
msg+="\n\n以上數據是否正確?";
// 顯示輸入消息框詢問用戶所輸入的信息是否正確
if(MessageBox(msg,NULL,MB_YESNO|MB_ICONQUESTION)==IDNO)
{
// 當用戶回答「否」時從新顯示對話框以供便用戶能夠更改所做的選擇
ShowWindow(SW_SHOW);
return;
}
// 不然退出應用程序
CDialog::OnOK();
}
以上應用程序的運行結果如圖6.32所示:
按如圖6.32所示進行選擇,單擊肯定彈出如圖6.33所示的對話框。
下一節中咱們將講述複選框的使用。
圖6. 32 單選鈕示例程序的運行結果
圖6. 33 單擊「肯定」以後的確認消息框
6.3.5 複選框
複選框與單選鈕很相象,不一樣之處在於在同一組控件中,一般使用複選框來表明多重選擇,即選項不是互斥的。從外觀上來講,複選框所使用的選中標記是一個方框和方框裏面的小叉,而不是單選鈕所使用的小圓圈和裏面的小點。
對於編程者來講,複選框和單選鈕很是類似。咱們經過SetCheck成員函數來設置某一複選框的選中狀態,經過GetCheck成員函數來獲取某一複選框的選中狀態。通常來講,對於複選框,因爲其選項不是互斥的,咱們通常不經過GetCheckedRadioButton之類的函數來得到處於選中狀態的按鈕。
如下特定於複選框的樣式能夠Properties對話框的Styles屬性頁中進行設置:
Auto: |
對於Auto屬性爲真的複選框,在單擊時將自動在「選中」和「不選中」之間進行切換。若是在一組複選框中使用了Dialog Data Exchange,則必須將該屬性設置爲真。類型:布爾值 默認值:真 |
Tri-state: |
建立三態複選框。除了處於「選中」和「不選中」狀態外,三態複選框還能夠處於變灰狀態。一般,態複選框的變灰狀態表示其選中狀態不肯定。在不少軟件的安裝程序中,變灰每每表示僅選中該組件中的一部分。 |
圖6. 34 工程CheckBox的主對話框的設計
下面的應用程序舉例說明了複選框的使用。
1. 使用默認選項建立一個基於對話框的MFC工程,設置工程名爲CheckBox。
2. 按圖6.34繪製對話框中的各個複選框(在Control工具箱中複選框所對應的圖標爲 ),並按表6.24設置各複選框的樣式和屬性。
表6. 24 工程CheckBox中各控件的屬性設置
控件 |
ID |
標題文本 |
其它 |
複選框 |
IDC_PLACE1 |
在家裏(&H) |
Auto屬性和Tri-state屬性均爲真 |
IDC_PLACE2 |
在公司辦公室(&O) |
||
IDC_PLACE3 |
在學校公共機房(&S) |
||
IDC_OFTEN |
常常 |
Auto屬性爲假,Tri-state屬性爲真 |
|
IDC_SELDOM |
偶爾 |
||
IDC_NEVER |
從不 |
||
組框 |
IDC_STATIC |
使用計算機的場所 |
|
3. 使用下面的代碼替換類CCheckBoxDlg的成員函數OnInitDialog中的// TODO註釋:
((CButton*)GetDlgItem(IDC_OFTEN))->SetCheck(1);
((CButton*)GetDlgItem(IDC_SELDOM))->SetCheck(2);
((CButton*)GetDlgItem(IDC_NEVER))->SetCheck(0);
因爲三個複選框IDC_OFTEN、IDC_SELDOM、IDC_NEVER的Auto屬性值爲假,所以當用戶單擊這三個複選框時其狀態不會發生改變。它們在本示例程序中起了圖例的做用。
4. 在類CCheckBoxDlg中重載類CDialog的成員函數OnOK以下(關於對命令處理成員函數OnOK的重載咱們已經在前一小節中做了講述):
void CCheckBoxDlg::OnOK()
{
// 定義和初始化所用的變量
CString strMsg, // 消息字符串
strMsgA[3]; // 分別對應於三種不一樣時間頻度的消息字符串
int iCount[3]; // 對應於每種時間頻度的狀況計數
// 初始化各變量
iCount[0]=iCount[1]=iCount[2]=0;
strMsgA[0]="從不在";
strMsgA[1]="常常在";
strMsgA[2]="偶爾在";
int i; // 用着循環變量或中間變量
// 檢查各複選框的選中狀態,並根據用戶的選擇生成對應於三種不一樣時間
// 頻度的消息字符串
// 檢查複選框 IDC_PLACE1
i=( (CButton*)GetDlgItem(IDC_PLACE1) )->GetCheck();
if ( (iCount[i]++)==0 )
strMsgA[i]+="家裏";
else
strMsgA[i]+="、家裏";
// 檢查複選框 IDC_PLACE2
i=( (CButton*)GetDlgItem(IDC_PLACE2) )->GetCheck();
if ( (iCount[i]++)==0 )
strMsgA[i]+="公司辦公室";
else
strMsgA[i]+="、公司辦公室";
// 檢查複選框 IDC_PLACE3
i=( (CButton*)GetDlgItem(IDC_PLACE3) )->GetCheck();
if ( (iCount[i]++)==0 )
strMsgA[i]+="學校開放機房";
else
strMsgA[i]+="、學校開放機房";
// 爲了符合漢語的語氣轉折,判斷是否須要在「從不……」分句前添加轉折
// 連詞「但」。若是用戶對三種狀況的選擇都是「從不」,那麼這個「但」
// 字是不該該要的。
if ( !(iCount[1]==0 && iCount[2]==0) )
strMsgA[0]=CString("但")+strMsgA[0];
// 若是用戶對三種狀況的選擇都不屬於某種時間頻度,那麼該時間頻度所對應
// 的消息字符串應該爲空。不然,在該分句的末尾加了字符串「使用計算機,」。
for (i=0;i<3;i++)
{
if ( iCount[i]==0 )
strMsgA[i]="";
else
strMsgA[i]+="使用計算機,";
}
// 生成最終顯示的消息字符串
strMsg=CString("您")+strMsgA[1]+strMsgA[2]+strMsgA[0];
// 處理消息字符串的標點
strMsg=strMsg.Left( strMsg.GetLength()-2 )+"。";
// 彈出消息框詢問用戶所輸入的數據是否正確
if ( MessageBox( strMsg,"確認",MB_YESNO|MB_ICONQUESTION )==IDNO )
{
// 若是用戶選擇「否」,則從新輸入數據
return;
}
// 調用基類的 OnOK 成員函數,並關閉對話框
CDialog::OnOK();
}
上面的代碼都加上了詳細的註釋,並且所用的函數也都是咱們所熟知的,這裏咱們就再也不重複講述了。
到目前爲止,咱們已經講述完了Windows標準控件中的按鈕類控件:下壓按鈕、組框、單選鈕和複選框。此外,咱們還介紹了位圖按鈕,通常來講咱們並不把它納入Windows標準控件中,而認爲它是由MFC提供的少數幾個控件之一。而位圖按鈕事實上是具備Owner draw屬性的自繪製下壓按鈕,MFC類CBitmapButton封裝了其內部實現的複雜性,而以簡單的接口提供給程序員。
做爲本節的結束,咱們來討論同樣如何改變按鈕標題文本的字體屬性。在Developer Studio的資源編輯器中,咱們能夠統一的修改同一對話框中全部按鈕的標題文本的字體屬性。方法是打開對話框自己的屬性(Properties)對話框,在General選項卡中單擊Font按鈕,從彈出Select Dialog Font對話框中選擇對話框所用的字體。
圖6. 35 設置對話框的字體屬性
經過上面的方法設置的字體對整個對話框中全部的控件都有效。若是須要設置單個控件的字體,咱們必須經過編寫代碼來實現。下面的示例程序ButtonFont演示瞭如何單獨更改某個控件的字體。
圖6. 36 示例程序ButtonFont的主對話框
按圖6.36繪製應用程序主對話框中全部的各按鈕控件。其中標籤爲「我愛你」的按鈕ID爲IDC_LOVE,標籤爲「改變字體」的按鈕ID爲IDC_CHANGEFONT。
在類CButtonFontDlg中添加類型爲CFont的私有成員變量m_Font。
爲按鈕IDC_CHANGEFONT的BN_CLICKED事件編寫下面的處理函數:
void CButtonFontDlg::OnChangefont()
{
// 獲取按鈕 IDC_LOVE 的當前所用字體
LOGFONT lf;
GetDlgItem(IDC_LOVE)->GetFont()->GetLogFont(&lf);
// 使用按鈕的當前字體初始化字體對話框
CFontDialog dlgFontDlg(&lf);
// 顯示字體選擇對話框
if (dlgFontDlg.DoModal()==IDOK)
{
// 若是用戶在字體選擇對話框中單擊了「肯定」按鈕,
// 則使用
dlgFontDlg.GetCurrentFont(&lf);
m_Font.DeleteObject();
m_Font.CreateFontIndirect(&lf);
GetDlgItem(IDC_LOVE)->SetFont(&m_Font);
}
}
編譯並運行程序ButtonFont,單擊「改變字體」按鈕,在隨後彈出的字體選擇對話框中設置字體並單擊「肯定」按鈕。對話框的顯示可能如圖6.37所示。
圖6. 37 示例程序ButtonFont的運行結果
按下面的方式編寫命令處理函數OnChangefont不會獲得正確的結果:
之因此會出現這種狀況與用來設置字體的CFont變量的存活期有關。
第四節 靜態控件
靜態控件通常用來顯示靜態的文本、圖標、位圖或圖元文件,它不能用來接受用戶的輸入,也不多用來顯示輸出,而在更多的狀況下用做那些沒有固定的標題文本屬性的控件(如文本編輯控件、列表框等)的標籤,或者用來進行控件的分組,或者用來顯示一些提示性文本。
MFC類CStatic封裝了標準的Windows靜態控件。下面的示例程序StaticDemo演示了靜態控件的使用。
1. 使用AppWizard建立一個基於對話框的MFC應用程序,設置其工程名爲StaticDemo。
2. 按如圖6.38繪製主對話框中的控件。其中標籤爲「靜態控件」的靜態控件ID爲IDC_STATIC。須要注意是的,由資源管理器添加的靜態控件在默認狀況下其ID均爲IDC_STATIC,所以,若是須要在程序中區分和操縱各個不一樣的靜態控件,通常狀況下咱們都須要更改新添加的靜態控件的ID值。這裏咱們將靜態控件的ID值設置爲IDC_STATICDEMO。
圖6. 38 示例程序StaticDemo的主對話框
如下屬性和樣式沒有在本章前面的內容中涉及,它們能夠適用於靜態控件。能夠經過靜態控件的Properties屬性對話框的Styles選項卡進行這些屬性或樣式的設置。
Align text: |
決定靜態文本控件中文本的橫向對齊方式。可供選擇的值爲Left (向左對齊)、Center (居中對齊)和Right (向右對齊)。默認值:Left |
Center Vertically: |
在靜態文本控件中將文本進行垂直居中。類型:布爾值 默認值:假 |
No prefix: |
不將控件文本中的「&」符解釋爲助記字符。在默認狀況下,「&」符號在顯示時會被去掉,取而代之的是緊接「&」符以後的字符被以加下劃線的格式進行顯示。咱們早在前面說過,經過雙寫「&」符能夠在控件文本中顯示出實際的「&」符,可是,對於一些特殊的場合,如使用靜態文本控件來顯示文件名的時候,將No prefix屬性設置爲「真」要更方便。 |
No wrap: |
以左對齊的方式來顯示文本,而且不進行文本的自動回行。超出控件右邊界的文本將被裁去。須要注意的是,這時即便使用轉義字符序列"\n"也不能夠強制控件文本進行換行。類型:布爾值 默認值:假 |
Simple: |
禁止設置Text Align屬性和No Wrap樣式。在該屬性爲真的狀況下,靜態文本控件中的文本不會被自動回行,也不會被剪裁。類型:布爾值 默認值:假 |
Notify: |
決定控件在被單擊時是否通知父窗口。類型:布爾值 默認值:假 |
Sunken: |
使用靜態文本控件看上去有下凹的感受。類型:布爾值 默認值:假 |
Border: |
爲文本控件建立邊框。類型:布爾值 默認值:假 |
4. 靜態控件通常不用於輸入,可是若是它的Notify屬性設置爲真,則當用戶單擊靜態控件時,靜態控件將向父窗口發送通知消息。可是,咱們不可使用前面所講述的方法(即便用ClassWizard或從上下文菜單中選擇Events命令)來爲靜態控件添加消息處理函數。而要以手動的方式來實現這一點。下面咱們結合示例StaticDemo來講明如何爲靜態控件添加單擊事件的命令處理程序。在進行下面的步驟以前,請確認靜態控件IDC_STATICDEMO的Notify屬性值爲真。
在類CStaticDemoDlg的定義處添加下面的命令處理函數聲明:
afx_msg void OnStaticDemo();
最好把成員函數OnStaticDemo的聲明與其它命令處理函數的聲明放在一塊兒,但不要放到//{{AFX_MSG和//}}AFX_MSG之間。
而後,打開類CStaticDemoDlg的實現文件StaticDemoDlg.cpp,在宏
BEGIN_MESSAGE_MAP(CStaticDemoDlg, CDialog)
和宏
BEGIN_MESSAGE_MAP
之間添加以下的消息映射入口:
ON_BN_CLICKED(IDC_STATICDEMO, OnStaticDemo)
一樣,不要把手動添加的消息映射入口項放到註釋//{{AFX_MSG_MAP和//}}AFX_MSG_MAP之間。
手動添加成員函數OnStaticDemo或OnDoubleclickedStaticDemo的實現代碼:
void CStaticDemoDlg::OnStaticDemo()
{
MessageBox("您剛纔單擊了「靜態控件」!");
}
編譯上面的示例程序,單擊「靜態控件」,命令處理函數OnStaticDemo將被調用,從而彈出相應的消息框。
下面咱們來看一下若是在靜態控件中使用圖標和位圖。
圖6. 39 使用圖標代替靜態控件中的文本
首先介紹使用圖標代替文本的例子,方法以下:
假設對話框類爲CStaticDemoDlg,所需使用圖標的靜態控件ID爲IDC_STATICDEMO,相應的圖標的ID爲IDR_MAINFRAME,則可用下面的代碼代替類CStaticDemoDlg的成員函數OnInitDialog中的// TODO註釋:
// 得到指向靜態控件的指針
CStatic *pStaticDemo=(CStatic*)GetDlgItem(IDC_STATICDEMO);
// 加載圖標
HICON hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
// 設置靜態控件的樣式以使得可使用圖標,並使圖標顯示時居中
pStaticDemo->ModifyStyle(0xF,SS_ICON|SS_CENTERIMAGE);
// 設置靜態控件圖標
pStaticDemo->SetIcon(hIcon);
運行該程序,顯示如圖6.39。
接着咱們來看如何使用位圖代替文本,方法以下:
假設所用位圖的資源ID爲IDB_STATICDEMO,其他設置如上。用如下代碼來代替成員函數OnInitDialog中的// TODO註釋:
// 得到指向靜態控件的指針
CStatic *pStaticDemo=(CStatic*)GetDlgItem(IDC_STATICDEMO);
// 得到位圖句柄
HBITMAP hBitmap=::LoadBitmap(AfxGetApp()->m_hInstance,
MAKEINTRESOURCE(IDB_STATICDEMO));
// 設置靜態控件的樣式以使得可使用位圖,並使位圖在顯示時居中
pStaticDemo->ModifyStyle(0xF,SS_BITMAP|SS_CENTERIMAGE);
// 設置靜態控件顯示時使用的位圖
pStaticDemo->SetBitmap(hBitmap);
編譯並運行該程序,對話框顯示如圖6.40所示。
圖6. 40 使用位圖代替文本的靜態控件
第五節 文本編輯控件
靜態文本控件只能用來顯示文本,而不能夠用來輸入文本。若是須要提供輸入文本的功能應該使用文本編輯控件。文本編輯控件在Control工具箱中對應的圖標爲 。對於文本編輯控件,除了咱們在前面所涉及的一些外,還能夠設置如下的一些屬性樣式:
Align text: |
決定當Multiline屬性爲真時文本的對齊方式。默認值爲:Left |
Multi-line: |
建立一個多行文本編輯控件。當一個多行文本編輯控件具備輸入焦點時,若是用戶按下了ENTER鍵,以默認狀況下的行爲是選擇對話框中的默認命令按鈕,而不是向文本編輯控件中插入新行。將AutoHScroll屬性或Want return屬性設置爲真能夠將用戶按下的ENTER鍵解釋爲插入新行,而不是選擇默認命令按鈕。 在選擇了AutoHScroll屬性時,若是插入點超過了控件的右邊界,多行文本編輯控件自動進行水平滾動。用戶可使用ENTER鍵來開始新行。 若是沒有選擇AutoHScroll屬性,多行文本編輯控件將視須要將文本進行自動折行。而僅當Want return屬性爲真時,用戶纔可使用ENTER鍵來開始新行。 多行文本編輯控件也能夠擁有本身的滾動條。具備滾動條的編輯控件處理本身的滾動條消息,而不具備滾動條的編輯控件也能夠由父窗口發送的滾動條消息。 類型:布爾值 默認值:假 |
Number: |
用戶不能輸入非數字字符。類型:布爾值 默認值:假 |
Horizontal scroll: |
爲多行控件提供水平滾動條。類型:布爾值 默認值:假 |
Auto HScroll: |
當用戶輸入的字符超過了編輯框的右邊界時自動水平向右滾動文本。類型:布爾值 默認值:真 |
Vertical scroll: |
爲多行控件提供垂直滾動條。類型:布爾值 默認值:假 |
Auto VScroll: |
在多行控件中,當用戶在最後一行按下ENTER鍵時自動向上滾動文本 |
Password: |
當用戶鍵入時將全部字符顯示爲星號(*)。該屬性對於多行控件不可用。類型:布爾值 默認值:假 |
No hide selection: |
改變當編輯框失去和從新得到焦點時文本的顯示方式。若是該屬性爲真,在編輯框中選中的文本在任什麼時候候都顯示爲選中狀態(即反白狀態)。類型:布爾值 默認值:假 |
OEM convert: |
將鍵入的文本從Windows字符集轉換爲OEM字符集,再轉換回Windows字符集。該操做確認應用程序在調用AnsiToOem函數將編輯框中的字符串轉換爲OEM字符串時進行正確的字符轉換,所以該樣式對於包括文件名的編輯控件特別有用。類型:布爾值 默認值:假 |
Want return: |
指定當用戶在多行編輯控件中按下ENTER鍵時插入一個回車符,不然用戶按下ENTER將被解釋爲選擇了對話框中的默認命令按鈕。該樣式對於單行編輯框控件沒有任何影響。類型:布爾值 默認值:假 |
Border: |
在編輯框邊緣建立邊框。類型:布爾值 默認值:真 |
Uppercase: |
將用戶在編輯框中輸入的字符轉換爲大寫。類型:布爾值 默認值:假 |
Lowercase: |
將用戶在編輯框中輸入的字符轉換爲小寫。 類型:布爾值 默認值:假 |
Read-only: |
防止用戶編輯和更改編輯框中的文本。類型:布爾值 默認值:假 |
相比咱們在前面所講述的幾個類CButton、CBitmapButton和CStatic而言,封裝標準編輯控件的MFC類CEdit要複雜得多。表給出了在類CEdit中定義的成員函數:
表6. 25 類CEdit中定義的成員函數
成員函數 |
描述 |
CEdit |
構造CEdit控件對象 |
Create |
建立Windows編輯控件,並將其與CEdit對象相關聯 |
GetSel |
得到編輯控件中當前選擇的開始和結束字符的位置 |
ReplaceSel |
使用特定的文原本替換編輯控件中的當前選擇 |
SetSel |
設置編輯控件中所選定的字符範圍 |
Clear |
刪除編輯控件中當前選定的字符 |
Copy |
使用CF_TEXT格式將編輯控件中當前選定的文本複製到剪貼板 |
Cut |
刪除當前選定的字符,並將所刪除的字符複製到剪貼板 |
Paste |
將剪貼板中格式爲CF_TEXT的數據(若是有的話)插入到編輯框中的當前位置。 |
Undo |
撤銷最後一次編輯操做 |
CanUndo |
決定編輯控件的操做是否能夠被撤銷 |
EmptyUndoBuffer |
重置編輯控件的undo標誌 |
GetModify |
判斷編輯控件中的內容是否被修改過 |
SetModify |
設置或清除編輯控件中的修改標誌 |
SetReadOnly |
設置編輯控件的只讀狀態 |
GetPasswordChar |
當用戶輸入文本時得到編輯控件中顯示的密碼字符 |
SetPasswordChar |
設置或移去當用戶輸入文本時編輯控件中顯示的密碼字符 |
GetFirstVisibleLine |
得到編輯控件中最上面的可見行 |
LineLength |
得到編輯控件中一行的長度 |
LineScroll |
滾動多行編輯控件中的文本 |
LineFromChar |
得到包含指定索引字符的行的行號 |
GetRect |
得到編輯控件的格式矩形 |
LimitText |
限制用戶能夠在編輯控件中輸入的文本的長度 |
GetLineCount |
得到多行編輯控件中行的數目 |
GetLine |
得到編輯控件中的一行文本 |
LineIndex |
得到多行編輯控件中一行的字符索引 |
FmtLines |
在多行編輯控件中設置是否包含軟換行符的開關 |
續表6.25
成員函數 |
描述 |
SetTabStops |
在多行編輯控件中設置製表位 |
SetRect |
設置多行文本編輯控件的格式矩形,並更新控件 |
SetRectNP |
設置多行文本編輯控件的格式矩形,但不重繪控件窗口 |
GetHandle |
得到爲多行編輯控件分配的內存的句柄 |
SetHandle |
設置供多行編輯控件使用的本地內存句柄 |
GetMargins |
得到當前CEdit對象的左右頁邊距 |
SetMargins |
設置當前CEdit對象的左右頁邊距 |
GetLimitText |
得到當前CEdit對象能夠包括的最大文本量 |
SetLimitText |
設置當前CEdit對象能夠包括的最大文本量 |
CharFromPos |
得到最接近於指定位圖的行和字符的索引 |
PosFromChar |
得到指定字符索引的左上角的座標 |
上面的成員函數涵蓋了編輯控件在使用中的不少方面,能夠知足咱們在不少狀況下的絕大部分須要。這裏要注意的是,一些CWnd中定義的成員函數也是很重要的,好比說咱們經常使用CWnd的成員函數GetWindowText和SetWindowText來獲取和設置編輯控件的文本,使用成員函數GetFont和SetFont來獲取和設置編輯控件顯示文本時所使用的字體。
編輯控件能夠向父窗口發送的通知消息也要比前面講述的幾種控件多。這些消息有:
|
|
|
ON_EN_CHANGE:ON_EN_ERRSPACE: |
編輯控件不能按選定須要分配足夠的內存 |
|
ON_EN_HSCROLL: |
用戶單擊了編輯控件中的水平滾動條。父窗口在屏幕更新前得到此消息 |
|
ON_EN_KILLFOCUS: |
編輯控件失去輸入焦點 |
|
ON_EN_MAXTEXT: |
當前插入內容超過了編輯控件中的指定的字符數,該插入內容已被裁剪。若是控件沒有設置ES_AUTOHSCROLL樣式,那麼在插入的字符超出了編輯控件的寬度也發送該通知消息。一樣,若是控件沒有指定ES_AUTOVSCROLL樣式,該通知也以插入操做致使總行數超過編輯控件的高度時發送。 |
|
ON_EN_SETFOCUS: |
編輯按鈕得到輸入焦點 |
|
ON_EN_UPDATE: |
控件已對文本做了格式化,但還沒有顯示文本。一般能夠處理該消息以決定是否須要對窗口的大小做改變等。 |
|
ON_EN_VSCROLL: |
用戶單擊了編輯控件的垂直滾動條。父窗口在屏幕更新前收到該消息。 |
|
示例程序EditDemo演示了編輯控件的通常使用方法。按以下步驟建立該工程:
1. 使用AppWizard建立基於對話框的工程EditDemo。
2. 向工程中添加菜單資源IDR_MAINMENU,該菜單資源包括兩個頂層菜單項「文件(&F)」和「編輯(&E)」,「文件(&F)」下包括如圖6.41所示的菜單命令。各菜單命令(不包括具備Separator樣式的菜單項)的資源ID依次爲ID_FILE_NEW和ID_FILE_EXIT。「編輯(&E)」菜單下包括如圖6.42所示菜單命令。各菜單命令的資源ID依次爲ID_EDIT_UNDO、ID_EDIT_CUT、ID_EDIT_COPY、ID_EDIT_PASTE、ID_EDIT_DEL、ID_EDIT_SELECTALL和ID_EDIT_SETFONT。
圖6. 41 「文件」菜單下的菜單命令
圖6. 42 「編輯」菜單下的菜單命令
3. 按圖6.43在應用程序的主對話框上繪製編輯框(對應於Control工具箱中的圖標爲 ),設置其ID爲IDC_EDIT,並將其Multiline屬性、Auto VScroll屬性和Want return屬性設置爲真,同時將Auto HScroll屬性設置爲假。這裏,編輯框IDC_EDIT在大小和位置並不重要,咱們將在程序中對其進行調整。
4. 刪除原有的「肯定」按鈕和「取消」按鈕。接着打開對話框自己的屬性對話框,從Menu下拉列表框中選擇IDR_MAINMENU。
圖6. 43 設置主對話框的Menu屬性
5. 在資源管理器中打開菜單資源IDR_MAINMENU,如圖6.44所示。在任一菜單項上單擊鼠標右鍵,選擇命令ClassWizard。這時ClassWizard將彈出如圖6.45所示的對話框,單擊Cancel。在Object IDs處選擇ID_FILE_EXIT,在Messages處選擇COMMAND,單擊And function按鈕並接受默認的處理函數名OnFileExit,在函數OnFileExit中調用類CDialog的成員函數OnCancel,以下面的代碼所示:
void CEditDemoDlg::OnFileExit()
{
// 調用基類成員函數 OnCancel 終止對話框
OnCancel();
}
按一樣的方法爲ID_FILE_NEW的COMMAND命令添加處理函數OnFileNew以下:
void CEditDemoDlg::OnFileNew()
{
// 將編輯控件中的文本初始化爲零,
// 並清除其撤消緩衝區。
CEdit *pEdit=(CEdit*)GetDlgItem(IDC_EDIT);
pEdit->SetWindowText("");
pEdit->EmptyUndoBuffer();
}}
圖6. 44 在Developer Studio的資源編輯器中打開菜單資源IDR_MAINMENU
爲ID_EDIT_UNDO的COMMAND命令添加處理函數OnEditUndo以下:
void CEditDemoDlg::OnEditUndo()
{
// 直接調用類 CEdit 的成員函數 Undo
CEdit *pEdit=(CEdit*)GetDlgItem(IDC_EDIT);
pEdit->Undo();
}
圖6. 45 詢問是否將菜單IDR_MAINMENU與某一視類相關聯
爲ID_EDIT_CUT的COMMAND命令添加處理函數OnEditCut以下:
void CEditDemoDlg::OnEditCut()
{
// 直接調用類 CEdit 的成員函數 Cut
((CEdit*)GetDlgItem(IDC_EDIT))->Cut();
}
爲ID_EDIT_COPY的COMMAND命令添加處理函數OnEditCopy以下:
void CEditDemoDlg::OnEditCopy()
{
// 直接調用類 CEdit 的成員函數 Copy
((CEdit*)GetDlgItem(IDC_EDIT))->Copy();
}
爲ID_EDIT_PASTE的COMMAND命令添加處理函數OnEditPaste以下:
void CEditDemoDlg::OnEditPaste()
{
// 直接調用類 CEdit 的成員函數 Paste
((CEdit*)GetDlgItem(IDC_EDIT))->Paste();
}
爲ID_EDIT_DEL的COMMAND命令添加處理函數OnEditDel以下:
void CEditDemoDlg::OnEditDel()
{
// 直接調用類 CEdit 的成員函數 Clear
((CEdit*)GetDlgItem(IDC_EDIT))->Clear();
}
爲ID_EDIT_SELECT的COMMAND命令添加處理函數OnEditSelectall以下:
void CEditDemoDlg::OnEditSelectall()
{
int nStart,nEnd;
// 設置選定字符的開始
nStart=0;
// 設置選定字符的結尾。函數 GetWindowTextLength 返回編輯控件中文本的長度
nEnd=((CEdit*)GetDlgItem(IDC_EDIT))->GetWindowTextLength();
// 以 nStart 和 nEnd 爲參數調用類 CEdit 的成員函數 SetSel
((CEdit*)GetDlgItem(IDC_EDIT))->SetSel(nStart,nSel);
}
爲ID_EDIT_SETFONT的COMMAND命令添加處理函數OnEditSetfont以下:
void CEditDemoDlg::OnEditSetfont()
{
LOGFONT lf;
static CFont font;
// 得到編輯框原來使用的字體信息,並使用該信息初始化字體對話框
CEdit *pEdit=(CEdit*)GetDlgItem(IDC_EDIT);
pEdit->GetFont()->GetLogFont(&lf);
CFontDialog dlg(&lf);
// 彈出字體對話框以供用戶選擇新的字體,
// 並在用戶確認的狀況下更改編輯控件所使用的字體。
if (dlg.DoModal()==IDOK)
{
dlg.GetCurrentFont(&lf);
font.DeleteObject();
font.CreateFontIndirect(&lf);
pEdit->SetFont(&font);
}
}
在成員函數OnEditSetfont中所使用的方法和技巧已在第三節的末尾講述如何爲按鈕控件設置字體時進行了介紹。所以對於函數OnEditSetfont咱們不進行詳細的註解。
6. 考慮下面的狀況:若是當前沒有可供撤消的操做,「編輯」菜單下的「撤消」應該處於不可用(變灰)狀態;一樣的,若是當前編輯控件中沒有選定任何文本,那麼「剪貼」、「複製」以及「刪除」命令也應該不可用;若是當前剪貼板中沒有任何文本數據,「粘貼」命令應該不可用。咱們經過爲消息WM_INITMENUPOPUP添加消息處理函數來設置各菜單命令的可用狀態。該消息在用戶單擊某菜單以後在菜單項彈出以前發送。
對於類CEditDemoDlg,咱們不能使用ClassWizard來爲消息WM_INITMENUPOPUP添加消息處理函數,但事實上,對話框也能夠接收到消息WM_INITMENUPOPUP。這裏,咱們能夠手動來添加相應的消息映射項。
第一步是在類CEditDemoDlg的定義中添加消息處理函數
afx_msg void OnInitMenuPopup( CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu );
能夠把該處理函數的聲明添加到由ClassWizard生成的消息處理函數的後面。由ClassWizard生成的消息處理函數位於兩行註釋標記//{{AFX_MSG和//}}AFX_MSG之間。同咱們在此以前強調過的同樣,不要將OnInitMenuPopup的聲明添加到兩行註釋之間。之後若是再遇到與此類似的狀況,咱們將再也不強調。
接着添加相應的消息映射入口,在類CEditDemoDlg的實現文件EditEemoDlg.cpp中找到宏BEGIN_MESSAGE_MAP(CEditDemoDlg, CDialog),在它以後,宏END_MESSAGE_MAP以前添加下面的宏代碼:
ON_WM_INITMENUPOPUP()
咱們仍應將上面的代碼添加到註釋標記//{{AFX_MSG_MAP和//}}AFX_MSG_MAP以外。一樣的,之後若是再遇到這種狀況咱們將再也不強調。
最後添加函數OnInitMenuPopup的定義:
void CEditDemoDlg::OnInitMenuPopup( CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu )
{
CEdit *pEdit=(CEdit*)GetDlgItem(IDC_EDIT);
// 當用戶單擊的是窗口的控制菜單時 bSysMenu 參數爲真,不然爲假
if (!bSysMenu)
{
// 檢查編輯控件是否有可撤消的操做
if (pEdit->CanUndo())
{
pPopupMenu->EnableMenuItem(ID_EDIT_UNDO,MF_ENABLED);
}
else
{
pPopupMenu->EnableMenuItem(ID_EDIT_UNDO,MF_GRAYED);
}
// 檢查編輯控件中是否有選定的文本
int nStart,nEnd;
pEdit->GetSel(nStart,nEnd);
if (nStart==nEnd)
{
pPopupMenu->EnableMenuItem(ID_EDIT_CUT,MF_GRAYED);
pPopupMenu->EnableMenuItem(ID_EDIT_COPY,MF_GRAYED);
pPopupMenu->EnableMenuItem(ID_EDIT_DEL,MF_GRAYED);
}
else
{
pPopupMenu->EnableMenuItem(ID_EDIT_CUT,MF_ENABLED);
pPopupMenu->EnableMenuItem(ID_EDIT_COPY,MF_ENABLED);
pPopupMenu->EnableMenuItem(ID_EDIT_DEL,MF_ENABLED);
}
// 檢查剪貼板中是否有文本格式的數據可供粘貼
// 該過程經過調用 Win32 API 函數 IsClipboardFormatAvailable 來實現
if (IsClipboardFormatAvailable(CF_TEXT))
{
pPopupMenu->EnableMenuItem(ID_EDIT_PASTE,MF_ENABLED);
}
else
{
pPopupMenu->EnableMenuItem(ID_EDIT_PASTE,MF_GRAYED);
}
}
}
7. 最後咱們但願一點,就是說用戶能夠改變對話框的大小,並且當用戶改變對話框的大小時,編輯框自動的改變其大小以適應父窗口大小的變化。方法是爲WM_SIZE添加消息處理函數。在進行這一步操做以前,打開對話框的Dialog Properties對話框,在Styles選項卡中將其Border屬性設置爲Resizing (便可以改變大小),同時將Maximize box屬性值設置爲真。而後,使用ClassWizard爲消息WM_SIZE添加消息處理函數OnSize,其定義以下:
void CEditDemoDlg::OnSize(UINT nType, int cx, int cy)
{
// 調用基類的 OnSize 成員函數
CDialog::OnSize(nType, cx, cy);
CRect rect;
// 得到父窗口的客戶區矩形
GetClientRect(&rect);
CEdit *pEdit=(CEdit*)GetDlgItem(IDC_EDIT);
if (pEdit)
{
// 改變編輯控件的大小以適應父窗口大小的改變
pEdit->MoveWindow(&rect);
}
}
因爲OnSize會在對話框第一次顯示時被調用,所以使用if語句檢查pEdit是否爲NULL是必要的。出於一樣的目的,咱們還須要使用下面的代碼來替換成員函數OnInitDialog中的// TODO註釋:
CRect rect;
GetClientRect(&rect);
CEdit *pEdit=(CEdit*)GetDlgItem(IDC_EDIT);
if (pEdit)
{
pEdit->MoveWindow(&rect);
}
它在對話框第一次顯示時完成與上面的OnSize成員函數一樣的操做。以保證在第一次顯示對話框時編輯框控件以正確的大小進行顯示。
編譯並運行上面的程序(如圖6.46),並測試其各項功能是否正常。
圖6. 46 示例程序EditDemo的運行結果
第六節 列表框控件
列表框控件一般用來列出一系列可供用戶從中進行選擇的項,這些項通常來講都在字符串的形式給出,但也能夠採用其它的形式,如圖形等。列表框能夠只容許單一選擇,也就是說用戶同時只能選擇全部列表項中的一項;除此以外,列表框也能夠是多項選擇的,用戶能夠在多項選擇列表框中選擇多於一項的列表項。當用戶選擇了某項時,該項被反白顯示,同時列表框向父窗口發送一條通知消息。MFC類CListBox封裝了Windows標準列表框控件,其成員函數(參見表6.26)提供了對標準列表框的絕大多數操做。
表6. 26 在類CListBox中定義的成員函數
成員函數 |
描述 |
AddString |
向列表框中添加字符串 |
CharToItem |
爲不包含字符串的自繪製列表框提供對WM_CHAR的定製處理 |
CListBox |
構造一個CListBox對象 |
CompareItem |
由框架調用以決定新添加的項在有序自繪製列表框中的位置 |
Create |
建立一個Windows列表框控件,並將它與CListBox對象相關聯 |
DeleteItem |
當用戶從自繪製列表框中刪除一項時由框架調用 |
DeleteString |
從列表框中刪除字符串 |
Dir |
從當前目錄向列表框中添加文件名 |
DrawItem |
當自繪列表框的可視部分改變時由框架調用 |
FindString |
在列表框中查詢指定的字符串 |
FindStringExact |
查找與指定字符串相匹配的第一個列表框字符串 |
GetAnchorIndex |
返回列表框中當前「錨點」項的基於零的索引 |
續表6.26
成員函數 |
描述 |
GetCaretIndex |
在多重選擇列表框中得到當前擁有焦點矩形的項的索引 |
GetCount |
返回列表框中字符串的數目 |
GetCurSel |
返回列表框中當前選擇字符串的基於零的索引值 |
GetHorizontalExtent |
以象素爲單位返回列表框橫向可滾動的寬度 |
GetItemData |
返回下列表框項相關聯的32位值 |
GetItemDataPtr |
返回指向列表框項的指針 |
GetItemHeight |
決定列表框中項的高度 |
GetLocale |
得到列表框使用的區域標識符 |
GetSel |
返回列表框項的選定狀態 |
GetSelItems |
返回當前選定字符串的索引 |
GetSelCount |
在多重選擇列表框中得到當前選定字符串的數目 |
GetText |
拷貝列表框項到緩衝區 |
GetTextLen |
以字節爲單位返回列表框項的長度 |
GetTopIndex |
返回列表框中第一個可視項的索引 |
InitStorage |
爲列表框項和字符串預先分配內存 |
InsertString |
在列表框中的指定位置插入一個字符串 |
ItemFromPoint |
返回與指定點最接近的列表框項的索引 |
MeasureItem |
當自繪列表框建立時由框架調用以得到列表框的尺寸 |
ResetContent |
從列表框中清除全部的項 |
SelectString |
從單項選擇列表框中查找並選定一個字符串 |
SelItemRange |
在多重選擇列表框中選中某一範圍的字符串或清除某一範圍的字符串的選定狀態 |
SetAnchorIndex |
在多重選擇列表框的設置擴展選定的起點(「錨點」項) |
SetCaretIndex |
在多重選擇列表框中設置當前擁有焦點矩形的項的索引 |
SetColumnWidth |
設置多列列表框的列寬 |
SetCurSel |
在列表框中選定一字符串 |
SetHorizontalExtent |
以象素爲單位設置列表框橫向可滾動的寬度 |
SetItemHeight |
設置列表框中項的高度 |
SetItemRect |
返回列表框項當前顯示的邊界矩形 |
SetLocale |
爲列表框指定區域標識符 |
續表6.26
成員函數 |
描述 |
SetSel |
在多重選擇列表框中選定一列表框項或清除某一列表框項的選定狀態 |
SetTabStops |
設置列表框的製表位 |
SetTopIndex |
設置列表框中第一個可視項的基於零的索引 |
VKeyToItem |
爲具備LBS_WANTKEYBOARDINPUT樣式的列表框提供定製的WM_KEYDOWN消息處理 |
如下是列表框可能向父窗口發送的通知消息及其說明:
ON_LBN_DLBCLK: |
用戶雙擊了列表框中的字符串。僅當列表框具備LBS_NOTIFY樣式時會發送該通知消息 |
ON_LBN_ERRSPACE: |
列表框不能按須要分配足夠的內存 |
ON_LBN_KILLFOCUS: |
列表框失去輸入焦點 |
ON_LBN_SELCANCEL: |
列表框中的當前選擇被取消。僅當列表框具備LBS_NOTIFY樣式時纔會發送該通知消息 |
ON_LBN_SELCHANGE: |
列表框中的選擇將被更新。須要注意的是,當使用成員函數CListBox::SetCurSel時不會發送該通知消息,同時,該消息也僅當列表框具備LBS_NOTIFY樣式纔會發送。對於多重選擇列表框,當用戶按下光標鍵時,即便所選擇的內容沒有改變,也會發送LBN_SELCHANGE通知消息。 |
ON_LBN_SETFOCUS: |
列表框得到輸入焦點 |
ON_WM_CHARTOITEM: |
不包括字符串的列表框收到WM_CHAR消息 |
ON_WM_VKEYTOITEM: |
具備LBS_WANTKEYBOARDINPUT樣式的列表框接收到WM_KEYDOWN消息 |
在資源編輯器中,對應於列表框的Control工具箱圖標爲 。在繪製列表框的同時能夠在Properties屬性對話框中指定其屬性。除了在前面幾節中所講述的之外,咱們還能夠爲列表框設置如下的屬性,這些屬性能夠在Styles選項卡中設置。
Selection: |
決定列表框的選擇方式。能夠設置的值以下: Single:用戶同時只能選擇列表框中的一項 Multiple:用戶能夠同時選擇多於一個的列表框項,但不能夠從開始項擴展選定內容。在鼠標單擊時可使用SHIFT鍵和CTRL鍵選定和取消選定,同時選定項不必定須要連續。單擊或雙擊未選定項時將選定該項;單擊或雙擊已選定項時將取消對該項的選定。 Extended:用戶能夠經過拖動來擴展選定內容。用戶能夠鼠標和SHIFT鍵和CTRL鍵進行選定或取消選定,選擇成組的項或不連續的項。 默認值爲Single。 |
Owner draw: |
控制列表框的自繪特性。能夠設置的值以下: No:關閉自繪製樣式,列表框中包含的內容爲字符串。 Fixed:指定列表框的全部者負責繪製其內容,而且列表框中的項具備相同的高度。 Variable:指定列表框的全部者負責繪製其內容,而且列表框的項具備不一樣的高度。 當列表框建立時CWnd::OnMeasureItem將被調用;當列表框的可視部分改變時CWnd::OnDrawItem將被調用。 默認值爲No。 |
Has strings: |
指定自繪製列表框包括由字符串組成的項。列表框爲字符串維護內存和指針,所以應用程序可使用LB_GETTEXT消息來得到特定項的文本。在默認狀況下,除了自繪製按鈕之外,全部的列表框都具備該項屬性。由應用程序建立的自繪製列表框能夠具備或不具備該樣式。 該樣式僅當自繪製屬性被設置爲Fixed或Variable時可用。若是自繪製屬性被設置爲No,列表框在默認狀況下包括字符串。 類型:布爾值 默認值爲假 |
Sort: |
以字母爲序對列表框內容進行排序。 類型:布爾值 默認值爲真 |
Notify: |
若是列表項被單擊或雙擊時通知父窗口。 類型:布爾值 默認值爲真 |
Multi-colume: |
指定多列列表框,多列列表框能夠在水平方向上進行滾動。消息LB_SETCOLUMNWIDTH用來設置列寬。 類型:布爾值 默認值爲假 |
Horizontal scroll: |
建立具備水平滾動條的列表框。類型:布爾值 默認值爲假 |
Vertical scroll: |
建立具備垂直滾動條的列表框。類型:布爾值 默認值爲真 |
No redraw: |
指定當發生改變時列表框外觀不進行更新。能夠經過發送WM_SETREDRAW消息或調用CWnd::SetRedraw函數改變該屬性。 類型:布爾值 默認值爲假 |
Use tabstops: |
容許列表框在繪製字符串辨認和擴展製表符。默認的製表位爲32個對話框單位(DLU)。類型:布爾值 默認值爲假 |
Want key input: |
指定當用戶有按鍵動做而且列表框具備輸入焦點時列表框的全部者收到WM_VKEYTOITEM和WM_CHARTOITEM消息,以容許應用程序在使用鍵盤輸入時進行特定的處理。若是列表框具備了Has Strings樣式,列表框將接收到WM_VKEYTOITEM消息;若是列表框不具備WM_CHARTOITEM消息,則列表框將接收到WM_CHARTOITEM消息。 類型:布爾值 默認值爲假 |
Disable no scroll: |
當列表框不具備足夠多的項時顯示不可用的滾動條。若是不使用該屬性,在這種狀況下將不使用滾動條。類型:布爾值 默認值爲假 |
No integral height: |
設置對話框的大小嚴格等於建立對話框時由應用程序指定的大小。通常狀況下,Windows改變列表框的大小以使得它不會只顯示某一項的一部分,即列表框客戶區的高度爲項高的整數倍。 類型:布爾值 默認值爲真 |
下面的示例程序演示了列表框控件的使用。
1. 使用AppWizard建立名爲ListBoxDemo的基於對話框的MFC應用程序工程。
2. 按圖6.47設計應用程序的主對話框。各控件的屬性值如表6.27所示。
圖6. 47 應用程序ListBoxDemo的主對話框
3. 單擊Insert菜單下的Resource命令,插入ID爲IDD_INPUT的對話框,按圖6.48添加對話框的各個控件。
圖6. 48 應用程序ListBoxDemo的IDD_INPUT對話框
各控件的屬性如表6.28所示。
4. 爲對話框IDD_INPUT建立新的對話框類CInputDlg。方法是在資源編輯器中打開對話框IDD_INPUT,此時按下Ctrl+W鍵打開ClassWizard,因爲尚沒有類與對話框IDD_INPUT相關聯,所以ClassWizard將彈出如圖6.49所示的對話框,詢問是否爲對話框建立新的類。選擇Create a new class (這也是默認選項),單擊OK,彈出如圖6.50所示的New class對話框。在Name處輸入CInputDlg,其他採用默認設置,單擊OK即爲對話框IDD_INPUT建立了新類CInputDlg。這時就可使用ClassWizard的Member Variables選項卡爲對話框進行如表6.28所示的成員變量映射了。
表6. 27 應用程序ListBoxDemo主對話框各控件的屬性設置
控件類型 |
資源ID |
控件標題 |
其餘 |
列表框 |
IDC_LISTSELECTABLE |
|
位於圖6.47左邊的列表框,其Selection屬性爲Extended。對應的DDX變量映射(使用ClassWizard的Member Vari-ables選項卡進行設置)爲CListBox類型變量m_lsSelectable。 |
IDC_LISTSELECTED |
|
位於圖6.47右邊的列表框,其Selection屬性爲Extended。對應的DDX變量映射爲CListBox類型變量m_lsSelected。 |
|
靜態控件 |
(無需更改) |
待選擇的文件 |
|
已選擇的文件 |
|
||
下壓按鈕 |
IDC_BTNCHANGEDIR |
<- 改變目錄(&H) |
|
IDC_BTNADD |
添加到(&A) -> |
|
|
IDC_BTNDEL |
刪除(&D) <- |
|
|
IDC_BTNCLEAR |
所有清除(&L) <- |
|
表6. 28 對話框IDD_INPUT各控件的屬性設置
控件類型 |
資源ID |
控件標題 |
其餘 |
靜態控件 |
IDC_PROMPT |
提示字符串 |
對應的DDX變量映射爲CString類型成員變量m_strPrompt |
編輯框 |
IDC_INPUT |
|
對應的DDX變量映射爲CString類型成員變量m_strInput |
接着爲類添加類型爲CString的保護成員變量m_strTitle。然而在類CInputDlg中添加成員函數GetInput的聲明:
CString GetInput(LPCTSTR lpszTitle="輸入",
LPCTSTR lpszPrompt="請在下面的文本框中輸入字符串: ");
該函數顯示對話框IDD_INPUT (如圖6.51所示),並返回用戶在對話框中輸入的字符串,若是用戶單擊了輸入對話框的「取消」按鈕,則函數返回空字符串,參數lpszTitle爲輸入對話框的標題,lpszPrompt爲輸入對話框的提示字符串。其實現以下:
圖6. 49 詢問是否爲對話框IDD_INPUT建立新類
圖6. 50 爲對話框IDD_INPUT建立新類
圖6. 51 輸入對話框
CString CInputDlg::GetInput(LPCTSTR lpszTitle, LPCTSTR lpszPrompt)
{
// 設置標題字符串和提示字符串
m_strTitle=lpszTitle;
m_strPrompt=lpszPrompt;
// 顯示輸入對話框並返回用戶輸入的字符串
if (DoModal()==IDOK)
{
return m_strInput;
}
else
{
return CString("");
}
}
爲類CInputDlg重載OnInitDialog成員函數
BOOL CInputDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: 在這裏添加額外的初始化代碼
SetWindowText(m_strTitle);
GetDlgItem(IDC_INPUT)->SetFocus();
// 因爲爲控件 IDC_INPUT 設置了輸入焦點,所以函數 OnInitDialog 應該返回 FALSE
return FALSE;
}
成員函數OnInitDialog的重載版本設置輸入對話框的標題文本和提示字符串。
5. 用下面的代碼替代類CListBoxDemoDlg的OnInitDialog成員函數中的// TODO註釋:
m_lsSelectable.ResetContent();
m_lsSelectable.Dir(0x17,"*.*");
上面的代碼先調用成員函數ResetContent清除列表框IDC_LISTSELECTABLE中的全部項,再調用成員函數Dir使用當前目錄下的文件名來填充該列表框。第一個參數0x17是文件類型屏蔽位,它等於0x01|0x02|0x04|0x10,它包括了全部常規屬性文件、只讀文件、系統文件和目錄名,第二個參數爲所顯示的文件名,在參數中可使用通配符。
爲按鈕IDC_BTNCHANGEDIR的BN_CLICKED命令添加下面的處理函數OnBtnChangeDir:
void CListBoxDemoDlg::OnBtnChangeDir()
{
CInputDlg dlg;
CString str=dlg.GetInput("輸入目錄","輸入新的目錄名:");
if (str!="" && str.Left(1)!="\\")
{
str+="\\";
}
if (str!="")
{
m_lsSelectable.ResetContent();
int iResult=m_lsSelectable.Dir(0x17,str+"*.*");
if (iResult==LB_ERR)
{
MessageBox("添加文件名出錯!");
}
else if (iResult==LB_ERRSPACE)
{
MessageBox("沒法爲列表框分配足夠的內存!");
}
}
}
上面的代碼首先定義一個類型爲CInputDlg的成員變量,而後調用其成員函數GetInput (咱們已在前面討論過該成員函數)得到用戶輸入的列表目錄名,若是用戶輸入的目錄名不爲空字符串,則調用類CListBox的成員函數將指定目錄下的文件名添加到列表框IDC_LISTSELECTABLE中,若是添加失敗,則彈出相應的出錯信息。
爲按鈕IDC_BTNADD的BN_CLICKED命令添加下面的處理函數OnBtnAdd:
void CListBoxDemoDlg::OnBtnAdd()
{
CString str;
for (int i=0; i<m_lsSelectable.GetCount(); i++)
{
if (m_lsSelectable.GetSel(i))
{
m_lsSelectable.GetText(i, str);
m_lsSelected.AddString(str);
}
}
}
其中,類CListBox的成員函數GetCount返回了列表框中項的數目,而後使用GetSel成員函數得到每一項的選定狀態,這裏要注意列表框中項的索引是基於零的。若是該項的已被選定(即GetSel成員函數返回真值),則使用GetText成員函數得到該項的文本,並將它放到CString類型的變量str中,接着,調用類CListBox中定義的成員函數AddString將字符串str添加到列表框IDC_LISTSELECTED中。
爲按鈕IDC_BTNDEL的BN_CLICKED命令添加以下的處理函數OnBtnDel:
void CListBoxDemoDlg::OnBtnDel()
{
for (int i=m_lsSelected.GetCount()-1; i>-1; i--)
{
if (m_lsSelected.GetSel(i))
{
m_lsSelected.DeleteString(i);
}
}
}
上面的代碼從最末一項開始,檢查列表框IDC_LISTSELECTED中每一項的選定狀態,若是發現該項被選定,則將它從列表框中刪除。從列表框中刪除一項使用類CListBox的成員函數DeleteString,其參數爲所刪除項的索引值。
而不是
這是由於成員函數DeleteString的使用將致使所刪除項以後的全部項的索引值發生改變,這裏,若是所刪除的項的下一項仍被選定的話,該項將不會被刪除。與此相反,刪除一項並不會致使此項以前的項的索引值發生改變,所以,從最末一項開始進行檢查是可行的。
按鈕IDC_BTNCLEAR的BN_CLICKED命令的處理成員函數OnBtnClear具備最簡單的結構,它直接調用類CListBox的成員函數ResetContent刪除列表框IDC_LISTSELECTED中的全部項。
void CListBoxDemoDlg::OnBtnClear()
{
m_lsSelected.ResetContent();
}
圖6. 52 示例程序ListBoxDemo的運行結果
編譯並運行上面的示例程序,其運行結果如圖6.52所示。單擊「改變目錄」按鈕,輸入一個新的目錄名,查看左邊列表框中項的改變狀況。從左邊列表框中選定若干項,單擊「添加到」,將所選定的項添加到右邊列表框(注意列表框中能夠包括相同字符串的項)。再從右邊列表框中選定若干項,驗證按鈕「刪除」和「所有清除」是否正常工做。
第七節 組合框
組合框(combo box)能夠看做是一個編輯框或靜態文本框與一個列表框的組合,組合框的名稱也正是由此而來。當前選定的項將顯示在組合框的編輯框或靜態文本框中。若是組合框具備下拉列表(drop-down list)樣式,則用戶能夠在編輯框中鍵入列表框中某一項的首字母,在列表框可見時,與該首字母相匹配的最近的項將被加亮顯示。
組合框對應於Controls工具箱內的按鈕爲 。在繪製組合框的同時可使用控件的Properties對話框設置控件的各類屬性樣式。一些樣式已在前面的幾節中做了介紹,所以這裏再也不重複,下面給出一些在前面的內容中沒有進行說明的樣式及其含義:
Type: |
指定組合框的類型。可使用的類型以下: Simple:建立包括編輯框控件和列表框的簡單組合框,其中編輯框控件用來接受用戶的輸入。 Dropdown:建立下拉組合框。該類型與簡單組合框相似。但僅當用戶單擊了編輯框控件部分右邊的下拉箭頭時組合框的列表框部分才被顯示。 Drop List:該類型相似於下拉樣式(drop-down),只是使用靜態文本項代替編輯框控件來顯示列表框中的當前選擇。 默認值爲Dropdown。 |
Uppercase: |
將選擇域或列表中的全部文本轉換爲大寫。 類型:布爾值 默認值爲假 |
Lowercase: |
將選擇域或列表中的全部文本轉換爲小寫。 類型:布爾值 默認值爲假 |
與列表框不一樣的是,在繪製組合框的同時能夠預先爲組合框添加一些可選項,方法是單擊Properties對話框中的Data選項卡(如圖所示),直接在Enter listbox items處鍵入組合框中的可選項,每一行爲一個選項,使用Ctrl+Enter鍵開始新的一行。在運行時這些選項將出如今組合框的列表框中。
圖6. 53 爲組合框預置選項
MFC類CComboBox封裝了Windows標準組合框,其成員函數提供了對組合框控件的常見操做的實現。表給出了對在類CListBox中定義的成員函數的描述。
表6. 29 在類CListBox中定義的成員函數
成員函數 |
描述 |
CComboBox |
構造一個CComboBox對象 |
Create |
建立一個組合框並將它與CComboBox對象相關聯 |
InitStorage |
爲組合框的列表框部分的項和字符串預先分配內存塊 |
GetCount |
得到組合框中列表框項的數目 |
GetCurSel |
若是存在的話,返回組合框中列表框的當前選定項的索引 |
SetCurSel |
選擇組合框中列表框內的一條字符串 |
GetEditSel |
得到組合框中編輯控件的當前選定的起始和終止字符位置 |
SetEditSel |
在組合框的編輯控件中選定字符 |
SetItemData |
設置與組合框中指定項相關聯的32位值 |
SetItemDataPtr |
將與組合框中指定項相關聯的32位值設置爲指定的void指針 |
GetItemData |
得到由應用程序提供的與指定組合框項相關聯的32位值 |
GetItemDataPtr |
以void指針的形式返回由應用程序提供的與指定組合框項相關聯的32位值 |
GetTopIndex |
返回組合框中列表框部分的第一個可視項的索引 |
SetTopIndex |
在組合框中的列表框部分的頂部顯示指定索引對應的項 |
SetHorizontalExtent |
以象素爲單位指定組合框的列表框部分能夠橫向滾動的寬度 |
GetHorizontalExtent |
以象素爲單位得到組合框中列表框部分能夠橫向滾動的寬度 |
SetDroppedWidth |
爲組合框的下拉列表框部分設置最小容許寬度 |
GetDroppedWidth |
得到組合框的下拉列表框部分的最小容許寬度 |
Clear |
若是存在的話,刪除編輯控件中當前選定的內容 |
Copy |
若是存在的話,將當前選定以CF_TEXT格式複製到剪貼板 |
Cut |
若是存在的話,刪除編輯控件中當前選定的內容,並將其以CF_TEXT格式複製到剪貼板 |
Paste |
當剪貼板包括CF_TEXT格式的數據時,從剪貼板複製數據到編輯控件的當前插入位置 |
LimitText |
設置用戶能夠在組合框的編輯控件中輸入的文本的長度限制 |
SetItemHeight |
設置組合框中列表項的高度或編輯控件(或靜態文本控件)部分的高度 |
GetItemHeight |
得到組合框中列表項的高度 |
GetLBText |
從組合框中的列表框獲取字符串 |
續表6.29
成員函數 |
描述 |
GetLBTextLen |
得到組合框的列表框中某一字符串的長度 |
ShowDropDown |
對於具備CBS_DROPDOWN或CBS_DROPDOWNLIST屬性的組合框,顯示或隱藏其列表框 |
GetDroppedControlRect |
得到下拉組合框的可視(下拉)列表框的屏幕座標 |
GetDroppedState |
判斷下拉組合框的列表框是否可見(處理下拉狀態) |
SetExtendedUI |
對於具備CBS_DROPDOWN或CBS_DROPDOWNLIST樣式的組合框,選擇默認用戶界面或擴展用戶界面 |
GetExtendedUI |
判斷組合框具備默認用戶界面仍是擴展用戶界面 |
GetLocale |
得到組合框的區域標識符 |
SetLocale |
設置組合框的區域標識符 |
AddString |
向組合框的列表框添加一字符串,對於具備CBS_SORT樣式的組合框,新增長的字符串將被排序並插入到合適的位置,不然將被添加到列表框框的末尾 |
DeleteString |
從組合框的列表框中刪除字符串 |
InsertString |
向組合框的列表框中插入一字符串 |
ResetContent |
清除組合框的列表框和編輯控件中的全部內容 |
Dir |
添加文件名列表到組合框的列表框中 |
FindString |
在組合框的列表框中查找包括指定前綴的第一個字符串 |
FindStringExact |
在組合框的列表框中查找與指定字符串匹配的字符串 |
SelectString |
在組合框的列表框中查找字符串,若是找到的話,在列表框中選擇該字符串,並將字符串複製到編輯控件中 |
DrawItem |
當一個自繪製組合框的可視部分改變時由框架調用 |
MeasureItem |
在建立自繪製組合框時,由框架調用以判斷組合框的尺寸 |
CompareItem |
當將一新項插入到排序的自繪製框中時由框架調用以判斷項的相對位置 |
DeleteItem |
當一列表項被從自繪製組合框中刪除時由框架調用 |
下面的示例程序演示了自繪製組合框的使用。
1. 使用AppWizard建立名爲ComboDemo的基於對話框的工程,按圖6.54添加工程的主對話框(IDD_COMBODEMO_DIALOG)中的各個控件。每一個控件的屬性如表6.30所示。
2. 在ClassView中用鼠標右擊ComboDemo classes,選擇New Class命令。上面的操做將彈出如圖6.55所示的對話框,確認在Class type下拉列表框[注] 中選擇了MFC Class。而後在Name處輸入新的類名CClrComboBox,在Base class下拉列表框中選擇CComboBox。若是須要修改新類的頭文件或實現文件的文件名,能夠單擊Change按鈕,這裏,咱們接受默認的文件名ClrComboBox.cpp和ClrComboBox.h。
圖6. 54 工程ComboDemo的主對話框
表6. 30 對話框IDD_COMBODEMO_DIALOG的控件屬性設置
控件類型 |
ID |
屬性值 |
組合框 |
IDC_CLRCOMBO |
Type:Dropdown Owner draw:Fixed Sort:真 Vertical scroll:真 Has string:假 |
下壓按鈕 |
IDC_ADDCLR |
Caption:添加顏色(&A) |
IDC_CHGCLR |
Caption:改變顏色(&C) |
|
靜態控件 |
IDC_STATICCLR |
Caption屬性值爲空 |
3. 使用ClassWizard的Message Map選項卡在類CClrComboBox中重載基類的MeasureItem成員函數,其重載版本的代碼以下:
void CClrComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
// 因爲組合框具備 CBS_OWNERDRAWFIXED 樣式,所以以 0 爲參數調用成員函數
// GetItemHeight 得到每一項的固定高度
lpMeasureItemStruct->itemHeight=GetItemHeight(0);
}
圖6. 55 從CComboBox派生新類CClrComboBox
函數MeasureItem在自繪製樣式的組合框建立時由框架調用。該函數將每一項的高度放入MEASUREITEMSTRUCT結構的成員中。若是對話框以CBS_OWNERDRAWVARIABLE樣式建立,框架將爲列表框中的每一項調用一次該成員函數,不然,該成員函數只被調用一次。
接着,在CClrComboBox的重載基類的DrawItem成員函數,其代碼以下:
void CClrComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC=CDC::FromHandle(lpDrawItemStruct->hDC);
COLORREF cr=(COLORREF)lpDrawItemStruct->itemData;
// 注意到在出錯的狀況下,GetCurSel 和 GetItemData 返回 CB_ERR,而常量
// CB_ERR 被定義爲 -1,這時不該把它視爲一種系統顏色。
if (cr==CB_ERR)
cr=GetSysColor(COLOR_WINDOW);
if (lpDrawItemStruct->itemAction & ODA_DRAWENTIRE)
{
// 須要重繪整個項
// 以該項所對應的顏色填充整個項
CBrush br(cr);
pDC->FillRect(&lpDrawItemStruct->rcItem, &br);
// 反色居中顯示該顏色的 RGB 組成
CString str;
str.Format("R: %d G: %d B: %d", GetRValue(cr), GetGValue(cr), GetBValue(cr));
CSize size;
size=pDC->GetTextExtent(str);
CRect rect=lpDrawItemStruct->rcItem;
COLORREF tcr;
tcr=~cr & 0x00FFFFFF; // 得到背景色的反色,不能簡單的使用 ~cr
pDC->SetTextColor(tcr);
pDC->SetBkColor(cr);
pDC->TextOut(rect.left+(rect.Width()-size.cx)/2,
rect.top+(rect.Height()-size.cy)/2, str);
}
if ((lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
// 選中狀態由未選中變爲選中,其邊框被加亮顯示
COLORREF crHilite=~cr & 0x00FFFFFF;
CBrush br(crHilite);
pDC->FrameRect(&lpDrawItemStruct->rcItem, &br);
}
if (!(lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & ODA_SELECT))
{
// 選中狀態由選中變爲非選中,清除其邊框的加亮顯示
CBrush br(cr);
pDC->FrameRect(&lpDrawItemStruct->rcItem, &br);
}
}
對於自繪製組合框來講,成員函數DrawItem是須要重載的一個很重要的成員函數。該函數在自繪製組合框的可視部分發生改變時由框架調用。在默認狀況下,該成員函數不作任何操做。其參數lpDrawItemStruct所指向的DRAWITEMSTRUCT結構包括了重繪製所須要的各類信息,如所需重繪的項、其設備上下文以及所執行的重繪行爲等。在該成員函數終止前,應用程序應該恢復由該DRAWITEMSTRUCT結構所提供的爲該顯示上下文所選定圖形設備接口。
由表6.30可知在本示例程序中所使用的自繪組合框中的可選項是有序的,而它們都是一些顏色值,框架如何知道當一個新的顏色值被添加到組合框的列表框中時,它應該處於哪一個顏色值以前,哪一個顏色值以後呢?這時經過調用成員函數CompareItem成員函數來實現的。若是在建立組合框時指定了LBS_SORT樣式,則必須重載該成員函數以提供足夠的理由來幫助框架對新添加入組合框的列表框中的顏色項進行排序。這裏,咱們首先根據顏色亮度的大小來對顏色進行排序,對於亮度相同的顏色,咱們依次以從藍色到紅色的優先級來斷定其相對位置。這個操做是如下面的代碼來實現的:
int CClrComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct)
{
// TODO: 添加判斷指定項的排序順序的代碼
// 當項 1 在項 2 以前時返回 -1
// 當項 1 和項 2 順序相同時返回 0
// 當項 1 在項 2 以後時返回 1
// 得到項 1 和項 2 的顏色值
COLORREF cr1 = (COLORREF)lpCompareItemStruct->itemData1;
COLORREF cr2 = (COLORREF)lpCompareItemStruct->itemData2;
if (cr1 == cr2)
{
// 項 1 和項 2 具備相同的顏色
return 0;
}
// 進行亮度比較, 亮度低的排列順序在前
int intensity1 = GetRValue(cr1) + GetGValue(cr1) + GetBValue(cr1);
int intensity2 = GetRValue(cr2) + GetGValue(cr2) + GetBValue(cr2);
if (intensity1 < intensity2)
return -1;
else if (intensity1 > intensity2)
return 1;
// 若是亮度相同, 按顏色進行排序, (藍色最前, 紅色最後)
if (GetBValue(cr1) > GetBValue(cr2))
return -1;
else if (GetGValue(cr1) > GetGValue(cr2))
return -1;
else if (GetRValue(cr1) > GetRValue(cr2))
return -1;
else
return 1;
}
上面的代碼同時也說明了CompareItem成員函數的不一樣返回值所表明的不一樣含義。這裏要注意的是,因爲同亮度的不一樣顏色給人的眼睛的亮度感受是不同的(這比如人耳對聲音的高頻段和低頻段的聽覺靈敏度要比對中頻段的聽覺靈敏度要小同樣),上面的排序結果給人感受並不象咱們所想象的那樣,是經過顏色的亮度來進行的。但咱們沒有必要在這個問題上過度的糾纏而浪費時間。另外咱們解釋一下,爲何要使用~cr & 0x00FFFFFF來代替~cr。不少人會認爲直接將顏色值按位取反就能夠獲得其對比色,但事實不是這樣的。這是由於若是32位顏色值的高位字節不爲零的話,該顏色值將不被看成一個RGB顏色值,而使用某個32位值與0x00FFFFFF按位與恰可使其高位字節爲零,而其它位則不變。
到目前爲止咱們完成了自繪製組合框對應的類CClrComboBox的設計,下面咱們來看如何在程序中將類CClrComboBox的對象與對話框模板中的現存組合框相關聯,這是使用函數SubclassDlgItem來實現的。首先在類CComboDemoDlg中添加一個類型爲CClrComboBox的成員變量m_clrCombo,其訪問限制在本工程中是不重要的,能夠將它設置爲protected。而後,在類CComboDemoDlg的OnInitDialog成員函數中的// TODO註釋以後添加下面的一行代碼,這行代碼將對象m_clrCombo與ID爲IDC_CLRCOMBO相關聯,第一個參數爲控件的父窗口的指針。
m_clrCombo.SubclassDlgItem(IDC_CLRCOMBO, this);
這樣就能夠經過CWnd的消息映射機制和消息傳遞路徑在類CClrComboBox中處理IDC_CLRCOMBO中事件了。好比當組合框中的項須要重繪時,在CClrComboBox中定義的DrawItem成員函數將被調用,在正確的繪製組合框中的內容。
4. 這裏,咱們在初始時沒有爲組合框添加任何選擇項,用戶能夠單擊如圖6.54的對話框中所示的「添加顏色」按鈕向組合框的列表框中添加新顏色項。單擊該按鈕首先將彈出一個顏色選擇對話框,用戶若是從顏色選擇對話框中選擇了一種具體的顏色,該顏色將被添加到組合框的列表框中以供選擇。基於這個要求,咱們爲按鈕IDC_ADDCLR的BN_CLICKED事件添加以下的命令處理成員函數OnAddClr:
void CComboDemoDlg::OnAddClr()
{
CColorDialog dlg(0, 0, this);
int iRes=dlg.DoModal();
if (iRes==IDOK)
{
COLORREF cr=dlg.GetColor();
m_clrCombo.AddString( (LPCTSTR)cr );
}
else
{
}
}
雖然使用顏色對話框在本書的前面內容中沒有講述過,但即便是對初學者而言,上面的代碼也是很是之簡單的,咱們這裏就不過多的做講解了。
下面來看如何爲「改變顏色」按鈕(IDC_CHGCLR)添加單擊命令處理成員函數。咱們但願用戶在單擊該按鈕時,改變靜態文本控件IDC_CLRSTATIC的顏色以反映用戶所選擇的顏色。若是改變靜態文本控件的顏色呢?咱們這裏使用了將自繪製靜態文本控件的辦法。這並非最簡單的方法。最簡單的方法是處理對話框的WM_CTLCOLOR消息,該消息在控件將要被重繪前發送給該控件的父窗口。這裏咱們舍近而求遠,主要是爲了附帶講述一下自繪製靜態文本控件的用法,它和自繪製組合框的用法存在一些區別。可是,在資源編輯器中咱們不能夠設置一個靜態文本控件的自繪製樣式,不過這並不意味將不可使用自繪製靜態控件。方法並不複雜。首先,爲靜態文本控件添加自繪製樣式,將下面的代碼添加到類CComboDemoDlg的OnInitDialog成員函數中的// TODO註釋以後:
GetDlgItem(IDC_CLRSTATIC)->ModifyStyle(0, SS_OWNERDRAW);
上面的代碼將靜態文本控件修改成具備SS_OWNERDRAW樣式的自繪製靜態控件。下面咱們來看如何在須要的時候從新繪製靜態文本控件IDC_CLRSTATIC,方法是在類CComboDemoDlg中爲消息WM_DRAWITEM添加處理函數OnDrawItem,而使用ClassWizard很容易辦到這一點。重載版本的OnDrawItem成員函數的定義以下:
void CComboDemoDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
if (nIDCtl==IDC_CLRSTATIC)
{
CDC *pDC=CDC::FromHandle(lpDrawItemStruct->hDC);
CBrush br(m_crClrStatic);
CRect rc=lpDrawItemStruct->rcItem;
pDC->FillRect(&rc, &br);
}
}
要使上面的代碼正常工做,咱們還須要在類CComboDemoDlg中定義類型爲COLORREF的成員變量m_crClrStatic,該成員變量保存了靜態文本控件應該具備的顏色。咱們注意到了在重載版本的OnDrawItem成員變量調用了基類的OnDrawItem成員函數,這是必要的,不要忘記在咱們的對話框中還有一個自繪製組合框,基類的OnDrawItem成員函數調用自繪製組合框所對應的CComboBox的派生類的DrawItem成員函數來繪製組合框中的各個項。
下面來看下壓按鈕IDC_CHGCLR的BN_CLICKED事件的處理函數OnChgClr,其代碼以下:
void CComboDemoDlg::OnChgClr()
{
int nSel=m_clrCombo.GetCurSel();
COLORREF cr=(COLORREF)m_clrCombo.GetItemData(nSel);
if (cr!=-1)
{
DRAWITEMSTRUCT drawItemStruct;
drawItemStruct.CtlID=IDC_CLRSTATIC;
drawItemStruct.hwndItem=GetDlgItem(IDC_CLRSTATIC)->GetSafeHwnd();
drawItemStruct.hDC=::GetDC(drawItemStruct.hwndItem);
GetDlgItem(IDC_CLRSTATIC)->GetClientRect(&(drawItemStruct.rcItem));
m_crClrStatic=cr;
OnDrawItem(IDC_CLRSTATIC, &drawItemStruct);
}
}
該成員函數將當前選中的顏色值(若是不爲CB_ERR的話)放入成員變量m_crClrStatic中,而後構造一個DRAWITEMSTRUCT結構變量,並對它進行必要的初始化,最後調用該結構對象調用OnDrawItem成員函數重繪對話框。之後在須要重繪時,OnDrawItem成員函數會由框架自動調用。
這時便可編輯並運行上面的示例程序了,其運行結果如圖6.56所示。單擊「添加顏色」向組合框的列表框中添加幾種顏色選項,再來調試程序的各項功能是否正常。還能夠不一樣的窗口以前進行切換和相互覆蓋或移開,以觀察自繪製組合框和自繪製靜態文本控件是否正確的繪製了自身。
圖6. 56 示例程序ComboDemo的運行結果
相比較標準的組合框而言,自繪製組合框要複雜得多,咱們得本身考慮不少特殊的問題,可是如示例程序所示,它的確能夠實現一些頗有趣的特性,所以在不少程序中獲得普遍的使用。而掌握了自繪製控件的使用,就可使你所編寫的應用程序界面更加的繽紛多彩,可是,要注意一切事物的使用都有一個「度」,不適宜的將應用程序的用戶界面作得過度的「花哩呼哨」,不少時候只會拔苗助長。
第八節 滾動條控件
滾動條(如圖6.57所示)自己也能夠做爲一種控件,一般咱們使用這種控件來進行如定位之類的操做。滾動條控件分爲水平滾動條和垂直滾動條兩種,它們對應於Controls工具箱中的圖標分別爲 和 。
圖6. 57 滾動條控件
對於滾動條控件,能夠在Properties對話框的Styles選項卡內設置的屬性只有一種:即Align屬性,該屬性能夠爲三種值之一:None、Top/Left和Bottom/Right。其中,Top/Left表示將滾動條控件的左上邊與由CreateWindowEx函數的參數定義的矩形的左上邊對齊,而Botton/Right則表示以右下邊進行對齊。該屬性的默認值爲None,即不進行任何對齊操做。
Windows標準滾動條的行爲由MFC類CScrollBar封裝。表中列出了在類CScrollBar中定義的成員函數及其說明。
表6. 31 在類CScrollBar中定義的成員函數
成員函數 |
描述 |
CScrollBar |
構造一個CScrollBar對象 |
Create |
建立一個Windows滾動條,並將它與CScrollBar對象相關聯 |
GetScrollPos |
得到滾動條的當前位置 |
SetScrollPos |
設置滾動條的當前位置 |
GetScrollRange |
得到給定滾動條的當前最大和最小位置 |
SetScrollRange |
設置給定滾動條的當前最大和最小位置 |
ShowScrollBar |
顯示或隱藏滾動條 |
EnableScrollBar |
容許或禁止滾動條上的一個或兩個箭頭 |
SetScrollInfo |
設置關於滾動條的信息 |
GetScrollInfo |
得到滾動條的信息 |
GetScrollLimit |
得到滾動條的限制 |
當用戶單擊了滾動條時,父窗口將收到WM_HSCROLL或WM_VSCROLL消息,在CWnd類的定義了處理該消息的成員函數爲OnHScroll和OnVScroll。成員函數OnHScroll的原型以下:
afx_msg void OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar );
第一個參數nSBCode指定以下之一的滾動條代碼,這些代碼表明用戶所做的滾動請求:
SB_LEFT: |
向左滾動較遠距離 |
SB_ENDSCROLL: |
結束滾動 |
SB_LINELEFT: |
向左滾動 |
SB_LINERIGHT: |
向右滾動 |
SB_PAGELEFT: |
向左滾動一頁 |
SB_PAGERIGHT: |
向右滾動一頁 |
SB_RIGHT: |
向右滾動較遠距離 |
SB_THUMBPOSITION: |
滾動到絕對位置。當前位置由nPos參數指定 |
SB_THUMBTRACK: |
拖動滾動條到指定的位置。當前位置由nPos參數指定 |
一般,SB_THUMBTRACK滾動條代碼由應用程序使用,以便在滾動條被拖動時給以反饋。若是應用程序滾動了由滾動條控制的內容,它必須使用SetScrollPos來重置滾動條的位置。
傳遞給函數OnHScroll的參數反映了當收到消息時由框架得到的值,若是在重載版本的函數中調用了基類的實現,該實現將使用最初由消息傳遞的參數,而不是向函數提供的參數。
消息WM_VSCROLL的處理函數OnVScroll與OnHScroll相似,咱們這裏就再也不重複講述了。下面咱們來看一個例子:
1. 建立一個名爲ScrollDemo的基於對話框的MFC工程,按圖設置對話框的各控件。其中水平滾動條控件的ID爲IDC_SCROLL,編輯框控件的ID爲IDC_CURPOS。
圖6. 58 示例程序ScrollDemo的主對話框的設計
2. 使用ClassWizard爲編輯框控件IDC_CURPOS映射類型爲int的成員變量m_iCurPos,並設置其最大值爲100,最小值爲-100。
3. 使用ClassWizard在類CScrollDemoDlg中爲消息WM_HSCROLL添加處理函數OnHScroll,其代碼以下:
void CScrollDemoDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// 得到原有的滾動條位置
int iPos=pScrollBar->GetScrollPos();
// 根據不一樣的拖動方式設置新的滾動條位置
switch (nSBCode)
{
// 向右滾動一行
case SB_LINERIGHT:
iPos+=1;
break;
// 向左滾動一行
case SB_LINELEFT:
iPos-=1;
break;
// 向右滾動一頁
case SB_PAGERIGHT:
iPos+=10;
break;
// 向左滾動一頁
case SB_PAGELEFT:
iPos-=10;
break;
// 直接拖動滾動塊
case SB_THUMBTRACK:
iPos=nPos;
break;
default:
break;
}
// 滾動條的最大位置不超過 100, 最小位置不小於 -100
if (iPos<-100) iPos=-100;
if (iPos>100) iPos=100;
// 必須手動的更新滾動條的當前位置
pScrollBar->SetScrollPos(iPos);
// 在編輯框中顯示滾動條的當前位置
SetDlgItemInt(IDC_CURPOS, iPos);
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
上面的代碼暗示了一點,這就是在被拖動時,滾動條不會自動更新其位置,咱們必須本身在程序中作到這一點,即經過分析不一樣的滾動方式來改變並設置新的滾動條位置,上面的代碼演示了這一過程。
編譯上面的程序代碼,咱們發現滾動條不能正常工做!這是由於在默認狀況下,滾動條的滾動範圍爲從0到0。這時,咱們根本不可能對滾動條進行有意義的操做。所以,咱們須要將下面的代碼添加到OnInitDialog成員函數:
CScrollBar *pScroll=(CScrollBar*)GetDlgItem(IDC_SCROLL);
pScroll->SetScrollRange(-100, 100);
pScroll->SetScrollPos(0);
SetDlgItemInt(IDC_CURPOS, 0);
上面的代碼設定了滾動條的滾動範圍和默認的滾動條位置,而後,將當前滾動條位置顯示在編輯控件IDC_CURPOS中。
4. 最後咱們來實現一個功能,這就是咱們但願當編輯控件中的文本發生改變時,滾動條上的滑塊的位置也相應的變化。要實現這一點,使用ClassWizard爲控件IDC_CURPOS的通知消息EN_CHANGE添加消息處理函數OnChangeCurPos。
void CScrollDemoDlg::OnChangeCurPos()
{
CString str;
GetDlgItemText(IDC_CURPOS, str);
str.TrimLeft();
str.TrimRight();
int iPos=0;
if (str!="-" && str!="")
{
if (!UpdateData())
{
return;
}
iPos=m_iCurPos;
}
CScrollBar *pScroll=(CScrollBar*)GetDlgItem(IDC_SCROLL);
pScroll->SetScrollPos(iPos);
}
因爲須要檢驗用戶輸入數據的有效性,上面的代碼比較長。首先,若是用戶只輸入一個負號「?」或剛將原有的數據刪除,此時不該該報錯。這裏咱們能夠將滾動條的位置設置爲0。因爲用戶可能在所輸入的數據以前或以後插入一些空格,這種狀況下咱們也不該該報錯,所以,咱們使用了一些額外的代碼來避免了這種狀況。最後,咱們使用了UpdateData函數來使用控件IDC_CURPOS的值更新成員變量m_iCurPos,這樣的目的是便於使用MFC提供的對話框數據檢驗機制。但有個很差的地方是,若是用戶輸入的數據有錯,出現的報錯消息是英文的。若是咱們須要的是一個徹底中文化的軟件,這不能不算是一個瑕疵,這時,咱們應該編寫本身的數據檢驗代碼。可是在本示例程序中,並不須要這樣要求,這裏使用MFC的對話框數據檢驗機制是頗有效的。回到程序代碼中去,若是用戶在編輯控件中輸入的值有效的話,使用這個值去更新滾動條的當前位置,這是經過類CScrollBar的成員函數SetScrollPos來實現的。
其它的一些控件,如CSliderCtrl類所封裝的滑塊控件等,與滾動條控件的使用有很大的共通之處,讀者徹底能夠根據本章中所講述的內容經過觸類旁通來用於其它的場合。