创建没有标题栏,可调整大小的边框和伪造的6px白色条纹的窗口 [英] Create window without titlebar, with resizable border and without bogus 6px white stripe

查看:140
本文介绍了创建没有标题栏,可调整大小的边框和伪造的6px白色条纹的窗口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个没有标题栏但具有可调整大小的框架和阴影的窗口. 可以通过删除WS_CAPTION并添加WS_THICKFRAME轻松地实现此目标.但是,由于Windows 10的存在,因此存在一个6像素的白色非客户区域.

I want a window without title bar but with resizable frames and shadow. This can easily be achieved by removing WS_CAPTION and adding WS_THICKFRAME, however, since Windows 10, there's a 6px white non-client area.

使用以下代码,我创建了一个窗口,并将所有工作区都涂成黑色,该窗口分别获得了左,右和底部6像素的透明边距,而顶部边距是白色.

With the following code I create a window and paint all the client area with black, the window gets a left, right and bottom 6px transparent margins, however the top margin is white.

#ifndef UNICODE
#define UNICODE
#endif 

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    // Register the window class.
    const wchar_t CLASS_NAME[]  = L"Sample Window Class";

    WNDCLASS wc = { };

    wc.lpfnWndProc   = WindowProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    // Create the window.

    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        L"",    // Window text
                0,
        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
        );

    ShowWindow(hwnd, nCmdShow);

    LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
    lStyle |= WS_THICKFRAME;
    lStyle = lStyle & ~WS_CAPTION;
    SetWindowLong(hwnd, GWL_STYLE, lStyle);
    SetWindowPos(hwnd, NULL, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);

    // Run the message loop.

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);


            // Paint everything black
            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOWTEXT));
            EndPaint(hwnd, &ps);
        }
        return 0;

    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

渲染器:

如何去除白色条纹? 我还发现了与该Qt相关的错误报告 QTBUG-47543 ,该报告未作为非Qt文件而关闭问题,因为它可以用win32 api复制.

How can I remove the white stripe ? I also found this related Qt bug report QTBUG-47543 which was closed as not being a Qt problem, because it can be reproduced with win32 api.

推荐答案

那不是bug.在Windows 10中,左/右/底边框是透明的.顶部边框不透明.您应该保持原样.可能没人会抱怨.

That's not a bug. In Windows 10 the borders on left/right/bottom are transparent. The top border is not transparent. You should leave it as is. Probably nobody will complain.

要更改它,必须修改非客户区.在Windows Vista及更高版本中,这相当困难.请参见使用DWM的自定义窗口框架供参考.

To change it, you must modify the non-client area. This is rather difficult in Windows Vista and above. See Custom Window Frame Using DWM for reference.

  • 查找边框粗细

  • Find border thickness

使用DwmExtendFrameIntoClientArea可以访问非客户区域

Use DwmExtendFrameIntoClientArea to get access to non-client area

使用BeginBufferedPaint在非客户区域上绘制不透明的颜色

Use BeginBufferedPaint to draw opaque color over non-client area

Windows 10示例:

Windows 10 example:

(请参阅下一个示例,以了解与Windows Vista,7、8的兼容性)

//requires Dwmapi.lib and UxTheme.lib
#include <Windows.h>
#include <Dwmapi.h>

