2017-05-06 325 views
2

我想实现的东西,如Visual Studio安装确实有边界的窗口和阴影:边界的窗口与阴影

screenshot

我尝试了各种选项,如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); 
+0

没有类样式'CS_SHADOW'。你真的尝试过什么? – IInspectable

+1

对不起,我的意思是CS_DROPSHADOW。它的效果与我作为例子提供的效果有所不同。 [比较](http://i.imgur.com/2JNOEgj.png) – wajsic

+0

启动Spy ++工具并检查想要镜像的行为和属性。 – 2017-05-06 09:37:48

回答

6

可以使用的DwmExtendFrameIntoClientArea()组合和WM_NCCALCSIZE返回0如果wParam为TRUE产生这种效果。以下详细步骤。

  • 窗口风格应该是这样的,通常为整个框架将显示(很适合我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区域。
  • 如果wParam为TRUE,则从WM_NCCALCSIZE返回0。这具有将客户区扩展到窗口大小的效果,包括框架,但不包括阴影。请参阅文档的备注部分。
  • 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: 
     { 
      // Returning 0 from the message 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: 
     { 
      // Returning HTCAPTION allows the user to move the window around by clicking 
      // anywhere. 
      // Depending on the mouse coordinates passed in LPARAM, you may 
      // return 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 
} 
+0

非常感谢您的详细解释。虽然...我设法画了一个阴影,但现在我得到这个1px的框架,在我的情况下是绿色的。任何想法如何摆脱? [Image](http://i.imgur.com/Kgcc6Cu.png) – wajsic

+0

@wajsic您是否使用GDI +进行绘制? – zett42

+0

不。我使用的是DirectX – wajsic