2002/11/27
境界線の下の方にマウスカーソルを持っていくと上下矢印になりそこでサイズを変更しようとするとおかしくなるとの御指摘がありバグフィックスしました。
WM_SETCURSORとWM_LBUTTONDOWNに変更が加わっています。
新しいサンプルファイル
を実行すると

こんな風になります。
古い(バグあり)サンプルファイルです
| メインウインドウ | |
| 左側のベースウインドウ | 右側のベースウインドウ |
| 左側の子ウインドウ | 右側の子ウインドウ |
関数が成功すると、構造体にベースウインドウのハンドルとその上に子ウインドウを作る場合の大きさが返ってくるので、それらを使って子ウインドウを作ります。
WM_SIZEでは、メインウインドウの大きさにあわせてベースウインドウの大きさも変えます。
int CALLBACK WndProc(HWND hWnd, unsigned wMessage,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
BASEINFO BaseInfo;
switch (wMessage)
{
case WM_CREATE:
CenterWindow(hWnd);
//ベースウインドウを作る
if(CreateSplitBase(hWnd,30,&BaseInfo)==FALSE) return -1;
//左側の子ウインドウを作る
if(CreateChild(BaseInfo.hLeft,&BaseInfo.rcLeft)==NULL) return -1;
//右側の子ウインドウを作る
if(CreateChild(BaseInfo.hRight,&BaseInfo.rcRight)==NULL) return -1;
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SIZE:
//サイズの変更
SetSplitSize(hWnd);
return 0;
case WM_PAINT:
hdc = BeginPaint (hWnd, &ps);
EndPaint (hWnd, &ps);
return 0;
}
return (DefWindowProc(hWnd, wMessage, wParam, lParam));
}
//初期化
BOOL InitSplitWindow(void)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = (WNDPROC) SplitBaseProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = Instance;
wndclass.hIcon = NULL;
wndclass.hCursor = NULL;
wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szSplitClassName;
if (!RegisterClass (&wndclass)) return FALSE;
return TRUE;
}
//ベースウインドウを作る
BOOL CreateSplitBase(HWND hParent,int Rate,LPBASEINFO lpBaseInfo)
{
HWND hWndLeft,hWndRight;
RECT rc;
int LeftWidth,RightWidth,Height;
GetClientRect(hParent,&rc);
LeftWidth=rc.right*Rate/100;
RightWidth=rc.right-LeftWidth;
Height=rc.bottom;
//左側のベースウインドウ作成
hWndLeft = CreateWindowEx( 0,
szSplitClassName,
szSplitAppName,
WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
0,
0,
LeftWidth,
Height,
hParent,
NULL,
Instance,
NULL);
if(hWndLeft==NULL) return FALSE;
//右側のベースウインドウ作成
hWndRight = CreateWindowEx( 0,
szSplitClassName,
szSplitAppName,
WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
LeftWidth,
0,
RightWidth,
Height,
hParent,
NULL,
Instance,
NULL);
if(hWndRight==NULL) return FALSE;
//ベースウインドウ情報を設定
ZeroMemory(&g_LeftInfo,sizeof(g_LeftInfo));
g_LeftInfo.hWnd=hWndLeft;
g_LeftInfo.hParent=hParent;
g_LeftInfo.hRight=hWndRight;
SetWindowLong(hWndLeft,GWL_USERDATA,(LONG)&g_LeftInfo);
//ベースウインドウ情報を設定
ZeroMemory(&g_RightInfo,sizeof(g_RightInfo));
g_RightInfo.hWnd=hWndRight;
g_RightInfo.hParent=hParent;
SetWindowLong(hWndRight,GWL_USERDATA,(LONG)&g_RightInfo);
//呼び出したプログラムに、ウインドウハンドルと子ウインドウの大きさを返す
lpBaseInfo->hLeft=hWndLeft;
lpBaseInfo->rcLeft.left=0;
lpBaseInfo->rcLeft.right=LeftWidth-BORDERWIDTH;
lpBaseInfo->rcLeft.top=0;
lpBaseInfo->rcLeft.bottom=rc.bottom;
lpBaseInfo->hRight=hWndRight;
lpBaseInfo->rcRight.left=0;
lpBaseInfo->rcRight.right=RightWidth;
lpBaseInfo->rcRight.top=0;
lpBaseInfo->rcRight.bottom=rc.bottom;
return TRUE;
}
//子ウインドウをベースウインドウに関連づける
void SetChildToBase(HWND hBase,HWND hChild)
{
LPBWNDINFO lpBWndInfo;
lpBWndInfo=(LPBWNDINFO)GetWindowLong(hBase,GWL_USERDATA);
lpBWndInfo->hChild=hChild;
}
//全体の大きさを変更する
void SetSplitSize(HWND hParent)
{
RECT rc,rc2;
int LeftWidth,RightWidth,Height;
GetClientRect(hParent,&rc);
GetWindowRect(g_LeftInfo.hWnd,&rc2);
LeftWidth=rc2.right-rc2.left;
RightWidth=rc.right-LeftWidth;
Height=rc.bottom-rc.top;
if(RightWidth<0) RightWidth=0;
SetWindowPos(g_LeftInfo.hWnd,NULL,0,0,LeftWidth,Height,SWP_NOZORDER);
SetWindowPos(g_RightInfo.hWnd,NULL,LeftWidth,0,RightWidth,Height,SWP_NOZORDER);
}
case WM_CREATE:
return 0;
case WM_DESTROY:
return 0;
WM_NCHITTESTを細工しても同じ事が出来ると思いますが、今回はこうしました。
case WM_SETCURSOR:
//マウスカーソルの形を左右矢印か上下矢印のどちらかにする
GetCursorPos(&pt);
GetClientRect(hWnd,&rc);
ScreenToClient(hWnd,&pt);
//2002/11/27 左右境界線の下端で上下矢印になってしまうバグを修正
//下側にベースあり&カーソルがウインドウの下部 → 上下矢印
//左にベースウインドウがある → 左右矢印にする
//それ以外は標準カーソル
if(lpBWndInfo->hDown && pt.y>=rc.bottom-BORDERWIDTH) Cursor=IDC_SIZENS; //上下矢印
else if(lpBWndInfo->hRight) Cursor=IDC_SIZEWE; //左右矢印
else Cursor=IDC_ARROW; //標準
SetCursor(LoadCursor(NULL,Cursor)); //カーソルの設定
return 0;
case WM_SIZE:
if(lpBWndInfo){
//子ウインドウのサイズを決めます
x=0;
y=0;
cx=LOWORD(lParam);
cy=HIWORD(lParam);
//境界線が必要ならその分の長さを引く
if(lpBWndInfo->hRight) cx-=BORDERWIDTH;
if(lpBWndInfo->hDown) cy-=BORDERWIDTH;
//子ウインドウのサイズを変更する
if(lpBWndInfo->hChild) SetWindowPos(lpBWndInfo->hChild,NULL,x,y,cx,cy,SWP_NOZORDER);
}
return 0;
case WM_PAINT:
hdc = BeginPaint (hWnd, &ps);
EndPaint (hWnd, &ps);
return 0;
高さを変更しようとしているのか幅を変更しようとしているのかを調べて、モードをs_Capture変数に保存しておきます。また、境界線には幅があるので、境界線のどの当たりでクリックされたのかを境界線の右端または下端からの差で保存しておきます。
case WM_LBUTTONDOWN:
//2002/11/27 左右境界線の下端でサイズ変更するとおかしくなるバグを修正
//境界がクリックされたら、サイズ変更モードに移行します
GetClientRect(hWnd,&rc);
if(lpBWndInfo->hDown && MAKEPOINTS(lParam).y>=rc.bottom-BORDERWIDTH){ //高さ変更モード
s_Capture=CT_TATE;
s_Gap=rc.bottom-MAKEPOINTS(lParam).y;
}else if(lpBWndInfo->hRight){ //幅変更モード
s_Capture=CT_YOKO;
s_Gap=rc.right-MAKEPOINTS(lParam).x;
}else{
break; //何もしない
}
SetCapture(hWnd);
break;
case WM_LBUTTONUP:
//サイズ変更モードの終了
if(s_Capture){
ReleaseCapture();
s_Capture=0;
}
break;
2つのウインドウサイズが計算できたら、SetWindowPosでサイズの更新をするのですが、そのままだとちらついてしまうので、LockWindowUpdateを使ってウインドウの更新をロックしておきます。
case WM_MOUSEMOVE:
//サイズ変更モードになっていた場合、ベースウインドウのサイズを変更する
if(s_Capture){
//ベースウインドウの大きさを取得
GetWindowRect(hWnd,&rc);
pt.x=rc.left;
pt.y=rc.top;
//ベースウインドウの親ウインドウ内での座標に変換
ScreenToClient(lpBWndInfo->hParent,&pt);
if(s_Capture==CT_TATE){ //高さ変更モード時
//上側のウインドウのサイズを計算
x=pt.x;
y=pt.y;
cx=rc.right-rc.left;
cy=MAKEPOINTS(lParam).y+s_Gap;
if(cy<MINWIDTH) cy=MINWIDTH;
//下側のウインドウの大きさを計算
hWnd2=lpBWndInfo->hDown;
GetWindowRect(hWnd2,&rc2);
cx2=cx;
cy2=rc2.bottom-rc.top-cy; //2つのウインドウの高さを足したものから、
if(cy2<MINWIDTH){ //1つ目のウインドウの高さを引く
cy2=MINWIDTH;
cy=rc2.bottom-rc.top-cy2;
if(cy<MINWIDTH) cy=MINWIDTH;
}
x2=x;
y2=y+cy;
}else{ //幅変更モード時
//左側のウインドウのサイズを計算
x=pt.x;
y=pt.y;
cx=MAKEPOINTS(lParam).x+s_Gap;
cy=rc.bottom-rc.top;
if(cx<MINWIDTH) cx=MINWIDTH;
//右側のウインドウのサイズを計算
hWnd2=lpBWndInfo->hRight;
GetWindowRect(hWnd2,&rc2);
cx2=rc2.right-rc.left-cx; //2つのウインドウの幅を足したものから、
cy2=cy; //1つ目のウインドウの幅を引く
if(cx2<MINWIDTH){
cx2=MINWIDTH;
cx=rc2.right-rc.left-cx2;
if(cx<MINWIDTH) cx=MINWIDTH;
}
x2=x+cx;
y2=y;
}
//ちらつき防止のために、ウインドウの更新を一時ストップ
LockWindowUpdate(lpBWndInfo->hParent);
SetWindowPos(hWnd,NULL,x,y,cx,cy,SWP_NOZORDER);
SetWindowPos(hWnd2,NULL,x2,y2,cx2,cy2,SWP_NOZORDER);
//更新再開
LockWindowUpdate(NULL);
//再開しただけでは書き直されないので、再描画
UpdateWindow(lpBWndInfo->hParent);
}
break;