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
}