6

私は、Visual Studioインストーラーがボーダレスウィンドウとドロップシャドウで行うようなことを達成しようとしています:

スクリーンショット

や DWM APIなどのさまざまなオプションを試しCS_DROPSHADOWましたが、スタイルを適用するとすぐに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

if wParam isのメッセージ結果としてDwmExtendFrameIntoClientArea()と の設定を組み合わせて使用​​することで、この効果を作成できます。詳細な手順は次のとおりです。0WM_NCCALCSIZETRUE

  • ウィンドウ スタイルは、通常はフレーム全体が表示されるようにする必要があります (私にとってWS_CAPTION|WS_POPUPはうまく機能します) 。WS_MINIMIZEWS_MAXIMIZEWS_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_NCCALCSIZEwParam が の場合は0 を返しますTRUE。これには、フレームを含むが影を除くウィンドウ サイズにクライアント領域を拡張する効果があります。ドキュメントの備考セクションを参照してください。
  • WM_PAINTフレームとコンテンツ領域を好きなように描画しますが、呼び出しによって定義された余白領域には不透明なアルファ チャネル (値 255) を使用してくださいDwmExtendFrameIntoClientArea()そうしないと、通常のフレームの一部がこの領域に表示されます。ほとんどの通常の GDI 関数はアルファ チャネルを無視するため、GDI+ を使用できます。BitBlt()不透明なアルファ チャネルを含む 32bpp ソース ビットマップでも機能します。
  • WM_NCHITTESTサイズ変更可能なウィンドウが必要な場合は、処理できます。

このすべての効果は、DWM 呼び出しのために現在クライアント領域内にある通常のウィンドウ フレームを「上に」ペイントすることですが、通常のウィンドウの影は保持します。ウィンドウのサイズを変更できるようにしても、「塗りつぶし」によってちらつきが発生しないことを心配しないでください。

このウィンドウには、標準またはユーザー定義のコントロールを配置できます。DwmExtendFrameIntoClientArea()ほとんどの GDI ベースのコントロールはアルファ チャネルを無視するため、子コントロールが呼び出しによって定義されたマージンに重ならないようにしてください。

自己完結型の最小限のサンプル アプリケーションを次に示します。

#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 に答える