Windows 程式設計/使用者介面控制元件
一些預定義的視窗類旨在用作使用者介面控制元件。它們通常被稱為“標準 Windows 控制元件”和“通用控制元件”。
這些 UI 控制元件的使用應在面向任務的類別中進行記錄,而不是以面向 API 的方式進行記錄。
標準 Windows 控制元件在 USER32.DLL (舊的 USER.EXE) 中實現,後來 (Windows XP+) 由 COMCTL32.DLL 用於 Luna 外觀和感覺。
這些始終存在。不需要初始化。適用於 Windows 2,ListBox 和 ComboBox 適用於 Windows 3。
靜態控制元件是與使用者沒有互動的“控制元件”。
靜態控制元件是一個子視窗,它顯示文字、點陣圖影像或圖示。靜態控制元件不能被選中,也不會從鍵盤獲得焦點。但是,它們可以透過使用SS_NOTIFY接收滑鼠輸入。這將允許控制元件透過其父級的WM_NOTIFY事件檢測單擊和雙擊。
標籤應放置在其所指控制元件的前面。這樣做時,按 Alt+熱鍵將自動將焦點移動到下一個可能的控制元件,無需任何程式碼行。
標籤也可以用於在單行編輯控制元件的右側顯示適當的物理單位。由於此類文字從不充當標籤,因此邏輯順序 (Z 順序) 不重要。可以使用樣式位 SS_NOPREFIX 來避免與安培符號 (&) 字元相關的問題。
- 建立標籤
標籤靜態控制元件應是另一個視窗的子視窗,可以是主視窗或子視窗。要建立它,您只需使用 STATIC 類和您選擇的引數呼叫 CreateWindow()。以下是建立它的基本程式碼。
/*Create a Static Label control*/
hwndLabel = CreateWindow(
TEXT("STATIC"), /*The name of the static control's class*/
TEXT("Label 1"), /*Label's Text*/
WS_CHILD | WS_VISIBLE | SS_LEFT, /*Styles (continued)*/
0, /*X co-ordinates*/
0, /*Y co-ordinates*/
50, /*Width*/
25, /*Height*/
hwnd, /*Parent HWND*/
(HMENU) ID_MYSTATIC, /*The Label's ID*/
hInstance, /*The HINSTANCE of your program*/
NULL); /*Parameters for main window*/
使用標籤
- 設定標籤的文字
要設定標籤 (靜態控制元件) 的文字,請使用帶有 WM_SETTEXT 訊息的 SendMessage() 函式。
/*Setting the Label's text
/*You may need to cast the text as (LPARAM)*/
SendMessage(
hwndLabel , /*HWND Label*/
WM_SETTEXT, /*UINT Message*/
NULL, /*WPARAM Unused*/
(LPARAM) TEXT("Hello")); /*LPARAM Text*/
使用其他樣式位,可以顯示點陣圖或圖示 - 當源影像放置在同一個資源中時,無需任何程式碼行。
圖元檔案支援是在 Windows 95 中新增的。
靜態控制元件可用於繪製分組矩形或分隔線。
按鈕是具有簡單使用者介面的控制元件。
每個人都應該熟悉 Windows 按鈕。它只是一個帶有文字的凸起的正方形,當您單擊它時通常會發生一些事情。
與所有 Windows 控制元件一樣,按鈕是您的視窗的子視窗,可以是主視窗或另一個子視窗。因此,要將其實現到您的程式中,您只需呼叫 CreateWindow() 函式。以下是一行程式碼來建立一個按鈕。
- 建立按鈕
// create button and store the handle
HWND hwndButton = CreateWindow (TEXT("button"), // The class name required is button
TEXT("Push Button"), // the caption of the button
WS_CHILD |WS_VISIBLE | BS_PUSHBUTTON, // the styles
0,0, // the left and top co-ordinates
100,300, // width and height
hwnd, // parent window handle
(HMENU)ID_MYBUTTON, // the ID of your button
hInstance, // the instance of your application
NULL) ; // extra bits you don't really need
這將為您建立按鈕,但它在單擊時不會執行任何操作。為此,您需要進入 Windows 過程函式並處理 WM_COMMAND 事件。在 WM_COMMAND 事件中,wparam 的低位字是導致事件的子視窗的 ID。因此,要確保您收到了來自按鈕的訊息,請確保 ID 相同。
- 測試按鈕 ID
// compare button ID to message ID
if(ID_MYBUTTON == LOWORD(wparam))
{
/* it's your button so do some work */
}
所以現在您知道您的按鈕已被按下,您需要找出發生了什麼,所以我們處理儲存在 wparam 的高位字中的通知程式碼。您需要關注的通知程式碼是 BN_CLICKED。
- 測試通知
// compare Notification to message Notification
if(BN_CLICKED == HIWORD(wparam))
{
/* the button has been clicked do some stuff */
}
最後,您可能還需要知道的是 lparam 包含被按下的按鈕的控制代碼。
這種型別可以實現任何東西。在許多情況下,此類按鈕被停用 (WS_DISABLED,即沒有使用者輸入),並用於在對話方塊中繪製其他任何內容。在這種情況下,它充當實際繪製的方便佔位符,因為對話方塊的畫素大小以及控制元件的大小會根據使用者的 dpi 設定和當前字型度量而變化。
從 Windows Vista 開始引入,此按鈕具有一個額外的組合框下拉欄位,通常用於在按下之前更改此按鈕的行為。
複選框
[edit | edit source]複選框可以單獨使用,也可以組合成一個組。在一個組中,可以進行多選。此外,還可以使用三態複選框。此狀態應用於“不知道”或“以下選擇不同”等情況,例如在 Windows 設定控制面板應用程式中。
為了正確實現複選框,應在編譯時遵循以下規則,通常使用資源編輯器
- 所有複選框都應設定 BS_AUTOCHECKBOX 樣式位,否則程式邏輯必須處理點選事件
- 單獨的複選框或組中的第一個複選框應設定 WS_GROUP 樣式位
- 其他複選框不應設定 WS_GROUP 樣式位
- 複選框後的下一個控制元件必須設定 WS_GROUP。否則,向右/向下的箭頭鍵將不會迴圈焦點,而是會進入下一個控制元件(可能是編輯控制元件)
- 複選框組必須連續排序
- 設定 WS_TABSTOP 不是必需的。通常,每個複選框都設定了此樣式位。
如果這樣做了,箭頭鍵將按預期移動焦點,空格鍵將切換其狀態,無需編寫任何程式碼。
複選框組不應用於超過 7 個選項。如果可用選項更多,或在編譯時未知選項數量,則應使用複選框列表。
單選按鈕
[edit | edit source]單選按鈕始終在一個至少包含 2 個按鈕的組中,通常是 3 到 7 個選項。它們相互排斥,一次只能選擇(選中)一個。儘管可以這樣做,但程式邏輯應避免出現沒有選中或選中多個單選按鈕的情況。當所有按鈕都設定了 BS_AUTORADIOBUTTON 樣式位時,Windows(更確切地說,是在訊息迴圈中處理的 IsDialogMessage() 函式,就像呼叫 DialogBox() 時一樣)會自動確保這種行為,無需編寫任何程式碼。
- 所有按鈕都設定了 BS_AUTORADIOBUTTON 樣式位
- 組中的第一個單選按鈕具有 WS_GROUP 樣式
- 其他單選按鈕不具有 WS_GROUP 樣式
- 單選按鈕在對話方塊模板或 CreateWindowEx() 呼叫(即 Z 順序)中以連續順序排列。
單選按鈕不應設定 WS_TABSTOP 樣式位。Windows 會自動將 Tab 鍵的焦點設定為選中的單選按鈕。
單選按鈕不應用於超過 7 個選項。(這超出了良好 GUI 設計的“3 到 7 個選項”規則。)如果可用選項更多,或在編譯時未知選項數量,則應使用組合框。
單選按鈕組後的下一個控制元件應設定 WS_GROUP 位。否則,向右/向下的箭頭鍵將無法按預期工作:焦點將跳到下一個控制元件,而不是在單選按鈕組中迴圈。
示例程式碼
[edit | edit source]這就是使用 API 在 Win32 應用程式中實現按鈕所需的一切
- 單選按鈕
HWND hRadio =CreateWindow(TEXT("button"), TEXT("&Red"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 155, 100, 30, hDlg, (HMENU)ID_RED, GetModuleHandle(NULL), NULL);
順便說一句,幾乎沒有人使用 CreateWindow 建立單選按鈕。通常將其放置在 RC 檔案中,使用以下行
AUTORADIOBUTTON "&title",id,left,top,width,height[,morestyles]
視窗標題中的一個和號將建立一個熱鍵功能。CreateWindow() 使用畫素座標,而 RC 檔案條目使用對話方塊基單位來表示這四個度量值。
分組框
[edit | edit source]這不是一個真正的按鈕,沒有任何互動。它的類名也是“BUTTON”。此元素通常用於視覺上對單選按鈕進行分組。它不會像 .NET 對應元素那樣建立子元素。
分組框應放置在其視覺內容之前。如果這樣做,按下 Alt+熱鍵將自動將焦點移動到下一個可用的控制元件,無需編寫任何程式碼。
捲軸
[edit | edit source]雖然捲軸可以附加到任何視窗或控制元件(並且本身不是帶控制代碼的視窗),但捲軸控制元件是一個真正的帶控制代碼的視窗。這些可以建立為水平方向 (SB_HORZ) 或垂直方向 (SB_VERT)。
在通用控制元件進度條和軌道條出現之前,有人“誤用”了此控制元件來設定揚聲器的音量或顯示一個漫長過程的進度。現在,單個捲軸絕不會用於此類目的。
但是,捲軸對於與水平捲軸共享狀態列是必要的,就像我們從 Acrobat Reader 或某些辦公軟體中看到的那樣。因為標準捲軸始終與其視窗一樣長,不會更短。
編輯控制元件
[edit | edit source]編輯控制元件是文字編輯和顯示的標準基本物件(通常稱為文字框)。
編輯控制元件具有相當多的樣式。
單行編輯
[edit | edit source]單行編輯控制元件通常用於輸入簡短描述、檔名、引數和數字。儘管支援 WS_VSCROLL 樣式並顯示一個小小的垂直捲軸,就像一個上下控制元件一樣,但除此之外什麼都不會發生。通常,使用 ES_AUTOHSCROLL 來在文字不適合給定空間時水平移動內容。字型可以以程式設計方式更改,但不能混合字型和/或顏色。為此,可以使用 RichEdit 控制元件。
多行編輯
[edit | edit source]多行編輯看起來像 Notepad.exe。實際上,記事本只是一個帶有框架視窗的編輯控制元件,該框架視窗支援選單、載入/儲存、調整大小等功能。其他所有功能,甚至包括上下文選單、Unicode 和從右到左支援,都已經內建在該控制元件中。
// create a text box and store the handle
HWND hwndText = CreateWindow(
TEXT("edit"), // The class name required is edit
TEXT(""), // Default text.
WS_VISIBLE | WS_CHILD | WS_BORDER | WS_HSCROLL | WS_VSCROLL| ES_MULTILINE | ES_AUTOHSCROLL, // the styles
0,0, // the left and top co-ordinates
100,300, // width and height
hwnd, // parent window handle
(HMENU)ID_MYTEXT, // the ID of your editbox
hInstance, // the instance of your application
NULL
); // extra bits you dont really need
以下是在示例中使用的樣式
| WS_BORDER | 文字區域周圍的細邊框。 |
| WS_HSCROLL | 顯示水平捲軸。 |
| WS_VSCROLL | 顯示垂直捲軸。 |
| ES_MULTILINE | 這是一個多行文字框。 |
| ES_AUTOHSCROLL | 不換行。 |
// Set the text. SendMessage(hwndText, WM_SETTEXT, 0, (LPARAM)"Hello"); // Get the text. LRESULT iTextSize = SendMessage(hwndText, EM_GETLIMITTEXT, 0, 0); char *szText = new char[iTextSize]; SendMessage(hwndText, WM_GETTEXT, iTextSize, (LPARAM)szText);
多行編輯需要“\r\n”作為行分隔符,不能使用其他字元。否則,在某些 Windows 版本上可能會出現奇怪的行為。與之配套的父視窗對話方塊樣式 DS_LOCALEDIT 對 Win32 來說是多餘的。無法直接訪問內部文字緩衝區(EM_GETHANDLE 不起作用),因此您必須使用 GetWindowText() 和一個足夠大的緩衝區來獲取副本。
與其他所有控制元件相比,編輯控制元件的行為有所不同,因為它們在以程式設計方式更改其內容時會使用 EN_CHANGE 和 EN_UPDATE 通知父級。當多個編輯具有迴圈依賴關係時,很容易發生無限迴圈。有一些解決方法可以解決這個問題
- 在 EN_CHANGE 上,使用一些小的延遲呼叫 SetTimer。在 SetWindowText/SetDlgItemText/SetDlgItemInt 之後,在 WM_TIMER 處理程式被呼叫之前,呼叫 KillTimer 來刪除計時器。在計時器例程中處理實際更改。如果 OnChange 處理很長,但應避免按下 ENTER 鍵,這將非常有用。例如,設定無線電接收機的天線位置,您可以在其中輸入任意經度或步進電機位置。
- 在 EN_CHANGE 上,檢查 EM_GETMODIFY。在 SetWindowText 之前清除修改標誌。
- 使用一個全域性變數 (“bool NoEditChange”),您可以在 EN_CHANGE 上進行檢查。在 SetWindowText 之前將其設定為 true,並在完成時設定為 false。就像往常一樣,對話方塊是完全單執行緒的,這裡不會發生競爭條件。
列表框
[edit | edit source]列表框可以包含製表符,因此可以顯示某種程度上帶有製表符的條目。此控制元件並不常用,除了在類似電子表格的應用程式中。它的主要目的是提供下拉組合框的彈出列表框。
組合框
[edit | edit source]此控制元件有三種外觀。
簡單組合框(不可編輯)
[edit | edit source]這種型別很少使用,不值得解釋。
不可編輯的下拉組合框
[edit | edit source]當用戶需要從一些選項中選擇時使用此控制元件。
在建立時,使用較大的高度。它設定下拉列表框的最大高度。如果可用選項較少,Windows 會自動縮短其長度,但永遠不會擴充套件。並且在此處滾動很煩人。(注意,Windows 3.x 不會縮短。)剩餘控制元件的高度為 13 個對話方塊單位。(當您將其與類似的 Edit 控制元件組合時,您需要數字 13。)
// create a combo box and store the handle
HWND hwndCombo = CreateWindow (TEXT("combobox"), // The class name required is combobox
TEXT(""), // not used, ignored.
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, // the styles
0,0, // the left and top co-ordinates
100,300, // width and height
hwnd, // parent window handle
(HMENU)ID_MYCOMBO, // the ID of your combobox
hInstance, // the instance of your application
NULL) ; // extra bits you dont really need
實際小部件的顯示高度將根據字型自動更改。“未使用的”部分高度將用於下拉選單的大小。
例如,300 畫素高度中只有 33 畫素用於所選專案。當單擊向下按鈕時,選單的高度將為 267 畫素。
還有其他組合框樣式:CBS_SIMPLE(類似於列表框)和 CBS_DROPDOWN(類似於 CBS_DROPDOWNLIST,但所選欄位可編輯)。
// Add a list of strings to the combo box.
SendMessage(
hwndCombo, // The handle of the combo box
CB_ADDSTRING, // Tells the combo box to append this string to its list
0, // Not used, ignored.
(LPARAM) "Item A" // The string to add.
);
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item B");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item C");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item D");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item E");
// Select the default item to be "Item C".
SendMessage(
hwndCombo, // The handle of the combo b,
CB_SETCURSEL, // Tells the combo box to select the specified index
2, // The index of the item to select (starting at zero)
0 // Not used, ignored.
);
可編輯下拉組合框
[edit | edit source]當用戶可以輸入單行文字或可以選取一些預定義字串時使用它。一個典型的例子是選擇帶有歷史功能的 URL 或數字。或者選擇埠地址,允許輸入一個尚未知道的埠地址。
通用控制元件
[edit | edit source]通用控制元件大約在 Windows 3.11 中引入,並在功能和外觀上不斷擴充套件。請注意,當使用 Windows XP Luna 樣式或更新版本時,上面的標準控制元件會自動由 comctl32.dll 的程式碼進行子分類。清單資源控制此行為。
通用控制元件 DLL 的版本和功能很大程度上取決於安裝的 Internet Explorer 版本。
標頭檔案和連結要求
[edit | edit source]使用通用控制元件需要連結到 comctl32.lib 幷包含 commctrl.h。雖然某些控制元件預設存在,但其他控制元件需要使用 InitCommonControlsEx() 進行初始化。它取決於作業系統,因此最好始終使用正確的位呼叫 InitCommonControlsEx(),以滿足您的應用程式需要的控制元件。
函式 InitCommonControls() 不會做任何事情,但它確保當您不連結到任何其他通用控制元件庫函式(如 CreateToolBar())時載入庫。當然,Windows 3.11 類是在 DLL 載入時註冊的。
通用控制元件通常用作常規對話方塊元素。對話方塊資源模板大約在 Windows 2.0 中引入,如果這些不是 STATIC、BUTTON、EDIT、LISTBOX、COMBOBOX 或 SCROLLBAR(上面的標準控制元件),則為使用者類名保留空間。因此,示例可能會顯示如何使用 CreateWindowEx() 建立子視窗,但這很不常見,因為將它們包含到對話方塊模板中要容易得多。
工具欄
[edit | edit source]引入:Windows 3.11
case WM_VSCROLL:
{
int scrollMsg = (int)LOWORD(wParam);
int pos = (short int)HIWORD(wParam);
SCROLLINFO scrollInfo;
GetScrollInfo(hTextOutput, SB_VERT, &scrollInfo);
switch (scrollMsg)
{
case SB_LINEUP:
{
if (pos > 0)
{
pos--;
}
}
break;
case SB_LINEDOWN:
{
if (pos < scrollInfo.nMax)
{
pos++;
}
}
break;
}
scrollInfo.fMask = SIF_POS;
scrollInfo.nPos = pos;
SetScrollInfo (hTextOutput, SB_VERT, &scrollInfo, TRUE);
}
狀態列(狀態列)
[edit | edit source]請注意,CalcEffectiveClientRect() 的文件大多是錯誤的:陣列的第一個 UINT/BOOL 對根本沒有使用。
日期/時間選擇器
[edit | edit source]引入:Windows 95
IP 地址輸入欄位
[edit | edit source]向上/向下控制元件
[edit | edit source]它主要附加到一個編輯視窗,因此它看起來像一個帶有微小垂直捲軸的編輯視窗。事實上,捲軸的技巧在 Windows 3.x 的時代被廣泛使用。但現在它看起來比這個控制元件更糟糕。
向上/向下控制元件自動執行整數計數和限制監視。它們可以自動適應其“夥伴”視窗,因此不需要放置程式碼。編輯控制元件會自動向水平方向縮小,就好像添加了垂直捲軸一樣。
可能的夥伴視窗是單行編輯、進度條和滑塊。
不幸的是,Win32 上/下控制元件不適用於十進位制數,與 .NET 對等項相反。因此,您必須編寫一些無聊的程式碼來實現 .NET 行為。
帶有向上/向下控制元件的編輯應該具有 14 個對話方塊基本單位的高度,以獲得最佳外觀。不幸的是,當使用標準 Windows 方案時,文字內容看起來有點過高(即錯位)。
選項卡控制元件
[edit | edit source]此控制元件很少直接使用。屬性表大量使用它。
當選項卡周圍需要更多控制元件時,需要選項卡控制元件。屬性表只能在底部有一些按鈕。當真正使用時,每個選項卡應該控制一個子對話方塊(設定 WS_CHILD 樣式),然後子對話方塊是父對話方塊的子級(不是選項卡控制元件的子級!)。資源應該仔細規劃,因此不需要放置程式碼。雖然屬性表在第一次呼叫時會延遲初始化每個子級(以節省時間),但這取決於您是否複製此行為或在顯示時初始化所有子級。單擊選項卡應該 ShowWindow(...,SW_HIDE) 當前子對話方塊,並 ShowWindow(...,SW_SHOW) 所需的子對話方塊。
子對話方塊是在屬性表(Win95)中引入的,而不是在選項卡控制元件(Win32s)中引入的。這些具有以下行為
- 當子對話方塊消失時,其所有控制元件都會消失——即真正的層次結構
- 其子級的選項卡順序將被處理,就好像它們在父對話方塊中一樣
- 巢狀子級不受官方支援
- 每個子級都有自己的對話方塊過程
- 對話方塊字型必須與父對話方塊相同
工具提示
[edit | edit source]氣球式工具提示是在 Windows XP 中引入的。因此,大多數程式仍然使用標準的黑色黃色矩形工具提示。
列表檢視、樹檢視
[edit | edit source]Windows 資源管理器是這兩個控制元件的完美示例。左側窗格通常顯示目錄樹,右側窗格顯示檔案列表。此外,桌面本身(非常類似於)列表檢視。
static const INITCOMMONCONTROLSEX icc={sizeof(icc),ICC_TREEVIEW_CLASSES};
InitCommonControlsEx(&icc);
組合框 Ex
[edit | edit source]引入:Windows 95。
額外功能
- 文字左側的影像
- 所選專案的不同影像(但很少使用)
注意事項
- 初始化繁瑣
- 不要忘記 InitCommonControlsEx!
- 更大的二進位制資源
選中列表框
[edit | edit source]進度條
[edit | edit source]它是什麼
[edit | edit source]一個標準的條形圖,顯示專案的進度。顯示已完成數量與總數量的圖形表示。
示例程式碼
[edit | edit source]- 建立進度條
進度條控制元件應該是另一個視窗的子視窗,可以是主視窗或子視窗。要建立它,您只需使用 PROGRESS_CLASS 類和您選擇的引數呼叫 CreateWindow() 函式。以下是建立它的基本程式碼。
/*Create a Progress Bar*/ HWND hwndProgress = CreateWindow( PROGRESS_CLASS, /*The name of the progress class*/ NULL, /*Caption Text*/ WS_CHILD | WS_VISIBLE, /*Styles*/ 0, /*X co-ordinates*/ 0, /*Y co-ordinates*/ 200, /*Width*/ 30, /*Height*/ hwnd, /*Parent HWND*/ (HMENU) ID_MYPROGRESS, /*The Progress Bar's ID*/ hInstance, /*The HINSTANCE of your program*/ NULL); /*Parameters for main window*/
- 使用進度條
- 改變位置
有三種方法可以遞增/遞減進度條。PBM_DELTAPOS 以給定數字遞增。PBM_SETPOS 設定特定位置。PBM_SETSTEP 設定遞增/遞減數字,而 PBM_STEPIT 以該數字遞增/遞減。
- 增量位置
- PBM_DELTAPOS 使用作為 wparam 給出的數字推進進度條。
/*Advance progress bar by 25 units*/ SendMessage( hwndProgress , /*HWND*/ /*Progress Bar*/ PBM_DELTAPOS, /*UINT*/ /*Message*/ 25, /*WPARAM*/ /*Units*/ NULL) /*LPARAM*/ /*Unused*/
- 設定位置
- PBM_SETPOS 將進度條推進到 WPARAM 中指定的特定位置。
/*Advances progress bar to specified position (50)*/ SendMessage( hwndProgress , /*HWND*/ /*Progress Bar*/ PBM_SETPOS, /*UINT*/ /*Message*/ 50, /*WPARAM*/ /*Units*/ NULL) /*LPARAM*/ /*Unused*/
- 步進位置
- PBM_SETSTEP 指定要步進的單位數量。PBM_STEPIT 以 PBM_SETSTEP 給出的單位數量(預設值為 10 個單位)遞增。
/*Advances progress bar by specified units*/ /*If progress bar is stepped when at the progress bar's maximum, /* the progress bar will go back to its starting position*/ /*Set the step*/ SendMessage( hwndProgress , /*HWND*/ /*Progress Bar*/ PBM_SETSTEP, /*UINT*/ /*Message*/ 1, /*WPARAM*/ /*Amount to step by*/ NULL) /*LPARAM*/ /*Unused*/ /*Step*/ SendMessage( hwndProgress , /*HWND*/ /*Progress Bar*/ PBM_STEPIT, /*UINT*/ /*Message*/ NULL, /*WPARAM*/ /*Unused*/ NULL) /*LPARAM*/ /*Unused*/