Windows控制元件 概述

weixin_33941350發表於2011-11-30
本文將要介紹的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 接受Enter鍵輸入。

  下面給出建立、用編輯控制元件的示例程式碼,該示例首先通過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 使用者選擇了一個專案並且通過Enter鍵或按下滑鼠而隱藏組合框的下拉選單。
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標準控制元件,讀者也可通過類似的方法予以實現。

相關文章