ウィンドウの位置とサイズ
GetWindowRect関数
GetWindowRect
関数はウィンドウの位置とサイズを取得します。
- BOOL GetWindowRect(
HWND hWnd,
LPRECT lpRect
); - ウィンドウhWndのスクリーン座標を取得しlpRectに格納する。
成功した場合は0以外を、失敗した場合は0を返す。
スクリーン座標とはデスクトップ(ディスプレイ)上の位置のことで、画面の左上が基準(0, 0)になります。
第二引数lpRect
はRECT構造体へのポインタです。
子ウィンドウやコントロールに使用した場合でも取得される値はスクリーン座標です。
得られる値はウィンドウの左上と右下の座標ですので、ウィンドウの幅や高さが欲しい場合は「right - left」や「bottom - top」といった計算を行います。
Windows VISTA以降では視覚効果としてウィンドウの影が描画されますが、GetWindowRect関数で取得されるウィンドウ位置にはこの影の領域も含まれています。
GetClientRect関数
ウィンドウ内のクライアント領域のサイズを取得するにはGetClientRect関数を使用します。
詳しくはリンク先を参照してください。
ScreenToClient関数/ClientToScreen関数
スクリーン座標をクライアント座標に変換するにはScreenToClient関数を使用します。
その逆はClientToScreen関数を使用します。
詳しくはリンク先を参照してください。
MoveWindow関数
MoveWindow
関数はウィンドウの位置を設定します。
- BOOL MoveWindow(
HWND hWnd,
int X,
int Y,
int nWidth,
int nHeight,
BOOL bRepaint
); - ウィンドウhWndのスクリーン座標とウィンドウサイズを設定する。
bRepaintがTRUEの場合はウィンドウを再描画する。
成功した場合は0以外を、失敗した場合は0を返す。
X
/Y
はスクリーン座標、nWidth
/nHeight
は幅と高さです。
bRepaint
にTRUE
を設定すると、移動後にウィンドウおよび親ウィンドウ(ある場合)を再描画します。
コントロールに対してFALSE
を指定して移動すると親ウィンドウが再描画されないので、実際には移動されているのに見た目には変化がないという状態になります。
(見た目通りの位置にコントロールが存在しないかもしれない)
正常に表示するには無効領域を作成するなどして自分で再描画をする必要があります。
SetWindowPos関数
ウィンドウの移動にはSetWindowPos
関数を使用することもできます。
- BOOL SetWindowPos(
HWND hWnd,
HWND hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
UINT uFlags
); - ウィンドウhWndのスクリーン座標とZオーダーを設定する。
X
、Y
はスクリーン座標、cx
、cy
は幅と高さです。
hWndInsertAfter
はZオーダーの指定です。
Zオーダーとはウィンドウの重なり順を決定するものです。
以下の定数のいずれかを指定します。
定数 | 説明 |
---|---|
HWND_TOP | 最上位に配置する。 |
HWND_BOTTOM | 最下位に配置する。 |
HWND_TOPMOST | 常に手前に配置する。 |
HWND_NOTOPMOST | 常に手前状態を解除し、常に手前でないウィンドウの中で最上位に配置する。 |
Zオーダーは「通常の状態」と「常に手前の状態(TOPMOST)」とが存在します。
常に手前に配置されるウィンドウは、非アクティブ状態であっても他のウィンドウの後ろに隠れることはありません。
常に手前に配置されるウィンドウは複数作ることができ、「常に手前グループ」の中でも重なり順が存在します。
「通常の状態グループ」のウィンドウは全ての「常に手前グループ」のウィンドウの後ろに描画されます。
最後のuFlags
は以下の定数の組み合わせを指定します。
定数 | 説明 |
---|---|
SWP_NOSIZE | 現在のサイズを保持する。 ( cx 、cy の指定を無視する) |
SWP_NOMOVE | 現在の位置を保持する。 ( X 、Y の指定を無視する) |
SWP_NOZORDER | 現在のZオーダーを保持する。 ( hWndInsertAfter の指定を無視する) |
SWP_NOREDRAW | ウィンドウ(および親ウィンドウ)を再描画しない。 |
SWP_NOACTIVATE | ウィンドウをアクティブにしない。 |
SWP_FRAMECHANGED SWP_DRAWFRAME |
WM_NCCALCSIZE メッセージを送る。(WM_NCCALCSIZEメッセージは通常、ウィンドウサイズを変更した場合に送られる) SetWindowLongPtr関数でウィンドウスタイルを変更した場合に、変更を適用する。 |
SWP_SHOWWINDOW | ウィンドウを表示する。 |
SWP_HIDEWINDOW | ウィンドウを非表示にする。 |
SWP_NOCOPYBITS | クライアント領域の内容を破棄する。 このフラグをセットしない場合、クライアント領域の有効な内容が保存され、移動後にクライアント領域にコピーされる。 |
SWP_NOOWNERZORDER SWP_NOREPOSITION |
オーナーウィンドウのZオーダーを変更しない。 |
SWP_NOSENDCHANGING | ウィンドウがWM_WINDOWPOSCHANGING メッセージを受信しないようにする。 |
SWP_DEFERERASE | WM_SYNCPAINT メッセージを生成しない。 |
SWP_ASYNCWINDOWPOS | 関数呼び出し元スレッドとウィンドウを所有するスレッドが異なる場合に、ウィンドウを所有するスレッドにリクエストを送信する。 これにより、スレッドでリクエストの処理が終了するまで関数呼び出し元スレッドが待機(ブロッキング)することを防ぐことができる。 |
SetForegroundWindow関数
ウィンドウを前面に出すだけの操作を行う場合はSetForegroundWindow
関数が使用できます。
- BOOL SetForegroundWindow(
HWND hWnd
); - ウィンドウhWndをフォアグラウンドウィンドウに設定する。
成功した場合は0以外を、失敗した場合は0を返す。
デスクトップのサイズの取得
GetDesktopWindow関数
デスクトップ自体のサイズを取得する方法はいくつかあります。
GetDesktopWindow
関数でデスクトップのハンドルを取得できるので、これを使用してGetWindowRect
関数でそのサイズを取得できます。
- HWND GetDesktopWindow();
- デスクトップウィンドウのハンドルを取得する。
以下のように使用します。
RECT rtScreen;
GetWindowRect(GetDesktopWindow(), &rtScreen);
GetSystemMetrics関数
もうひとつ、GetSystemMetrics
関数を使用する方法もあります。
- int GetSystemMetrics(
int nIndex
); - nIndexで指定したシステムメトリクスまたはシステム構成設定を取得する。
システムメトリクスとはシステムで使用されているサイズ情報のことです。
nIndex
には定数を指定しますが、この関数は非常に多くの定数が用意されています。
ディスプレイのサイズの取得にはSM_CXSCREEN
(横幅)、SM_CYSCREEN
(縦幅)を指定します。
int width, height;
width = GetSystemMetrics(SM_CXSCREEN);
height = GetSystemMetrics(SM_CYSCREEN);
ウィンドウを画面中央に配置する
ウィンドウを画面上(または親ウィンドウ上)の中央に配置するような関数は用意されていないので、上述の関数を使用して自前で計算して配置します。
以下はそのサンプルです。
#include <windows.h>
//ウィンドウの生成等は省略
#define IDC_BUTTON_TOPLEFT 100
#define IDC_BUTTON_TOPCENTER 101
#define IDC_BUTTON_TOPRIGHT 102
#define IDC_BUTTON_MIDDLELEFT 103
#define IDC_BUTTON_MIDDLECENTER 104
#define IDC_BUTTON_MIDDLERIGHT 105
#define IDC_BUTTON_BOTTOMLEFT 106
#define IDC_BUTTON_BOTTOMCENTER 107
#define IDC_BUTTON_BOTTOMRIGHT 108
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static RECT rtScreen;
RECT rt;
int x, y, w, h;
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
CreateWindow(
L"BUTTON", L"┌",
WS_CHILD | WS_VISIBLE,
0, 0, 25, 25,
hWnd, (HMENU)IDC_BUTTON_TOPLEFT, hInst, NULL);
CreateWindow(
L"BUTTON", L"↑",
WS_CHILD | WS_VISIBLE,
25, 0, 25, 25,
hWnd, (HMENU)IDC_BUTTON_TOPCENTER, hInst, NULL);
CreateWindow(
L"BUTTON", L"┐",
WS_CHILD | WS_VISIBLE,
50, 0, 25, 25,
hWnd, (HMENU)IDC_BUTTON_TOPRIGHT, hInst, NULL);
//
CreateWindow(
L"BUTTON", L"←",
WS_CHILD | WS_VISIBLE,
0, 25, 25, 25,
hWnd, (HMENU)IDC_BUTTON_MIDDLELEFT, hInst, NULL);
CreateWindow(
L"BUTTON", L"┼",
WS_CHILD | WS_VISIBLE,
25, 25, 25, 25,
hWnd, (HMENU)IDC_BUTTON_MIDDLECENTER, hInst, NULL);
CreateWindow(
L"BUTTON", L"→",
WS_CHILD | WS_VISIBLE,
50, 25, 25, 25,
hWnd, (HMENU)IDC_BUTTON_MIDDLERIGHT, hInst, NULL);
//
CreateWindow(
L"BUTTON", L"└",
WS_CHILD | WS_VISIBLE,
0, 50, 25, 25,
hWnd, (HMENU)IDC_BUTTON_BOTTOMLEFT, hInst, NULL);
CreateWindow(
L"BUTTON", L"↓",
WS_CHILD | WS_VISIBLE,
25, 50, 25, 25,
hWnd, (HMENU)IDC_BUTTON_BOTTOMCENTER, hInst, NULL);
CreateWindow(
L"BUTTON", L"┘",
WS_CHILD | WS_VISIBLE,
50, 50, 25, 25,
hWnd, (HMENU)IDC_BUTTON_BOTTOMRIGHT, hInst, NULL);
//デスクトップサイズを取得
GetWindowRect(GetDesktopWindow(), &rtScreen);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON_TOPLEFT: //左上
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_TOPCENTER: //中上
GetWindowRect(hWnd, &rt);
w = rt.right - rt.left;
x = rtScreen.right / 2 - w / 2;
y = 0;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_TOPRIGHT: //右上
GetWindowRect(hWnd, &rt);
w = rt.right - rt.left;
x = rtScreen.right - w;
y = 0;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_MIDDLELEFT: //左中
GetWindowRect(hWnd, &rt);
h = rt.bottom - rt.top;
x = 0;
y = rtScreen.bottom / 2 - h / 2;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_MIDDLECENTER: //中中
GetWindowRect(hWnd, &rt);
w = rt.right - rt.left;
h = rt.bottom - rt.top;
x = rtScreen.right / 2 - w / 2;
y = rtScreen.bottom / 2 - h / 2;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_MIDDLERIGHT: //右中
GetWindowRect(hWnd, &rt);
w = rt.right - rt.left;
h = rt.bottom - rt.top;
x = rtScreen.right - w;
y = rtScreen.bottom / 2 - h / 2;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_BOTTOMLEFT: //左下
GetWindowRect(hWnd, &rt);
h = rt.bottom - rt.top;
x = 0;
y = rtScreen.bottom - h;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_BOTTOMCENTER: //中下
GetWindowRect(hWnd, &rt);
w = rt.right - rt.left;
h = rt.bottom - rt.top;
x = rtScreen.right / 2 - w / 2;
y = rtScreen.bottom - h;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
case IDC_BUTTON_BOTTOMRIGHT: //右下
GetWindowRect(hWnd, &rt);
w = rt.right - rt.left;
h = rt.bottom - rt.top;
x = rtScreen.right - w;
y = rtScreen.bottom - h;
SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
}
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
やたらと長いコードですが、似たような処理をボタン9個分書いているだけです。
左上のボタンを押下すればウィンドウはデスクトップの左上に配置されます。
中央のボタンを押下すれば画面中央に配置されます。
ウィンドウを中央に配置するには、まず配置の基準となるウィンドウ(今回はデスクトップ)の位置とサイズを取得します。
次に自身のウィンドウの位置とサイズを取得します。
ウィンドウの左位置(X座標)は「(親ウィンドウの横幅 / 2) - (自ウィンドウの横幅 / 2)」で求めることができます。
上位置(Y座標)は「(親ウィンドウの縦幅 / 2) - (自ウィンドウの縦幅 / 2)」です。
以下の図を見るとわかりやすいかもしれません。
今回はデスクトップが基準なので座標は(0, 0)ですが、別のウィンドウを基準とする場合は最後に基準ウィンドウの座標をプラスする必要があります。