ウィンドウの使用
このセクションの例では、次のタスクを実行する方法について説明します。
メイン ウィンドウの作成
アプリケーションが最初に作成するウィンドウは、通常、メイン ウィンドウです。 メイン ウィンドウを作成するには、CreateWindowEx 関数を使用して、ウィンドウ クラス、ウィンドウ名、ウィンドウ スタイル、サイズ、位置、メニュー ハンドル、インスタンス ハンドル、作成データを指定します。 メイン ウィンドウはアプリケーション定義のウィンドウ クラスに属しているため、メイン ウィンドウを作成する前に、ウィンドウ クラスを登録し、クラスのウィンドウ プロシージャを指定する必要があります。
ほとんどのアプリケーションでは、通常、WS_OVERLAPPEDWINDOW スタイルを使用してメイン ウィンドウを作成します。 このスタイルでは、ウィンドウにタイトル バー、ウィンドウ メニュー、サイズ設定の境界線、最小化ボタンと最大化ボタンが表示されます。 CreateWindowEx 関数は、ウィンドウを一意に識別するハンドルを返します。
次の例では、アプリケーション定義のウィンドウ クラスに属するメイン ウィンドウを作成します。 ウィンドウ名、メイン ウィンドウがウィンドウのタイトル バーに表示されます。 WS_VSCROLL スタイルと WS_HSCROLL スタイルを WS_OVERLAPPEDWINDOW スタイルと組み合わせることにより、アプリケーションは、WS_OVERLAPPEDWINDOW スタイルによって提供されるコンポーネントに加えて、水平スクロール バーと垂直スクロール バーを備えたメイン ウィンドウを作成します。 CW_USEDEFAULT 定数が 4 回出現すると、ウィンドウの初期サイズと位置がシステム定義の既定値に設定されます。 メニュー ハンドルの代わりに 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 スタイルを指定し、親ウィンドウを指定する必要があります。
次の例では、同じサイズの 3 つの子ウィンドウを作成することによって、アプリケーションのメイン ウィンドウのクライアント領域を 3 つの機能領域に分割しています。 各子ウィンドウは、メイン ウィンドウのクライアント領域と同じ高さですが、幅はそれぞれ 3 分の 1 です。 メイン ウィンドウは、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
が返されます。 破棄されるウィンドウはメイン ウィンドウであるため、この例では、WM_DESTROY に応答して PostQuitMessage を呼び出します。
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;
レイヤード ウィンドウの使用
ダイアログ ボックスを半透明のウィンドウとして表示するには、まず通常通りダイアログを作成します。 次に、WM_INITDIALOG で、ウィンドウの拡張スタイルのレイヤード ビットを設定し、希望するアルファ値で 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 の 3 番目のパラメーターは 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>
...
アプリ マニフェストの変更の詳細については、こちらを参照してください: アプリケーション マニフェスト