一直很不解分割窗口是什么原理。百度了老长时间发现这方面的资料甚少,但好歹也知道了个大概,今天就做个总结,免得到时候忘记。
当初很傻很天真的时候,真的以为简单只是把一个窗口像玻璃那样劈成2半,各做各的,其实是父窗口下有数个子窗口,父窗口被这些子窗口所遮挡,看起来就像是窗口分成好几个区域。因此第一步我们要创建好几个窗口,并调整好它们的位置。
HWND hwndMain;//主窗口
HWND hwndEdit;//编辑区域窗口HWND hwndPreview;//预览图区域窗口HWND hwndTitleimg;//显示图片块儿窗口在WinMain()里hwndMain=CreateWindow();
在WindowProc()里的WM_CREATE里,分别hwndEdit/hwndPreview/hwndTitleimg=CreateWindow();在WM_SIZE里,MoveWindow()到预定的位置。
之后你就会看到左侧的hwndEdit窗口,右侧上方的hwndPreview窗口,下方的hwndTitleimg窗口。
第二步,鼠标按在子窗口空隙形成的分割线(条)的时候拖动,子窗口能任意调整彼此之间的大小。怎么做呢?简单的一种做法就是,在鼠标拖动的时候画出一条矩形阴影来模拟,等鼠标释放时重新调整大小,也就是WM_SIZE里根据鼠标的位置设置3个子窗口的大小。复杂点的做法,就是分割条也当作一个子窗口CreateWindow()出来,不过有点麻烦,暂且不提。
下面给出完整代码:
//全局变量
static TCHAR szAppName[]=TEXT("Win32分割窗口");
char szClassName[ ] = TEXT("WindowsApp");
HINSTANCE hInst;
HWND hwndMain;
HWND hwndEdit;HWND hwndPreview;HWND hwndTitleimg;static int CLIENT_W = 800;//主窗口客户区宽static int CLIENT_H = 600;//主窗口客户区高static RECT cliRect = {0,0,CLIENT_W,CLIENT_H};//主窗口客户区矩形位置static int spX=550;//水平分割条初始位置static int spY=150;//垂直分隔条初始位置static int spBar=5;//分隔条的厚度static RECT rectSpVer={spX,0,spX+spBar,CLIENT_H};//垂直分隔条矩形位置static RECT rectSpHor={spX+spBar,spY,CLIENT_W,spY+spBar};//水平分隔条矩形位置bool fDragVer=false;bool fMoveVer=false;//垂直分隔条是否按下/拖动bool fDragHor=false;bool fMoveHor=false;//水平分隔条是否按下/拖动POINT pold;//注意 保存旧的鼠标POINT位置 这是一个要点 并没有WM_PAINT重画,所以我们要消除之前绘制的阴影,利用PATINVERT方式重新绘制一次//全局函数
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);//窗口过程函数
void InitHwndChild(HWND hwnd);//初始化三个子窗口所用 在WM_CREATE处被调用void SizeWindowContents(int nWidth, int nHeight);//调整三个子窗口size所用 在WM_SIZE处调用 参数为客户区宽高void Sp_LBDown(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//分隔条区域鼠标按下 WM_LBUTTONDOWN处调用void Sp_LBUp(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//分隔条区域鼠标释放 WM_LBUTTONUP处调用void Sp_MouseMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//分隔条区域鼠标移动 WM_MOUSEMOVE处调用void DrawSpBar(LPRECT lpRect);//绘制模拟分隔条的阴影 参数为要绘制的分隔条矩形位置LPRECT GetVerSpBar();//根据鼠标位置求得新的垂直分割矩形 用于绘制分割条时LPRECT GetHorSpBar();//根据鼠标位置求得新的水平分割矩形 用于绘制分隔条时void SetWndCenter(HWND hwnd);//根据客户区大小设置主窗口大小并居中
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow)
{ HWND hwnd; /* This is the handle for our window */MSG msg;
WNDCLASSEX wndClass; wndClass.cbSize=sizeof(WNDCLASSEX); wndClass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS; wndClass.lpfnWndProc=WindowProcedure; wndClass.cbClsExtra=0; wndClass.cbWndExtra=0; wndClass.hInstance=hInstance; wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION); wndClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wndClass.hCursor=LoadCursor(NULL,IDC_ARROW); wndClass.hbrBackground=(HBRUSH)COLOR_BACKGROUND; wndClass.lpszMenuName=NULL; wndClass.lpszClassName=szAppName; if(!RegisterClassEx(&wndClass)) { MessageBox(NULL,TEXT("error"),szAppName,MB_ICONERROR|MB_OK); return 0; } hInst=hInstance; hwndMain=CreateWindowEx(0,szAppName,TEXT("The hello program"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL); SetWndCenter(hwndMain);//设置窗口客户区大小并居中 ShowWindow(hwndMain,iCmdShow); UpdateWindow(hwndMain); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0;}LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ HDC hdc; PAINTSTRUCT ps; switch (message) /* handle the messages */ { case WM_CREATE: InitHwndChild(hwnd);//init three HWND child break; case WM_SIZE: SizeWindowContents(LOWORD(lParam), HIWORD(lParam));//set three child window positions break; case WM_LBUTTONDOWN: Sp_LBDown(hwnd,message,wParam,lParam); break; case WM_LBUTTONUP: Sp_LBUp(hwnd,message,wParam,lParam); break; case WM_MOUSEMOVE: Sp_MouseMove(hwnd,message,wParam,lParam); break; case WM_PAINT: hdc=BeginPaint(hwnd,&ps); EndPaint(hwnd,&ps); break; case WM_DESTROY: PostQuitMessage (0); /* send a WM_QUIT to the message queue */ break; default: /* for messages that we don't deal with */ return DefWindowProc (hwnd, message, wParam, lParam); }return 0;
}void InitHwndChild(HWND hwnd){ hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "hwndEdit", WS_VISIBLE|WS_CHILD, 0,0,0,0,hwnd, 0, hInst, 0); //SetWindowLongPtr(hwndEdit,GWLP_WNDPROC,(LONG)hwndEditProc); hwndPreview = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "hwndPreview", WS_VISIBLE|WS_CHILD, 0,0,0,0,hwnd, 0, hInst, 0); hwndTitleimg = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "hwndTitleimg", WS_VISIBLE|WS_CHILD, 0,0,0,0,hwnd, 0, hInst, 0);}void SizeWindowContents(int nWidth, int nHeight){ MoveWindow(hwndEdit, 0, 0, spX, nHeight, true); MoveWindow(hwndPreview, spX+spBar, 0, nWidth-spX-spBar, spY, true); MoveWindow(hwndTitleimg, spX+spBar, spY+spBar, nWidth-spX-spBar, nWidth-spY-spBar, true);}void Sp_LBDown(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ SetCapture(hwndMain); POINT p; p.x=LOWORD(lParam);p.y=HIWORD(lParam); if((fDragVer=PtInRect(GetVerSpBar(),p))) {DrawSpBar(GetVerSpBar());pold.x=spX;pold.y=spY;return;} if((fDragHor=PtInRect(GetHorSpBar(),p))) {DrawSpBar(GetHorSpBar());pold.x=spX;pold.y=spY;return;} //if(fDragVer||fDragVer) MessageBox(hwnd,"asd","asd",MB_OK);}void Sp_LBUp(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ POINT p;p.x=LOWORD(lParam);p.y=HIWORD(lParam); RECT rect;GetClientRect(hwnd,&rect); if(fDragVer) { DrawSpBar(GetVerSpBar());fDragVer=fMoveVer=false; SizeWindowContents(rect.right, rect.bottom);ReleaseCapture();return; } if(fDragHor) { DrawSpBar(GetHorSpBar());fDragHor=fMoveHor=false; SizeWindowContents(rect.right, rect.bottom);ReleaseCapture();return; }}void Sp_MouseMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ POINT p; p.x=LOWORD(lParam);p.y=HIWORD(lParam); if(wParam & MK_LBUTTON)//only handle mouse LeftButton drag and moved { if(fDragVer) { fMoveVer=true; spX=pold.x;DrawSpBar(GetVerSpBar()); spX=p.x;DrawSpBar(GetVerSpBar()); pold=p;return; } if(fDragHor) { fMoveHor=true; spY=pold.y;DrawSpBar(GetHorSpBar()); spY=p.y;DrawSpBar(GetHorSpBar()); pold=p;return; } }}void DrawSpBar(LPRECT lpRect){ //InvalidateRect(hwndMain,NULL,false); HDC hdc; hdc = GetDC(hwndMain); static WORD _dotPatternBmp[8] = { 0x00aa, 0x0055, 0x00aa, 0x0055, 0x00aa, 0x0055, 0x00aa, 0x0055 };HBITMAP hbm;
HBRUSH hbr, hbrushOld;hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);
hbr = CreatePatternBrush(hbm);SetBrushOrgEx(hdc, lpRect->left, lpRect->top, 0);
hbrushOld = (HBRUSH)SelectObject(hdc, hbr);PatBlt(hdc, lpRect->left, lpRect->top, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top, PATINVERT);
SelectObject(hdc, hbrushOld);DeleteObject(hbr);
DeleteObject(hbm); ReleaseDC(hwndMain,hdc); }LPRECT GetVerSpBar(){ rectSpVer.left=spX; rectSpVer.top=0; rectSpVer.right=spX+spBar; rectSpVer.bottom=CLIENT_H; return &rectSpVer;}LPRECT GetHorSpBar(){ rectSpHor.left=spX+spBar; rectSpHor.top=spY; rectSpHor.right=CLIENT_W; rectSpHor.bottom=spY+spBar; return &rectSpHor;}void SetWndCenter(HWND hwnd)
{ AdjustWindowRect(&cliRect,WS_OVERLAPPEDWINDOW,false); int WIN_W = cliRect.right - cliRect.left; int WIN_H = cliRect.bottom - cliRect.top; cliRect.left = (GetSystemMetrics(SM_CXSCREEN)-WIN_W)/2; cliRect.top = (GetSystemMetrics(SM_CYSCREEN)-WIN_H)/2; cliRect.right = cliRect.left + WIN_W; cliRect.bottom = cliRect.top + WIN_H; SetWindowPos(hwnd,HWND_TOPMOST,cliRect.left,cliRect.top,WIN_W,WIN_H, SWP_SHOWWINDOW);}代码注释不是很全,而且还有很多要补充的地方,如:拖动分割条的限制,离窗口一定距离就不再拖动等
如有问题请留言,欢迎交流。