簡體   English   中英

帶投影的無邊框窗口

[英]Borderless Window with Drop Shadow

我正在嘗試使用無邊框窗口和投影來實現類似 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);

如果 wParam 為TRUE您可以通過使用DwmExtendFrameIntoClientArea()和設置0作為WM_NCCALCSIZE的消息結果的組合來創建此效果。 詳細步驟如下。

  • 窗口樣式應該是這樣的,通常會顯示整個框架( WS_CAPTION|WS_POPUP對我來說效果很好),但不包括WS_MINIMIZEWS_MAXIMIZEWS_SYSMENU任何一個。
  • 使用DwmExtendFrameIntoClientArea() MARGINS{0,0,0,1}調用DwmExtendFrameIntoClientArea() 我們真的不想要一個透明的框架,所以只設置底部邊距就足夠了。
  • 調用SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED)讓系統重新計算NC區域。
  • 如果 wParam 為TRUE則從WM_NCCALCSIZE返回 0。 這具有將客戶區擴展到包括 frame 在內的窗口大小的效果,但不包括陰影。 請參閱文檔的備注部分。
  • WM_PAINT繪制框架和內容區域,但請確保對DwmExtendFrameIntoClientArea()調用定義的邊距區域使用不透明的 alpha 通道(值為 255)。 否則,該區域中將可見部分常規框架。 您可以為此使用 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, DWL_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, ps.rcPaint.left, ps.rcPaint.top, 
                ps.rcPaint.right - ps.rcPaint.left, 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, DWL_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
}

暫無
暫無

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

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