本文將要介紹的Windows控制項指的是Windows系統預定義的標準控制項,如按鈕控制項、編輯控制項和清單控制項等。這些預定義控制項實際是一種特殊的子視窗,主要供使用者同應用程式的互動之用。和普通視窗類別一樣,每一個預定義控制項也都是由所屬的視窗類別規定了自身的外觀屬性和具有的功能。Windows系統通過預定義的方式提供了一些標準控制項的視窗類別名,在程式設計時只需通過調用CreateWindow()函數或CreateWindowEx()函數並將預定義的視窗類別名作為參數傳入即可建立出相應的控制項。當使用者通過螢幕對象同控制項進行互動操作時,控制項將以"通知訊息"的形式向父視窗發送WM_COMMAND通知訊息,訊息的wParam參數含有控制標識,在lPamam參數的高位字和低位字中分別含有通知碼和控制控制代碼,由父視窗完成對訊息的響應處理。
按鈕類控制項
按鈕類控制項是視窗類別名被系統預定義為BUTTON的一類控制項,該類控制項具有十餘種不同的視窗風格,包含了普通的下壓式按鈕、選項按鈕、複選按鈕和群組方塊等多種常用的按鈕形式。具體情況列表如下:
按鈕風格 說明
BS_AUTOCHECKBOX 同複選按鈕類似,點擊一下選中,再次點擊取消。
BS_AUTORADIOBUTTON 同選項按鈕類似,點擊後選中標誌將從同組的其他選項按鈕處移到當前選項。
BS_AUTO3STATE 同三態複選按鈕類似,只是在使用者點擊後改變狀態。
BS_CHECKBOX 複選按鈕
BS_DEFPUSHBUTTON 下壓按鈕,具有較黑的邊框。
BS_GROUPBOX 群組方塊
BS_LEFTTEXT 同選項按鈕或複選按鈕配合使用,標題將顯示在左側。
BS_OWNERDRAW 可建立一個擁有者自繪按鈕。
BS_PUSHBUTTON 普通下壓按鈕
BS_RADIOBUTTON 選項按鈕
BS_3STATE 三態複選按鈕
以上按鈕風格通常需要同視窗風格共同使用,由CreateWindow()函數或MFC的CButton類成員函數Create()完成對按鈕的建立:
// 按鈕類視窗風格
DWORD STYLE[9] = {BS_AUTOCHECKBOX,
BS_AUTORADIOBUTTON,
BS_AUTO3STATE,
BS_CHECKBOX,
BS_DEFPUSHBUTTON,
BS_GROUPBOX,
BS_PUSHBUTTON,
BS_RADIOBUTTON,
BS_3STATE};
// 動態建立按鈕
for (int i = 0; i < 9; i++)
{
m_ctrButton[i].Create("BUTTON", WS_VISIBLE | WS_CHILD | WS_BORDER | STYLE[i], CRect(10 + 110 * i, 10, 100 + 110 * i, 40), this, IDC_BUTTON1 + i);
}
如果需要處理按鈕發送給其父視窗的訊息,可以在進行訊息響應的類實現中添加一個如下形式的訊息映射入口和相應的訊息處理函數:
ON_Notification(id, memberFxn )
其中,id為發送通知訊息的按鈕ID號,memberFxn為訊息處理函數。如果按鈕是以new操作符的方式在堆(heap)內建立一個CButton對象,那麼就必須確保在關閉視窗前能調用delete銷毀該對象。如果CButton對象是在棧上建立的就不必顯式銷毀對象了,應用程式在退出時會自動予以銷毀。
編輯類控制項
以視窗類別名"EDIT"建立的編輯類控制項是一個可以用來接受使用者鍵盤字元輸入的矩形地區,可以在其內進行編輯操作。該控制項是程式接受使用者字元輸入的一種主要手段,輸入的內容存放在其父視窗容量有限的(32KB)局部堆中。
MFC的CEdit類提供了有關編輯類控制項的功能函數。編輯控制項既可以在對話方塊模板上建立也可以通過代碼來直接建立,這兩種方式均要通過CEdit的建構函式來構造一個CEdit對象。CEdit類從CWnd繼承了一些重要的函數,比如可以通過使用CWnd類成員函數SetWindowText()和GetWindowText()來設定和擷取一個編輯控制項中的文本。同按鈕類控制項類似,如果要處理由編輯控制項發送給其父視窗的通知訊息,需要在父視窗類中為每一個待處理訊息增添訊息映射入口和訊息響應函數。
在調用Create()函數建立編輯控制項時,Windows系統將發出WM_NCCREATE、WM_NCCALCSIZE、WM_CREATE和WM_GETMINMAXINFO等訊息給編輯控制項。這些訊息預設地分別由OnNcCreate()、OnNcCalcSize()、OnCreate()和OnGetMinMaxInfo()等CWnd類成員函數進行處理,可以根據實際需要對其進行重載使用。作為一種特殊的視窗,編輯控制項除了需要指定普通視窗風格外,還可以通過選用不同的控制項風格而擷取相應的功能效果,可選用的編輯風格列表如下:
編輯風格 說明
ES_AUTOHSCROLL 當在行尾添加一個字元後自動向右滾動10個字元。
ES_AUTOVSCROLL 當輸入斷行符號後自動上滾一行。
ES_CENTER 字元置中顯示。
ES_LEFT 字元靠左對齊。
ES_LOWERCASE 統一轉化為小寫字母。
ES_MULTILINE 允許多行顯示。
ES_NOHIDESEL 當編輯失去焦點時隱藏對字元的選定,重新獲得焦點後以反色顯示選中內容。
ES_OEMCONVERT 將ANSI字元轉化為OEM字元。
ES_PASSWORD 以星號顯示字元,多用於回顯密碼。
ES_RIGHT 字元靠右對齊
ES_UPPERCASE 統一轉化為大寫字母。
ES_READONLY 設定字元為唯讀。
ES_WANTRETURN 接受斷行符號鍵輸入。
下面給出建立、用編輯控制項的範例程式碼,該樣本首先通過Create()函數建立一個控制項對象,並通過SetWindowText()函數為其設定字元。通過添加對控制項通知訊息的響應代碼可以檢測文本是否發生了改變。程式主要實現清單如下:
// 控制項的建立部分:
// 建立編輯控制項
m_ctrEdit.Create(WS_VISIBLE | WS_CHILD | WS_BORDER | ES_WANTRETURN | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, CRect(10, 50, 250, 150), this, ID_EDIT1);
// 為控制項設定字元
m_ctrEdit.SetWindowText("Hello World!");
……
// 添件對控制項通知訊息的響應
//{{AFX_MSG(CSample02View)
afx_msg void OnEnChange();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
……
BEGIN_MESSAGE_MAP(CSample02View, CView)
//{{AFX_MSG_MAP(CSample02View)
ON_EN_CHANGE(ID_EDIT1, OnEnChange)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
……
void CSample02View::OnEnChange()
{
// 通知訊息響應代碼
AfxMessageBox("內容已改變!");
}
列表框與組合框
列表框是視窗類別名為"ListBox"的預定義子視窗,在視窗矩形內包含有一些可以滾動顯示的欄狀字串列表。標準的列表框只能允許選中一個條目,選中的條目將以系統色彩COLOR_HIGHLIGHT高亮顯示。Windows還提供了多種不同風格的標準列表框,其中包括多重選擇清單方塊、多列顯示的列表框和可以顯示圖象的擁有者畫(Owner-draw)列表框等。另一種與列表框相關的控制項是組合框,該控制項預定義類名為"COMBOBOX",實際是一個編輯控制項同一個彼此相關的列表框控制項的組合。使用者既可以在組合框的編輯欄上直接輸入、編輯文字也可以從下拉式清單中顯示的可能選擇中進行選取。
MFC的CListBox 類封裝了列表框控制項,由成員函數Create()完成對列表框的建立,在建立的同時指定了控制項的視窗風格。當列表框中的條目被選中或被滑鼠雙擊後將向父視窗發送WM_COMMAND訊息。如果列表框採用了LBS_NOTIFY風格,父視窗就可以通過ON_LBN訊息映射宏直接將通知訊息同訊息處理函數建立起映射關係。列表框提供了十餘種視窗風格,現將列表框風格及說明列表如下:
列表框風格 說明
LBS_STANDARD 建立一個具有邊界和垂直捲軸、當選擇發生變化或條目被雙擊時能夠通知父視窗的標準列表框。所有條目按字母排序。
LBS_SORT 按字母排序。
LBS_NOSEL 條目可視但不可選。
LBS_NOTIFY 當使用者選擇或雙擊一個串時,發出訊息通知父視窗。
LBS_DISABLENOSCROLL 在條目不多時依然顯示並不起作用的捲軸。
LBS_MULTIPLESEL 允許條目多選。
LBS_EXTENDEDSEL 可用SHIFT和滑鼠或指定鍵組合來選擇多個條目。
LBS_MULTICOLUMN 允許多列顯示。
LBS_OWNERDRAWVARIABLE 建立一個擁有者畫列表框,條目高度可以不同。
LBS_OWNERDRAWFIXED 建立一個具有相同條目高度的擁有者畫列表框。
LBS_USETABSTOPS 允許使用TAB定位字元。
LBS_NOREDRAW 當條目被增刪後不自動更新列表顯示。
LBS_HASSTRINGS 記憶了添加到列表中的字串。
LBS_WANTKEYBOARDINPUT 當有鍵按下時向父視窗發送WM_VKEYTOITEM或WM_CHARTOITEM訊息。
LBS_NOINTEGRALHEIGHT 按程式設定尺寸建立列表框。
預設的,列表框在每添加或刪除一個條目後都會自動重繪,如果在列表框中已經有了幾百條甚至上千條條目,將會因為重繪而引起比較嚴重的閃爍。可以通過使用LBS_NOREDRAW風格來禁止列表框的自動重繪。在需要更新顯示時強制重繪列表框視窗即可。如果在建立時未使用LBS_NOREDRAW風格,可以在增刪條目前向列表框發送WM_SETREDRAW訊息,指定其不重繪,增添完畢後再次發送WM_SETREDRAW訊息重新啟用自動重繪風格。樣本過程如下:
CListBox m_ctrListBox;
// 禁止自動重繪
m_ctrListBox.SendMessage(WM_SETREDRAW, FALSE, 0);
// 進行條目增刪操作
……
// 允許自動重繪
m_ctrListBox.SendMessage(WM_SETREDRAW, TRUE, 0);
列表框建立之初是不含任何條目的,通過CListBox成員函數AddString()和InsertString()向列表框增添或插入條目。如果列表框具有LBS_SORT風格,那麼新添加字串的位置是不固定的,要根據字串的字母進行排序;如果不具有該風格,新字串將添加到列表框的末尾。
如果有必要,可以使用SetItemDataPtr()或SetItemData()將一個32位的指標(或一個DWORD的值)同列表框中的一個條目聯絡起來,並且在設定後可以通過調用GetItemDataPtr()或GetItemData()而擷取。這樣做的目的是可以將列表框中的條目同外部資料建立聯絡。例如:可以用這種方式非常方便地將一個包含有地址、電話號碼和E-mail地址等資訊的資料結構同列舉在列表框中的持有人建立起關聯。當從列表框中選中某個人時,可以同時得到有關該人的通訊資訊。
當巨集指令清單框時,將會通過WM_COMMAND訊息發送通知給父視窗,訊息參數lParam的高位元組包含了通知碼標識符。在MFC應用程式中,列表框的通知訊息通過ON_LBN訊息映射宏而映射到類成員函數。下表給出了列表框的幾個通知訊息以及相應的ON_LBN宏。其中,LBN_DBLCLK,LBN_SELCHANGE和LBN_SELCANCEL通知訊息只有在列表框使用了LBS_NOTIFY或LBS_STANDARD風格時才會被發出,其他通知訊息則無此限制。
通知碼標識符 ON_LBN宏 值 含義
LBN_SETFOCUS ON_LBN_SETFOCUS 4 列表框接收到輸入焦點
LBN_KILLFOCUS ON_LBN_KILLFOCUS 5 列表框接失去輸入焦點
LBN_ERRSPACE ON_LBN_ERRSPACE -2 列表框儲存溢出
LBN_DBLCLK ON_LBN_DBLCLK 2 雙擊條目
LBN_SELCHANGE ON_LBN_SELCHANGE 1 改變選擇
LBN_SELCANCEL ON_LBN_SELCANCEL 3 取消選擇
其中,最經常使用的兩個通知訊息是LBN_DBLCLK和LBN_SELCHANGE。對於不可複選的列表框可以通過GetCurSel()來擷取當前雙擊的是列表框條目的索引值;對於允許多選的列表框則需要用GetCaretIndex()來代替GetCurSel()。下面通過一段範例程式碼對清單控制項的使用做一個直觀的示範:
// 建立並初始化列表框
// 建立列表框
m_ctrListBox.Create(WS_VISIBLE | WS_CHILD | WS_BORDER | LBS_STANDARD,CRect(270, 50, 370, 150), this, IDC_LIST1);
// 添加條目
CString Item[9] = {"Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9"};
for (int i = 0; i < 9; i++)
m_ctrListBox.AddString(Item[i]);
// 選中第4個條目
m_ctrListBox.SetCurSel(3);
……
// 父視窗對通知訊息的處理函數的聲明(在標頭檔中)
//{{AFX_MSG(CSample02View)
afx_msg void OnLbnDblClk();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
……
// 父視窗對通知訊息的映射入口 (在實現檔案中)
BEGIN_MESSAGE_MAP(CSample02View, CView)
//{{AFX_MSG_MAP(CSample02View)
ON_LBN_DBLCLK(IDC_LIST1, OnLbnDblClk)
//}}AFX_MSG_MAP
// Standard printing commands
END_MESSAGE_MAP()
……
// 父視窗對LBN_DBLCLK通知訊息的處理
void CSample02View::OnLbnDblClk()
{
// 得到當前選中條目的索引
int Index = m_ctrListBox.GetCurSel();
// 得到此條目的內容
char Text[20];
m_ctrListBox.GetText(Index, Text);
// 以資訊框報告得到的內容
AfxMessageBox(CString(Text));
}
雖然組合框實際是列表框和編輯框的組合,但在使用中的表現使得組合框同其他控制項一樣當作一個獨立的控制項去使用。MFC的CComboBox類提供了對組合框的功能支援。在使用Create()函數建立組合框時可以同時指定組合框的風格(參見下表)。
視窗風格 說明
CBS_AUTOHSCROLL 當在行尾輸入字元時自動將編輯框中的文字向右滾動。
CBS_DROPDOWN 同CBS_SIMPLE風格類似,只是只有在使用者點擊下拉表徵圖時才會顯示出下拉式清單。
CBS_DROPDOWNLIST 同CBS_DROPDOWN類似,只是顯示當前選項的編輯框為一靜態框所代替。
CBS_HASSTRINGS 建立一個包含了由字串組成的項目的擁有者畫組合框。
CBS_OEMCONVERT 將組合框中的ANSI字串轉化為OEM字元。
CBS_OWNERDRAWFIXED 由下拉式清單方塊的擁有者負責對內容的繪製;列表框中各項目高度相同。
CBS_OWNERDRAWVARIABLE 由下拉式清單方塊的擁有者負責對內容的繪製;列表框中各項目高度可以不同。
CBS_SIMPLE 下拉式清單始終顯示。
CBS_SORT 自動對下拉式清單中的項目進行排序。
CBS_DISABLENOSCROLL 當下拉式清單顯示內容過少時顯示垂直捲軸。
CBS_NOINTEGRALHEIGHT 在建立控制項時以指定的大小來精確設定組合框尺寸。
對組合框進行操作也會向父視窗發送通知訊息,處理過程同前面幾種控制項大同小異,是通過ON_CBN訊息映射宏完成對通知訊息的映射的。下面就給出這些ON_CBN宏的詳細說明:
ON_CBN宏 對應事件
ON_CBN_CLOSEUP 關閉下拉式清單。
ON_CBN_DBLCLK 雙擊下拉式清單中的項目。
ON_CBN_DROPDOWN 下拉顯示列表框。
ON_CBN_EDITCHANGE 編輯框中常值內容被改動。
ON_CBN_EDITUPDATE 編輯框內容更新顯示。
ON_CBN_ERRSPACE 組合框不能為某個特殊請求分配足夠的記憶體。
ON_CBN_SELENDCANCEL 使用者的選擇被取消。
ON_CBN_SELENDOK 使用者選擇了一個項目並且通過斷行符號鍵或按下滑鼠而隱藏組合框的下拉式清單。
ON_CBN_KILLFOCUS 組合框失去焦點。
ON_CBN_SELCHANGE 選擇發生變化。
ON_CBN_SETFOCUS 組合框獲得輸入焦點。
最後給出一段有關組合框的範例程式碼,從代碼實現不難看出下拉式方塊控制項同前面給出的清單控制項在編程實現上的相似性。
// 組合框的建立
// 建立清單控制項
m_ctrComboBox.Create(WS_VISIBLE | WS_CHILD | WS_BORDER | CBS_DROPDOWN, CRect(400, 50, 470, 150), this, IDC_COMBOX1);
// 添加條目
CString Item[9] = {"Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9"};
for (int i = 0; i < 9; i++)
m_ctrComboBox.AddString(Item[i]);
// 選中第4個條目
m_ctrComboBox.SetCurSel(3);
……
// 通知訊息響應函數的聲明(在標頭檔中)
//{{AFX_MSG(CSample02View)
afx_msg void OnCbnSelChange();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
……
// 對通知訊息的響應處理(在實現檔案中)
BEGIN_MESSAGE_MAP(CSample02View, CView)
//{{AFX_MSG_MAP(CSample02View)
ON_CBN_SELCHANGE(IDC_COMBOX1, OnCbnSelChange)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
……
void CSample02View::OnCbnSelChange()
{
// 得到當前選中條目的索引
int Index = m_ctrComboBox.GetCurSel();
// 得到此條目的內容
char Text[20];
m_ctrComboBox.GetLBText(Index, Text);
// 以資訊框報告得到的內容
AfxMessageBox(CString(Text));
}
樹形控制項
樹形控制項是一種可以分級顯示項目列表的視窗,其所含項目以相互關聯的方式顯示在控制項中,通過點擊位於某個層次的項目節點可以展開下一層次中從屬於該節點的所有項目。樹形控制項非常適合於管理那些層次較多且相互間隸屬關係較為清晰的項目元素。在MFC中,由CTreeCtrl類提供了對樹形控制項的功能支援。
在用Create()建立了一個樹形控制項後可以用SetImageList()函數為其設定一個圖象列表,這樣就可以在樹形控制項中為各個層次的項目設定表徵圖。通過InsertItem()函數可以為其添加資料項目,返回的HTREEITEM類型的控制代碼唯一標識了此添加的項目。該控制代碼應當妥善保管,只有通過該控制代碼才能為此項目繼續添加子項目。如果在建立子視窗時指定父視窗控制代碼為NULL,則將直接在根目錄建立項目。下面這段代碼將通過上述函數建立一個樹形控制項並向其添加二個層次的項目:
// 建立一個樹形控制項
m_ctrTreeCtrl.Create(WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_LINESATROOT | TVS_HASLINES | TVS_HASBUTTONS | TVS_EDITLABELS, CRect(500, 50, 670, 200),this, IDC_TREE1);
……
TV_ITEM tvItem;
TV_INSERTSTRUCT tvInsert;
tvItem.mask = TVIF_TEXT; // 指定pszText成員可用
tvItem.pszText = "Item0"; // 根項目顯示的字元
tvInsert.hParent = TVI_ROOT; // 指定父控制代碼
tvInsert.item = tvItem; // 指定TV_ITEM結構對象
tvInsert.hInsertAfter = TVI_LAST; // 項目插入方式
// 建立根項目,當前項目控制代碼儲存於hItem0中
HTREEITEM hItem0 = m_ctrTreeCtrl.InsertItem(&tvInsert);
……
// 在根項目下繼續建立第二層項目
tvItem.mask = TVIF_TEXT;
tvItem.pszText = "SubItem0";
tvInsert.hParent = hItem0;
tvInsert.item = tvItem;
tvInsert.hInsertAfter = TVI_LAST;
HTREEITEM hItem3 = m_ctrTreeCtrl.InsertItem(&tvInsert);
作為同使用者的介面,樹形控制項將在不同的動作下發出各種通知訊息,可以在控制項視窗的訊息映射中添加ON_NOTIFY_REFLECT宏或是在控制項所在父視窗的訊息映射中添加ON_NOTIFY宏來為每一個通知訊息指定處理函數。
樹形控制項中的任何一個項目均可以擁有一個子項目列表,此列表可以隨時處於展開或縮起狀態。當處於展開狀態時,對應的子項目將以縮排方式顯示在父項目下;當處於縮起狀態時,子項目將不顯示。當使用者在雙擊父項目時,相應的子項目列表將自動在展開與縮起狀態切換。在子項目列表狀態發生改變時和狀態改變完成後樹形控制項將分別發出TVN_ITEMEXPANDING和 TVN_ITEMEXPANDED通知訊息。關於其它的通知訊息及其具體含義可參見下表:
通知訊息 訊息說明
TVN_BEGINDRAG 開始拖拽操作
TVN_BEGINLABELEDIT 開始編輯標籤
TVN_BEGINRDRAG 開始滑鼠右鍵拖拽操作
TVN_DELETEITEM 刪除一個指定的項目
TVN_ENDLABELEDIT 結束編輯標籤
TVN_GETDISPINFO 擷取一個項目的顯示資訊
TVN_ITEMEXPANDED 子項目列表被展開或收合
TVN_ITEMEXPANDING 子項目列表正將展開或收合
TVN_KEYDOWN 鍵盤事件
TVN_SELCHANGED 項目的選擇發生改變
TVN_SELCHANGING 項目的選擇將要發生改變
TVN_SETDISPINFO 通知更新一個項目的資訊
小結
本文對VC++編程中經常用到的按鈕控制項、編輯控制項、樹形控制項、清單控制項和下拉式方塊控制項等Windows預定義的標準控制項的動態建立、風格設定、通知訊息的響應等內容作了具體的講述,通過本文前述內容,讀者可以掌握這些常用控制項的一般使用方法。對於本文未提到的其他Windows標準控制項,讀者也可通過類似的方法予以實現。