使用 Windows
本節中的範例說明如何執行下列工作:
建立主視窗
應用程式建立的第一個視窗通常是主視窗。 您可以使用 CreateWindowEx 函式來建立主視窗,並指定視窗類別、視窗名稱、視窗樣式、大小、位置、功能表句柄、實例句柄和建立數據。 主視窗屬於應用程式定義的視窗類別,因此您必須註冊視窗類別,並在建立主視窗之前提供 類別的視窗程式。
大部分的應用程式通常會使用 WS_OVERLAPPEDWINDOW 樣式來建立主視窗。 此樣式提供視窗標題列、視窗功能表、重設大小框線,以及最小化和最大化按鈕。 CreateWindowEx 函式會傳回可唯一識別視窗的句柄。
下列範例會建立屬於應用程式定義窗口類別的主視窗。 視窗名稱 [主視窗] 會顯示在視窗的標題列中。 藉由結合WS_VSCROLL和WS_HSCROLL樣式與WS_OVERLAPPEDWINDOW樣式,除了WS_OVERLAPPEDWINDOW樣式所提供的元件之外,應用程式也會建立具有水準和垂直滾動條的主視窗。 CW_USEDEFAULT常數的四個出現次數會將視窗的初始大小和位置設定為系統定義的預設值。 藉由指定 NULL 而不是功能表句柄,視窗將會為視窗類別定義功能表。
HINSTANCE hinst;
HWND hwndMain;
// Create the main window.
hwndMain = CreateWindowEx(
0, // no extended styles
"MainWClass", // class name
"Main Window", // window name
WS_OVERLAPPEDWINDOW | // overlapped window
WS_HSCROLL | // horizontal scroll bar
WS_VSCROLL, // vertical scroll bar
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
CW_USEDEFAULT, // default width
CW_USEDEFAULT, // default height
(HWND) NULL, // no parent or owner window
(HMENU) NULL, // class menu used
hinst, // instance handle
NULL); // no window creation data
if (!hwndMain)
return FALSE;
// Show the window using the flag specified by the program
// that started the application, and send the application
// a WM_PAINT message.
ShowWindow(hwndMain, SW_SHOWDEFAULT);
UpdateWindow(hwndMain);
請注意,上述範例會在建立主窗口之後呼叫 ShowWindow 函式。 這是因為系統不會在建立主視窗之後自動顯示。 藉由將 SW_SHOWDEFAULT 旗標傳遞至 ShowWindow,應用程式可讓啟動應用程式的程式設定主視窗的初始顯示狀態。 UpdateWindow 函式會傳送視窗的第一個WM_PAINT訊息。
建立、列舉和重設大小子視窗
您可以使用子視窗,將視窗的工作區分割成不同的功能區域。 建立子視窗就像建立主視窗一樣,您可以使用 CreateWindowEx 函式。 若要建立應用程式定義視窗類別的視窗,您必須註冊視窗類別,並在建立子視窗之前提供視窗程式。 您必須為子視窗 提供WS_CHILD 樣式,並在建立子視窗時指定子視窗的父視窗。
下列範例會將應用程式主視窗的工作區分成三個功能區域,方法是建立三個大小相等的子視窗。 每個子視窗的高度與主視窗的工作區相同,但每一個都是其寬度的三分之一。 主視窗會建立子視窗,以回應 WM_CREATE 訊息,主視窗會在自己的視窗建立程式期間接收該訊息。 因為每個子視窗都有 WS_BORDER 樣式,因此每個視窗都有細線框線。 此外,因為 未指定WS_VISIBLE 樣式,因此每個子視窗一開始都會隱藏。 另請注意,每個子視窗都會指派子視窗標識碼。
主視窗會調整子視窗的大小,並放置子視窗,以回應 WM_SIZE 訊息,主視窗在大小變更時會收到訊息。 為了回應WM_SIZE,主視窗會使用 GetClientRect 函式擷取其工作區的維度,然後將維度傳遞至 EnumChildWindows 函式。 EnumChildWindows 會將句柄傳遞至每個子窗口,接著將句柄傳遞至應用程式定義的 EnumChildProc 回呼函式。 此函式會藉由呼叫 MoveWindow 函式來調整和定位每個子視窗;大小和位置是以主視窗工作區的維度和子視窗的標識碼為基礎。 之後, EnumChildProc 會呼叫 ShowWindow 函式,讓窗口可見。
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT rcClient;
int i;
switch(uMsg)
{
case WM_CREATE: // creating main window
// Create three invisible child windows.
for (i = 0; i < 3; i++)
{
CreateWindowEx(0,
"ChildWClass",
(LPCTSTR) NULL,
WS_CHILD | WS_BORDER,
0,0,0,0,
hwnd,
(HMENU) (int) (ID_FIRSTCHILD + i),
hinst,
NULL);
}
return 0;
case WM_SIZE: // main window changed size
// Get the dimensions of the main window's client
// area, and enumerate the child windows. Pass the
// dimensions to the child windows during enumeration.
GetClientRect(hwnd, &rcClient);
EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient);
return 0;
// Process other messages.
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
LPRECT rcParent;
int i, idChild;
// Retrieve the child-window identifier. Use it to set the
// position of the child window.
idChild = GetWindowLong(hwndChild, GWL_ID);
if (idChild == ID_FIRSTCHILD)
i = 0;
else if (idChild == ID_SECONDCHILD)
i = 1;
else
i = 2;
// Size and position the child window.
rcParent = (LPRECT) lParam;
MoveWindow(hwndChild,
(rcParent->right / 3) * i,
0,
rcParent->right / 3,
rcParent->bottom,
TRUE);
// Make sure the child window is visible.
ShowWindow(hwndChild, SW_SHOW);
return TRUE;
}
終結視窗
您可以使用 DestroyWindow 函式終結視窗。 一般而言,應用程式會在終結視窗之前傳送 WM_CLOSE 訊息,讓視窗有機會在窗口終結之前提示用戶確認。 當使用者從視窗功能表單擊 [關閉] 時,包含視窗功能表的視窗會自動接收WM_CLOSE訊息。 如果使用者確認視窗應該終結,應用程式會呼叫 DestroyWindow。 系統會在 從畫面中移除WM_DESTROY 訊息後,將訊息傳送至視窗。 為了回應 WM_DESTROY,視窗會儲存其數據,並釋放它配置的任何資源。 主視窗會呼叫 PostQuitMessage 函式來結束應用程式的處理WM_DESTROY。
下列範例示範如何在終結視窗之前提示用戶確認。 為了回應WM_CLOSE,會顯示包含 [是]、[否] 和 [取消] 按鈕的對話框。 如果使用者按兩下 [是],則會呼叫DestroyWindow,否則不會終結視窗。 當應用程式處理 WM_CLOSE 訊息時, 0
會在所有情況下傳回。 因為正在終結的視窗是主視窗,因此此範例會呼叫 PostQuitMessage 以回應WM_DESTROY。
case WM_CLOSE:
// Create the message box. If the user clicks
// the Yes button, destroy the main window.
if (MessageBox(hwnd, szConfirm, szAppName, MB_YESNOCANCEL) == IDYES)
DestroyWindow(hwndMain);
return 0;
case WM_DESTROY:
// Post the WM_QUIT message to
// quit the application terminate.
PostQuitMessage(0);
return 0;
使用分層式 Windows
若要讓對話框顯示為半透明視窗,請先如往常建立對話框。 然後,在 WM_INITDIALOG上,設定視窗延伸樣式的分層位,並使用所需的 Alpha 值呼叫 SetLayeredWindowAttributes 。 程式代碼看起來可能像這樣:
// Set WS_EX_LAYERED on this window
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
// Make this window 70% alpha
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);
請注意,SetLayeredWindowAttributes 的第三個參數是介於 0 到 255 之間的值,0 會讓視窗完全透明,255 表示完全不透明。 此參數會模擬 AlphaBlend 函式的更多功能 BLENDFUNCTION。
若要讓此視窗再次完全不透明,請呼叫 SetWindowLong 移除WS_EX_LAYERED位,然後要求視窗重新繪製。 想要移除位,讓系統知道它可以釋放一些與分層和重新導向相關聯的記憶體。 程式代碼看起來可能像這樣:
// Remove WS_EX_LAYERED from this window styles
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// Ask the window and its children to repaint
RedrawWindow(hwnd,
NULL,
NULL,
RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
若要使用分層子視窗,應用程式必須宣告在指令清單中自行宣告 Windows 8 感知。
針對 Windows 10/11,其中一個可以包含此相容性代碼段 app.manifest
,使其成為 Windows 10 感知:
...
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 GUID -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
...
如需修改應用程式指令清單的詳細資訊,請參閱這裡: 應用程式指令清單