void my_paint(HDC hdc, RECT rc)
{
    HBRUSH brush = CreateSolidBrush(RGB(0, 128, 0));
    FillRect(hdc, &rc, brush);
    DeleteObject(brush);
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static RECT border_thickness;

    switch (uMsg)
    {
    case WM_CREATE:
    {
        //find border thickness
        SetRectEmpty(&border_thickness);
        if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME)
        {
            AdjustWindowRectEx(&border_thickness, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
            border_thickness.left *= -1;
            border_thickness.top *= -1;
        }
        else if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER)
        {
            SetRect(&border_thickness, 1, 1, 1, 1);
        }

        MARGINS margins = { 0 };
        DwmExtendFrameIntoClientArea(hwnd, &margins);
        SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
        break;
    }

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);

        RECT rc = ps.rcPaint;
        BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
        HDC memdc;
        HPAINTBUFFER hbuffer = BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, &params, &memdc);

        my_paint(memdc, rc);

        BufferedPaintSetAlpha(hbuffer, &rc, 255);
        EndBufferedPaint(hbuffer, TRUE);

        EndPaint(hwnd, &ps);
        return 0;
    }

    case WM_NCACTIVATE:
        return 0;

    case WM_NCCALCSIZE:
        if (lParam)
        {
            NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lParam;
            sz->rgrc[0].left += border_thickness.left;
            sz->rgrc[0].right -= border_thickness.right;
            sz->rgrc[0].bottom -= border_thickness.bottom;
            return 0;
        }
        break;

    case WM_NCHITTEST:
    {
        //do default processing, but allow resizing from top-border
        LRESULT result = DefWindowProc(hwnd, uMsg, wParam, lParam);
        if (result == HTCLIENT)
        {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
            ScreenToClient(hwnd, &pt);
            if (pt.y < border_thickness.top) return HTTOP;
        }
        return result;
    }

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int)
{
    const wchar_t CLASS_NAME[] = L"Sample Window Class";

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = CLASS_NAME;
    RegisterClass(&wc);

    CreateWindowEx(0, CLASS_NAME,   NULL,
        WS_VISIBLE | WS_THICKFRAME | WS_POPUP,
        10, 10, 600, 400, NULL, NULL, hInstance, NULL);

    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

为与Windows Vista/7/8兼容,请使用此过程.这将在左/上/下边框以及上边框上绘制.该窗口将显示为带有边框大小的简单矩形:

For compatibility with Windows Vista/7/8 use this procedure instead. This will paint over left/top/bottom borders as well as top border. This window will appear as a simple rectangle, with resizing borders:

//for Windows Vista, 7, 8, 10
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static RECT border_thickness;

    switch (uMsg)
    {
    case WM_CREATE:
    {
        //find border thickness
        SetRectEmpty(&border_thickness);
        if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME)
        {
            AdjustWindowRectEx(&border_thickness, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
            border_thickness.left *= -1;
            border_thickness.top *= -1;
        }
        else if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER)
        {
            SetRect(&border_thickness, 1, 1, 1, 1);
        }

        MARGINS margins = { 0 };
        DwmExtendFrameIntoClientArea(hwnd, &margins);
        SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
        break;
    }

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);

        RECT rc = ps.rcPaint;
        BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
        HDC memdc;
        HPAINTBUFFER hbuffer = BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, &params, &memdc);

        my_paint(memdc, rc);

        BufferedPaintSetAlpha(hbuffer, &rc, 255);
        EndBufferedPaint(hbuffer, TRUE);

        EndPaint(hwnd, &ps);
        return 0;
    }

    case WM_NCACTIVATE:
        return 0;

    case WM_NCCALCSIZE:
        if (lParam)
            return 0;

    case WM_NCHITTEST:
    {
        POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
        ScreenToClient(hwnd, &pt);
        RECT rc;
        GetClientRect(hwnd, &rc);
        enum {left=1, top=2, right=4, bottom=8};
        int hit = 0;
        if (pt.x < border_thickness.left) hit |= left;
        if (pt.x > rc.right - border_thickness.right) hit |= right;
        if (pt.y < border_thickness.top) hit |= top;
        if (pt.y > rc.bottom - border_thickness.bottom) hit |= bottom;

        if (hit & top && hit & left) return HTTOPLEFT;
        if (hit & top && hit & right) return HTTOPRIGHT;
        if (hit & bottom && hit & left) return HTBOTTOMLEFT;
        if (hit & bottom && hit & right) return HTBOTTOMRIGHT;
        if (hit & left) return HTLEFT;
        if (hit & top) return HTTOP;
        if (hit & right) return HTRIGHT;
        if (hit & bottom) return HTBOTTOM;

        return HTCLIENT;
    }

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

这篇关于创建没有标题栏,可调整大小的边框和伪造的6px白色条纹的窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