在Windows 10上使用DrawThemeBackground绘制的部件不正确

时间:2016-09-04 17:07:18

标签: c winapi visual-c++

问题描述

我想在C中创建一个Windows API应用程序,它将菜单和标题按钮呈现在同一个非客户区域,类似于Firefox notice the menu and caption buttons

为了做到这一点,我已经确定解决方案需要:

  • 是WS_POPUP类型,以便菜单与顶部
  • 对齐
  • 取得非客户区域(呈现菜单的位置)的所有权
  • 手动渲染最小化/最大化/关闭按钮

该解决方案需要适用于Windows 7,8和10(理想情况下也适用于未来版本)。

现在的样子

I have a test program available on GitHub

在我的应用中,我已经覆盖了相应的事件:

WM_NCCALCSIZEWM_NCHITTESTWM_NCLBUTTONDOWNWM_NCLBUTTONUPWM_NCMOUSEMOVEWM_NCPAINT

然后我重新描述了这些事件的非客户区域: WM_NCACTIVATEWM_SETTEXT

以下是我如何进行渲染的示例:

// globals set elsewhere
RECT customAreaRect, minRect, maxRect, closeRect, coverMenuRect;
BOOL maximized;

// ...

LRESULT OnPaintNCA(HWND hWnd, WPARAM wParam, LPARAM lParam) {
  RECT windowRect;
  HRGN hRgn = NULL;
  GetWindowRect(hWnd, &windowRect);
  if (wParam == 1) {
    hRgn = CreateRectRgnIndirect(&windowRect);
  } else {
    hRgn = (HRGN)wParam;
  }

  if (hRgn) {
    // Carve out the area for custom content
    HRGN captionButtonRgn = CreateRectRgnIndirect(&customAreaRect);
    CombineRgn(hRgn, hRgn, captionButtonRgn, RGN_XOR);
    DeleteObject(captionButtonRgn);

    // Force default painting for non-client area
    LRESULT ret = DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);

    // black background covering part of menu, behind buttons
    HDC hDC = GetWindowDC(hWnd);
    FillRect(hDC, &coverMenuRect, (HBRUSH)GetStockObject(BLACK_BRUSH));

    HTHEME hTheme = OpenThemeData(hWnd, TEXT("WINDOW"));

    DrawThemeBackground(hTheme, hDC, WP_MINBUTTON, partState, minRect, NULL);
    DrawThemeBackground(hTheme, hDC, maximized ? WP_RESTOREBUTTON : WP_MAXBUTTON, partState, maxRect, NULL);
    DrawThemeBackground(hTheme, hDC, WP_CLOSEBUTTON, partState, closeRect, NULL);

    CloseThemeData(hTheme);
  }
}

渲染结果如下所示: screenshot showing what I have now 不幸的是,用于部件的样式(最小化,最大化/恢复,关闭)看起来像Windows 7/8的样式,而不是本机Windows 10控件。我好几天都在寻找一种方法来做这件事而没有运气。我需要帮助了解如何使用Windows API为Windows 10呈现这些按钮。

当前状态(以及我迄今为止尝试过的)

我的第一个预感是我需要正确启用视觉样式。

以下是我尝试的相关项目设置的一些屏幕截图: target platform version

embed manifest

其他想法

我在尝试的东西上跑得很少。在放弃之前,我会尝试一些事情:

  • 创意1:使用GetThemeStream,可以检索控件的大小/位图。

    • 像这样加载aero msstyles文件:

    HMODULE themeFile = LoadLibraryEx(TEXT("C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles"), NULL, LOAD_LIBRARY_AS_DATAFILE);

    • 获取部件的位图(最小化按钮,最大化按钮等),如此(传递加载的主题文件):

    GetThemeStream(h, WP_MAXBUTTON, MAXBS_NORMAL, TMT_DISKSTREAM, (void**)&buffer, &bufferSize, themeFile);

    • 加载位图;它似乎是PNG格式(我还没有到目前为止)
    • 绘制位图
  • 创意2:从隐藏窗口复制非客户区域,该窗口具有标题区域(并最小化,最大化,关闭按钮)。

    • 创建一个窗口,其中包含标题和最小/最大按钮,从不激活它。
    • 在非客户端绘制中,获取该窗口的DC并捕获最小/最大/关闭按钮的像素
    • 使用bitblt
    • 渲染它们

1 个答案:

答案 0 :(得分:1)

我认为问题来自于尝试在操作系统版本> = Win Vista上使用WM_NCPAINT。 由于Vista所有NC渲染都由DWM(桌面窗口管理器)控制。如果你仍然敢于处理WM_NCPAINT,DWM渲染将被关闭,你会看到“老派”:

From the Shell Revealed Blog

  

DWM没有任何遗留问题,因为应用程序不能   在玻璃框架内绘制,因为它的渲染和管理   完全不同的过程。如果应用程序试图这样做,Windows   将检测它并完全移除玻璃框架(因此   恢复到Basic框架),以便应用程序可以绘制它   我想画画。

要获得正确的结果,您必须执行“DWM way”(特别是“删除标准框架”和“在扩展框架窗口中绘图”部分)。这可以通过让DWM在客户区域内渲染帧来实现,因此您可以在绘制它。此外,使用此解决方案,您无需自己绘制标题按钮。 This answer summarizes the required steps(在“Aero支持的解决方案”下)。

需要注意的是,您必须自己绘制菜单并且不能使用大部分GDI API,因为GDI会忽略alpha通道,如果框架是半透明的,那么事情看起来会很难看(Vista和Win 7默认情况下,Win8 +带扩展名)。如果源是包含带alpha通道的32bpp位图的内存DC,则BitBlt()有效。 GDI +也可以。