簡體   English   中英

WinAPI C ++:重新編程窗口調整大小

[英]WinAPI C++: Reprogramming Window Resize

我有一個窗口,我希望將邊框實現為調整邊框,就像任何其他窗口一樣。 從評論和答案中提出建議,我重寫了我的代碼。 對於WM_GETMINMAXINFO,我有:

MINMAXINFO *min_max = reinterpret_cast<MINMAXINFO *>(lparam);

min_max->ptMinTrackSize.x = MINX;
min_max->ptMinTrackSize.y = MINY;

MINX和MINY是我想要窗口的最小尺寸。 對於WM_NCHITTEST我有:

RECT wnd_rect;
int x, y;

GetWindowRect (window, &wnd_rect);
x = GET_X_LPARAM (lparam) - wnd_rect.left;
y = GET_Y_LPARAM (lparam) - wnd_rect.top;

if (x >= BORDERWIDTH && x <= wnd_rect.right - wnd_rect.left - >BORDERWIDTH && y >= BORDERWIDTH && y <= TITLEBARWIDTH)
    return HTCAPTION;

else if (x < BORDERWIDTH && y < BORDERWIDTH)
    return HTTOPLEFT;
else if (x > wnd_rect.right - wnd_rect.left - BORDERWIDTH && y < BORDERWIDTH)
    return HTTOPRIGHT;
else if (x > wnd_rect.right - wnd_rect.left - BORDERWIDTH && y > wnd_rect.bottom - wnd_rect.top - BORDERWIDTH)
    return HTBOTTOMRIGHT;
else if (x < BORDERWIDTH && y > wnd_rect.bottom - wnd_rect.top - BORDERWIDTH)
    return HTBOTTOMLEFT;

else if (x < BORDERWIDTH)
    return HTLEFT;
else if (y < BORDERWIDTH)
    return HTTOP;
else if (x > wnd_rect.right - wnd_rect.left - BORDERWIDTH)
    return HTRIGHT;
else if (y > wnd_rect.bottom - wnd_rect.top - BORDERWIDTH)
    return HTBOTTOM;

return HTCLIENT;

這些變量非常明顯。 這段代碼給了我一個邊框,我可以拖動它來調整窗口大小。 拖動右下角,右下角和右邊框時效果很好。 對於其他邊框,當我嘗試拖動它們時,窗口的右下角似乎仍然來回移動。 它類似於谷歌Chrome或Visual Studio 2012中使用相同的邊框集,但我在Windows資源管理器中看不到這一點。

當我調整頂部或左邊框時,有沒有辦法讓右下角不來回“蠕動”,就像在Windows資源管理器中一樣?

我知道這有點晚了,但我想我找到了一種不用“蠕動”調整大小的方法(內部窗口繪制滯后仍然存在)。

與manuell所說的不同, WM_NCCALCSIZE是萬惡之源。 此方法也應該適用於任何窗口樣式(使用WS_POPUPWS_OVERLAPPEDWINDOW進行測試),同時保留它們的功能,因此我應該關閉並發布帶有注釋的代碼:

//some sizing border definitions

#define MINX 200
#define MINY 200
#define BORDERWIDTH  5
#define TITLEBARWIDTH  30

//................

HWND TempHwnd = Create(NULL, TEXT("CUSTOM BORDER"), TEXT("CUSTOM BORDER"),
               WS_POPUP | WS_VISIBLE,
               100, 100, 400, 400, NULL, NULL, 
                       GetModuleHandle(NULL), NULL);

//...............

