강좌와 팁

WS_POPUP과 WS_THICKFRAME 스타일 날짜:2021-8-7 10:28:56 조회수:52
작성자 : 소년가장
포인트 : 418
가입일 : 2020-02-02 00:50:03
방문횟수 : 50
글 40개, 댓글 27개
소개 : 자기소개
작성글 보기
쪽지 보내기
커스텀 윈도우를 만들려고 두 속성을 같이 줘 봤는데 알 수 없는 이상한 문제가 생겼다.

전에는 안 그랬던 거 같은데 팝업을 두꺼운 경계선으로 만들면 위쪽에 흰색 바가 표시된다.

또한 작업 영역의 크기가 경계선에 따라 줄어 드는 문제가 있다. 

왜 그런지 예제를 만들어 보았다. 

#include

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;
LPCTSTR lpszClass = TEXT("PopupWnd");

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
    , LPSTR lpszCmdParam, int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst = hInstance;

    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;
    WndClass.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
    WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    WndClass.hInstance = hInstance;
    WndClass.lpfnWndProc = WndProc;
    WndClass.lpszClassName = lpszClass;
    WndClass.lpszMenuName = NULL;
    WndClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&WndClass);

    // 이 두 스타일을 주면 위쪽에 흰 바가 생긴다. 
    hWnd = CreateWindow(lpszClass, lpszClass, WS_POPUP | WS_VISIBLE,
        1000, 500, 100, 100,
        NULL, (HMENU)NULL, hInstance, NULL);
    CreateWindow(lpszClass, lpszClass, WS_POPUP | WS_BORDER | WS_VISIBLE,
        1110, 500, 100, 100,
        NULL, (HMENU)NULL, hInstance, NULL);
    CreateWindow(lpszClass, lpszClass, WS_POPUP | WS_THICKFRAME | WS_VISIBLE,
        1220, 500, 100, 100,
        NULL, (HMENU)NULL, hInstance, NULL);
    ShowWindow(hWnd, nCmdShow);
    hWndMain = hWnd;

    while (GetMessage(&Message, NULL, 0, 0)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    UINT nHit;
    TCHAR Mes[128];

    switch (iMessage) {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        SetBkMode(hdc, TRANSPARENT);
        RECT wrt;
        GetWindowRect(hWnd, &wrt);
        wsprintf(Mes, TEXT("W:%d,%d"), wrt.right - wrt.left, wrt.bottom - wrt.top);
        TextOut(hdc, 10, 10, Mes, lstrlen(Mes));
        RECT crt;
        GetClientRect(hWnd, &crt);
        wsprintf(Mes, TEXT("C:%d,%d"), crt.right, crt.bottom);
        TextOut(hdc, 10, 30, Mes, lstrlen(Mes));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_NCHITTEST:
        nHit = DefWindowProc(hWnd, iMessage, wParam, lParam);
        if (nHit == HTCLIENT)
            nHit = HTCAPTION;
        return nHit;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

팝업 윈도우 3개를 스타일 바꿔 가며 100 * 100 크기로 출력했는데 결과는 다음과 같다. 



윈도우 크기는 당연히 100 * 100이고 경계선이 없으면 작업 영역도 똑같다. 

WS_BORDER 스타일을 주면 상하좌우로 1픽셀씩 경계선이 차지하여 작업 영역은 2픽셀씩 줄어 든다.

그런데 WS_THICKFRAME을 주면 14픽셀씩 줄어들 뿐만 아니라 타이틀 바 위쪽에 흰색 바가 생긴다.

윈도우 바깥쪽에 그림자를 까느라 작업영역이 지나치게 축소되고 불필요한 줄까지 그어진다. 

문제는 전 버전에서는 이러지 않았다는 것이다.

윈도우 버전이나 테마 상관없이 일정한 크기를 확보하려면 WS_THICKFRAME 스타일은 쓰지 말아야 한다.

WS_POPUP을 쓰면서 크기 변경도 가능하려면 결국 직접 코딩하는 수밖에 없다. 

평이하게 WS_POPUP 스타일만 주고 100 * 100 크기로 윈도우를 생성한다. 

    hWnd = CreateWindow(lpszClass, lpszClass, WS_POPUP | WS_VISIBLE,
        1000, 500, 100, 100,
        NULL, (HMENU)NULL, hInstance, NULL);
    ShowWindow(hWnd, nCmdShow);

.....

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    UINT nHit;
    TCHAR Mes[128];
    const int borderThick = 2;
    HPEN gray, darkgray, oldPen;

    switch (iMessage) {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        SetBkMode(hdc, TRANSPARENT);
        RECT wrt;
        GetWindowRect(hWnd, &wrt);
        RECT crt;
        GetClientRect(hWnd, &crt);
        // 경계선을 입맛대로 직접 그린다.
        // Rectangle로 그리면 깔끔하지 못하다.
        gray = CreatePen(PS_SOLID, 1, RGB(0xf0, 0xf0, 0xf0));
        darkgray = CreatePen(PS_SOLID, 1, RGB(0xa0, 0xa0, 0xa0));
        oldPen = (HPEN)SelectObject(hdc, gray);
        MoveToEx(hdc, 0, 0, NULL); LineTo(hdc, crt.right, 0);
        MoveToEx(hdc, 0, 0, NULL); LineTo(hdc, 0, crt.bottom);
        MoveToEx(hdc, crt.right - 2, 0, NULL); LineTo(hdc, crt.right - 2, crt.bottom - 1);
        MoveToEx(hdc, 1, crt.bottom - 2, NULL); LineTo(hdc, crt.right - 2, crt.bottom - 2);
        SelectObject(hdc, darkgray);
        MoveToEx(hdc, 1, 1, NULL); LineTo(hdc, crt.right - 2, 1);
        MoveToEx(hdc, 1, 1, NULL); LineTo(hdc, 1, crt.bottom - 2);
        MoveToEx(hdc, crt.right - 1, 0, NULL); LineTo(hdc, crt.right - 1, crt.bottom - 1);
        MoveToEx(hdc, 0, crt.bottom - 1, NULL); LineTo(hdc, crt.right - 1, crt.bottom - 1);
        SelectObject(hdc, oldPen);
        DeleteObject(gray);
        DeleteObject(darkgray);

        wsprintf(Mes, TEXT("W:%d,%d"), wrt.right - wrt.left, wrt.bottom - wrt.top);
        TextOut(hdc, 10, 10, Mes, lstrlen(Mes));
        wsprintf(Mes, TEXT("C:%d,%d"), crt.right, crt.bottom);
        TextOut(hdc, 10, 30, Mes, lstrlen(Mes));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_NCHITTEST:
        nHit = DefWindowProc(hWnd, iMessage, wParam, lParam);
        if (nHit == HTCLIENT) {
            RECT crt;
            POINT mpt;
            GetClientRect(hWnd, &crt);
            mpt.x = LOWORD(lParam);
            mpt.y = HIWORD(lParam);
            ScreenToClient(hWnd, &mpt);

            // 상하 좌우변에 커서가 있으면 크기를 조정한다.
            if (mpt.x < borderThick)
                return HTLEFT;
            if (mpt.x > crt.right - 1 - borderThick)
                return HTRIGHT;
            if (mpt.y < borderThick)
                return HTTOP;
            if (mpt.y > crt.bottom - 1 - borderThick)
                return HTBOTTOM;
        }
        // 그 외의 영역은 윈도우를 이동한다.
        nHit = HTCAPTION;
        return nHit;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

WM_PAINT에서 경계선을 직접 그린다. 위 왼쪽은 약간 밝게, 아래 오른쪽은 약간 어둡게 하여 입체감을 주었다.

이건 Rectangle로도 그릴 수 있지만 겹치는 부분이 깨지는 문제가 있어 직선으로 표현했다. 

또 ES_EX_CLIENTEDGE 확장 스타일로 비슷한 효과를 낼 수 있지만 이것도 테마나 버전에 따라 달라질 수 있어

안전하지 않다. 그냥 그리는게 제일 속편하다. 

WM_NCHITTEST에서 커서의 위치에 따라 경계선을 판별하여 커서 방향을 지정하면 크기 조정이 가능하다.

모서리 부분도 처리할 수 있지만 굳이 그렇게까지 하지는 않았다. 

경계선 이외의 부분은 윈도우를 이동하는 것으로 처리했다. 

이 방식대로 하면 커스텀 윈도우를 쉽게 만들 수 있고 자유도가 높다. 






 



오늘도 최선을 다 하자.

목록보기 삭제 수정 신고 스크랩


로그인하셔야 댓글을 달 수 있습니다.