学习游戏基础编程2:Win32分割窗口

一直很不解分割窗口是什么原理。百度了老长时间发现这方面的资料甚少,但好歹也知道了个大概,今天就做个总结,免得到时候忘记。

当初很傻很天真的时候,真的以为简单只是把一个窗口像玻璃那样劈成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);
}

代码注释不是很全,而且还有很多要补充的地方,如:拖动分割条的限制,离窗口一定距离就不再拖动等

如有问题请留言,或者QQ2878676678 欢迎交流。

学习游戏基础编程2:Win32分割窗口

上一篇:winform C#屏幕右下角弹出消息框并自动消失


下一篇:如何将Excel导入到DataGridView中