LRESULT CALLBACK WinMsgHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_SIZING: // I use this message to redraw window on sizing (o rly?)
            RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_NOERASE | RDW_INTERNALPAINT);
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
        case WM_PAINT: // Used to draw borders and stuff to test WM_NCHITTEST
            {
                PAINTSTRUCT ps;
                BeginPaint(hWnd, &ps);

                RECT ClientRect;
                GetClientRect(hWnd, &ClientRect);
                RECT BorderRect = { BORDERWIDTH, BORDERWIDTH, ClientRect.right - BORDERWIDTH - BORDERWIDTH, ClientRect.bottom - BORDERWIDTH - BORDERWIDTH },
                     TitleRect = { BORDERWIDTH, BORDERWIDTH, ClientRect.right - BORDERWIDTH - BORDERWIDTH, TITLEBARWIDTH };

                HBRUSH BorderBrush = CreateSolidBrush(0x0000ff);
                FillRect(ps.hdc, &ClientRect, BorderBrush);
                FillRect(ps.hdc, &BorderRect, GetSysColorBrush(2));
                FillRect(ps.hdc, &TitleRect, GetSysColorBrush(1));
                DeleteObject(BorderBrush);

                EndPaint(hWnd, &ps);
            }
            break;
        case WM_GETMINMAXINFO: // It is used to restrict WS_POPUP window size
            {              // I don't know if this works on others
                MINMAXINFO *min_max = reinterpret_cast<MINMAXINFO *>(lParam);

                min_max->ptMinTrackSize.x = MINX;
                min_max->ptMinTrackSize.y = MINY;
            }
            break;
        case WM_CREATE:  // In this message we use MoveWindow to invoke
            {        //WM_NCCALCSIZE msg to remove border
                CREATESTRUCT *WindowInfo = reinterpret_cast<CREATESTRUCT *>(lParam);
                MoveWindow(hWnd, WindowInfo->x, WindowInfo->y, WindowInfo->cx - BORDERWIDTH, WindowInfo->cy - BORDERWIDTH, TRUE); 
//Notice that "- BORDERWIDTH" is recommended on every manually called resize function,
//Because we will add BORDERWIDTH value in WM_NCCALCSIZE message
            }
            break;
        case WM_NCCALCSIZE:
            { // Microsoft mentioned that if wParam is true, returning 0 should be enough, but after MoveWindow or similar functions it would begin to "wriggle"
                if (wParam)
                {
                    NCCALCSIZE_PARAMS *Params = reinterpret_cast<NCCALCSIZE_PARAMS *>(lParam);
                    Params->rgrc[0].bottom += BORDERWIDTH; // rgrc[0] is what makes this work, don't know what others (rgrc[1], rgrc[2]) do, but why not change them all?
                    Params->rgrc[0].right += BORDERWIDTH;
                    Params->rgrc[1].bottom += BORDERWIDTH;
                    Params->rgrc[1].right += BORDERWIDTH;
                    Params->rgrc[2].bottom += BORDERWIDTH;
                    Params->rgrc[2].right += BORDERWIDTH;
                    return 0;
                }
                return DefWindowProc(hWnd, uMsg, wParam, lParam);
            }
            break;
        case WM_NCHITTEST:
            {
                RECT WindowRect;
                int x, y;

                GetWindowRect(hWnd, &WindowRect);
                x = GET_X_LPARAM(lParam) - WindowRect.left;
                y = GET_Y_LPARAM(lParam) - WindowRect.top;

                if (x >= BORDERWIDTH && x <= WindowRect.right - WindowRect.left - BORDERWIDTH && y >= BORDERWIDTH && y <= TITLEBARWIDTH)
                    return HTCAPTION;
                else if (x < BORDERWIDTH && y < BORDERWIDTH)
                    return HTTOPLEFT;
                else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH && y < BORDERWIDTH)
                    return HTTOPRIGHT;
                else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - BORDERWIDTH)
                    return HTBOTTOMRIGHT;
                else if (x < BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - BORDERWIDTH)
                    return HTBOTTOMLEFT;
                else if (x < BORDERWIDTH)
                    return HTLEFT;
                else if (y < BORDERWIDTH)
                    return HTTOP;
                else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH)
                    return HTRIGHT;
                else if (y > WindowRect.bottom - WindowRect.top - BORDERWIDTH)
                    return HTBOTTOM;
                else
                    return HTCLIENT;
            }
            break;
        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
        }
        return 0;
    }

這種代碼匆忙變得丑陋,你通過改變客戶區位置來改變相對鼠標位置。 這要求您在窗口太小時忽略鼠標移動時更新* track_start *變量。 不這樣做會產生一種諷刺,有趣的效果,窗戶來回跳躍。 是的,“扭動”。

只是不要這樣做,你正在尋找的功能已經實現。 WM_GETMINMAXINFO編寫消息處理程序。 首先調用DefWindowProc(),然后覆蓋MINMAXINFO.ptMinTrackSize值。 如果目的是在無邊框窗口上實現邊角或邊緣拖動,則為WM_NCHITTEST實現消息處理程序。 這也允許實現你的BORDERWIDTH。 相同的配方,首先調用DefWindowProc(),在適當時覆蓋返回值。

唉,這不是你要等的答案。 在Windows 7上,同時移動和調整大小與WS_POPUP樣式的頂級窗口確實被破壞了。 在視覺上,首先移動窗口,然后調整大小。 通過左側或頂部調整大小時,移動操作會短暫顯示背景像素,從而導致非常糟糕的用戶體驗。

據我了解發生了什么,它與WM_GETMINMAXINFO或WM_NCCALCSIZE無關。

看到效果非常簡單:創建一個WS_POPUP | WS_VISIBLE窗口帶有一個幾乎為空的窗口程序,設置一個計時器並在WM_TIMER中使用SetWindowPos,將窗口稍微向左移動,同時調整它的大小,以便讓右邊緣在同一個地方。 你會看到背景像素,這是愚蠢的。 在Windows XP上沒有這樣的破壞。

我嘗試了很多技巧,其中一些非常扭曲,但最終結果總是一樣的:當窗口最終呈現在新狀態時,首先是一個移動操作,然后是一個尺寸...

你有兩個選擇(如果你的目標是七+):

1)使用標准大小邊框並利用新API(例如:DwmExtendFrameIntoClientArea)自定義框架以滿足您的需求。 請參閱使用DWM自定義窗口框架, 網址http://msdn.microsoft.com/en-us/library/windows/desktop/bb688195.aspx

2)不要使用WS_POPUP,而是使用WS_BORDER並使用欺騙Windows的技巧來永久渲染邊框。 這似乎是VS2012正在做的事情。

不要忘記:閃爍在窗口內是另一個故事,我只是在談論右邊/底邊“穩定”這里。

看到正在改變窗口大小和位置的代碼會很有幫助。

當您移動底部或右側時,您只需更改窗口的大小(高度或寬度)。 移動頂部或左側時,您不僅要更改尺寸,還要更改上/左角位置。

如果有人想要將左邊框向右移動10個像素,則必須將角位置增加10並將寬度減小10 - 最好相同(例如,同時使用SetWindowPos進行兩次更改)。

請注意,更改該角位置也會更改鼠標的屏幕坐標的解釋方式。 因此,任何舊倉位的存儲也必須更新。

您只需處理WM_NCCALCSIZE消息,使用邊框寬度增加左邊的rgrc矩形,並使用CaptionBar高度增加頂部,使用邊框寬度減少右邊,使用CaptionBar高度減小底部。 對於邊界角,您應該在WM_SIZE消息上更改窗口區域。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM