6

我正在尝试实现类似于 Visual Studio 安装程序的无边框窗口和阴影:

截屏

我尝试了各种选项CS_DROPSHADOW,例如 DWM API,但是一旦应用WS_THICKFRAME样式,阴影就会消失。

这是我创建和居中窗口的代码:

RECT R = {0, 0, _clientWidth, _clientHeight};
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
_mainWnd = CreateWindow(L"D3DWndClassName", _mainWndCaption.c_str(), WS_OVERLAPPEDWINDOW, 100, 100, R.right, R.bottom, nullptr, nullptr, _appInst, nullptr);

if(!_mainWnd){
    MessageBox(nullptr, L"CreateWindow FAILED", nullptr, 0);
    PostQuitMessage(0);
}

RECT rc;

GetWindowRect(_mainWnd, &rc);

LONG lStyle = GetWindowLong(_mainWnd, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU );
SetWindowLong(_mainWnd, GWL_STYLE, lStyle);


int xPos = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
int yPos = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;

SetWindowPos(_mainWnd, 0, xPos, yPos, _clientWidth, _clientHeight, SWP_NOZORDER);

ShowWindow(_mainWnd, SW_SHOW);
UpdateWindow(_mainWnd);
4

1 回答 1

15

您可以通过使用 和 的组合来创建此效果,DwmExtendFrameIntoClientArea()作为if wParam is0的消息结果。详细步骤如下。WM_NCCALCSIZETRUE

  • 窗口样式应该是这样的,通常会显示整个框架(WS_CAPTION|WS_POPUP对我来说效果很好),但不要包括任何WS_MINIMIZE, WS_MAXIMIZE, WS_SYSMENU.
  • DwmExtendFrameIntoClientArea()用调用MARGINS{0,0,0,1}。我们真的不想要一个透明的框架,所以只设置底部边距就足够了。
  • 调用SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED)让系统重新计算NC面积。
  • WM_NCCALCSIZE如果 wParam 为,则返回 0 TRUE。这具有将客户区扩展到包括 frame 在内的窗口大小的效果,但不包括阴影。请参阅文档的备注部分。
  • 根据WM_PAINT需要绘制框架和内容区域,但请确保对调用定义的边距区域使用不透明的 Alpha 通道(值为 255)DwmExtendFrameIntoClientArea()。否则,该区域将可见部分常规框架。您可以为此使用 GDI+,因为大多数常规 GDI 函数会忽略 Alpha 通道。BitBlt()使用包含不透明 alpha 通道的 32bpp 源位图也可以。
  • WM_NCHITTEST如果你想要一个可调整大小的窗口,你可以处理。

所有这一切的效果是,由于 DWM 调用,您“覆盖”了现在位于客户区域内的常规窗口框架,但保留了常规窗口阴影。不用担心“涂漆”不会产生任何闪烁,即使您使窗口可调整大小。

您可以将任何标准或用户定义的控件放入此窗口。只要确保子控件不与DwmExtendFrameIntoClientArea()调用定义的边距重叠,因为大多数基于 GDI 的控件都会忽略 alpha 通道。

这是一个最小的、独立的示例应用程序:

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <dwmapi.h>
#include <unknwn.h>
#include <gdiplus.h>
#pragma comment( lib, "dwmapi" )
#pragma comment( lib, "gdiplus" )
namespace gdip = Gdiplus;

INT_PTR CALLBACK MyDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );

int APIENTRY wWinMain( _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow )
{
    // Initialize GDI+
    gdip::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdipToken = 0;
    gdip::GdiplusStartup( &gdipToken, &gdiplusStartupInput, nullptr );

    struct MyDialog : DLGTEMPLATE {
        WORD dummy[ 3 ] = { 0 };  // unused menu, class and title
    }
    dlg;
    dlg.style = WS_POPUP | WS_CAPTION | DS_CENTER;
    dlg.dwExtendedStyle = 0;
    dlg.cdit = 0;  // no controls in template
    dlg.x = 0;
    dlg.y = 0;
    dlg.cx = 300;  // width in dialog units
    dlg.cy = 200;  // height in dialog units

    DialogBoxIndirectW( hInstance, &dlg, nullptr, MyDialogProc );

    gdip::GdiplusShutdown( gdipToken );

    return 0;
}

INT_PTR CALLBACK MyDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch( message )
    {
        case WM_INITDIALOG:
        {
            SetWindowTextW( hDlg, L"Borderless Window with Shadow" );

            // This plays together with WM_NCALCSIZE.
            MARGINS m{ 0, 0, 0, 1 };
            DwmExtendFrameIntoClientArea( hDlg, &m );

            // Force the system to recalculate NC area (making it send WM_NCCALCSIZE).
            SetWindowPos( hDlg, nullptr, 0, 0, 0, 0,
                SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED );
            return TRUE;
        }
        case WM_NCCALCSIZE:
        {
            // Setting 0 as the message result when wParam is TRUE removes the
            // standard frame, but keeps the window shadow.
            if( wParam == TRUE )
            {
                SetWindowLong( hDlg, DWLP_MSGRESULT, 0 );
                return TRUE;
            }
            return FALSE;
        }
        case WM_PAINT:
        {
            PAINTSTRUCT ps{ 0 };
            HDC hdc = BeginPaint( hDlg, &ps );

            // Draw with GDI+ to make sure the alpha channel is opaque.
            gdip::Graphics gfx{ hdc };
            gdip::SolidBrush brush{ gdip::Color{ 255, 255, 255 } };
            gfx.FillRectangle( &brush, 
                static_cast<INT>( ps.rcPaint.left ), static_cast<INT>( ps.rcPaint.top ),
                static_cast<INT>( ps.rcPaint.right - ps.rcPaint.left ), static_cast<INT>( ps.rcPaint.bottom - ps.rcPaint.top ) );

            EndPaint( hDlg, &ps );
            return TRUE;
        }
        case WM_NCHITTEST:
        {
            // Setting HTCAPTION as the message result allows the user to move 
            // the window around by clicking anywhere within the window.
            // Depending on the mouse coordinates passed in LPARAM, you may 
            // set other values to enable resizing.
            SetWindowLong( hDlg, DWLP_MSGRESULT, HTCAPTION );
            return TRUE;
        }
        case WM_COMMAND:
        {
            WORD id = LOWORD( wParam );
            if( id == IDOK || id == IDCANCEL )
            {
                EndDialog( hDlg, id );
                return TRUE;
            }
            return FALSE;
        }
    }
    return FALSE; // return FALSE to let DefDialogProc handle the message
}
于 2017-05-06T10:28:47.183 回答