我想在C中创建一个Windows API应用程序,它将菜单和标题按钮呈现在同一个非客户区域,类似于Firefox
为了做到这一点,我已经确定解决方案需要:
该解决方案需要适用于Windows 7,8和10(理想情况下也适用于未来版本)。
I have a test program available on GitHub
在我的应用中,我已经覆盖了相应的事件:
WM_NCCALCSIZE
,WM_NCHITTEST
,WM_NCLBUTTONDOWN
,WM_NCLBUTTONUP
,WM_NCMOUSEMOVE
,WM_NCPAINT
然后我重新描述了这些事件的非客户区域:
WM_NCACTIVATE
,WM_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);
}
}
渲染结果如下所示: 不幸的是,用于部件的样式(最小化,最大化/恢复,关闭)看起来像Windows 7/8的样式,而不是本机Windows 10控件。我好几天都在寻找一种方法来做这件事而没有运气。我需要帮助了解如何使用Windows API为Windows 10呈现这些按钮。
我的第一个预感是我需要正确启用视觉样式。
Per this article,检查操作系统版本的调用将获得Windows 8 除非您通过清单专门定位Windows 10。 Click here to view my manifest。这确实有效:
Per the official "Enabling Visual Styles" article,我确保使用Common Controls版本6.
我在尝试的东西上跑得很少。在放弃之前,我会尝试一些事情:
创意1:使用GetThemeStream,可以检索控件的大小/位图。
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);
创意2:从隐藏窗口复制非客户区域,该窗口具有标题区域(并最小化,最大化,关闭按钮)。
答案 0 :(得分:1)
我认为问题来自于尝试在操作系统版本> = Win Vista上使用WM_NCPAINT
。
由于Vista所有NC渲染都由DWM(桌面窗口管理器)控制。如果你仍然敢于处理WM_NCPAINT
,DWM渲染将被关闭,你会看到“老派”:
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 +也可以。