EternalWindows
コントロール基礎 / コントロールのサイズ

これまで、コントロールの位置やサイズをCreateWindowExに直接指定してきましたが、 場合によってはこの方法が好ましくないこともあります。 たとえば、親ウインドウの左半分にボタンを表示したい場合、 ボタンのサイズは親ウインドウのサイズを取得しない限り求めることはできません。 このように、動的にウインドウサイズを変更する場合はMoveWindowを呼び出します。

BOOL MoveWindow(
  HWND hWnd,
  int X,
  int Y,
  int nWidth,
  int nHeight,
  BOOL bRepaint
);

hWndは、ウインドウハンドルを指定します。 Xは、ウインドウの新しいX位置を指定します。 Yは、ウインドウの新しいY位置を指定します。 nWidthは、ウインドウの新しい幅を指定します。 nHeightは、ウインドウの新しい高さを指定します。 bRepaintは、位置やサイズの変更に伴い、ウインドウを再描画するかどうかを指定します。 TRUEを指定した場合は、再描画が行われます。

今回のプログラムは、MoveWindowを呼び出してコントロールのサイズを決定します。

#include <windows.h>

#define ID_BUTTON 100

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndButton = NULL;

	switch (uMsg) {

	case WM_CREATE:
		hwndButton = CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		return 0;
	
	case WM_SIZE:
		MoveWindow(hwndButton, 0, 0, LOWORD(lParam) / 2, HIWORD(lParam) / 2, TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

MoveWindowの第1引数にコントロールのハンドルを指定すれば、 コントロールのサイズを決定することができますが、 具体的にはこれはどこで呼び出せばよいでしょうか。 コントロールのサイズ変更は、親ウインドウのサイズ変更に合わせて行うべきであるため、 親ウインドウのWM_SIZEで呼び出すのが最も適切であると思われます。 WM_SIZEは、ウインドウのサイズが変更されて新しいサイズに決定した場合に送られます。

case WM_SIZE:
	MoveWindow(hwndButton, 0, 0, LOWORD(lParam) / 2, HIWORD(lParam) / 2, TRUE);
	return 0;

WM_SIZEのwParamには、サイズがどのように変更されたかを示す定数が格納されていますが、 これは特に必要ありません。 重要なのはlParamの方であり、下位ワードにウインドウのクライアント領域の幅が格納され、 上位ワードにウインドウのクライアント領域の高さが格納されます。 これらは、それぞれLOWORDマクロとHIWORDマクロで取得することができます。 MoveWindowの呼び出しによって、ボタンの幅と高さは、常にクライアント領域の2分の1を占めることになります。

複数ウインドウ位置構造体について

今回はMoveWindowを呼び出してサイズ変更を行いましたが、 このような変更はBeginDeferWindowPosなどの呼び出しでも行うことができます。 これらの関数は、複数ウインドウ位置構造体というものを使用し、 複数のウインドウを一斉に変更したい場合は役立ちます。

#include <windows.h>

#define ID_BUTTON1 100
#define ID_BUTTON2 200

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndButton1 = NULL;
	static HWND hwndButton2 = NULL;

	switch (uMsg) {

	case WM_CREATE:
		hwndButton1 = CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)ID_BUTTON1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndButton2 = CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)ID_BUTTON2, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		return 0;
	
	case WM_SIZE: {
		HDWP hdwp;
		
		hdwp = BeginDeferWindowPos(2);
		hdwp = DeferWindowPos(hdwp, hwndButton1, NULL, 0, 0, LOWORD(lParam) / 3, HIWORD(lParam) / 3, SWP_NOZORDER);
		hdwp = DeferWindowPos(hdwp, hwndButton2, NULL, LOWORD(lParam) / 2, 0, LOWORD(lParam) / 2, HIWORD(lParam), SWP_NOZORDER);
		EndDeferWindowPos(hdwp);
		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

まず、BeginDeferWindowPosを呼び出して、複数ウインドウ位置構造体のハンドルを取得します。 このとき、第1引数には一斉に変更したいウインドウの数を指定します。 次に、ウインドウの数だけDeferWindowPosを呼び出し、 新しく更新された複数ウインドウ位置構造体を取得します。 この関数の第2引数以降は、SetWindowPosと同じ意味を持ちます。 最後のEndDeferWindowPosによって、DeferWindowPosに指定したウインドウが一斉に変更されます。



戻る