10

我在StackOverflow上发现了这个问题。基本上,用户想要在标题栏上绘制自定义按钮。

我尝试了代码并意识到它仅在禁用 Aero 时才在 vista/7 中工作。我的问题是,有没有办法在 启用 aero时在标题栏上绘制自定义按钮?

此外,是否有任何方法可以从当前主题读取信息,以便我可以设置按钮样式以匹配现有主题。


更新

这是我的电脑截图,展示了上述概念。安装 DisplayFusion 后,我得到了额外的标题栏按钮。
Notepad2 显示附加按钮。

而且我知道 DisplayFusion 是一个 .NET 程序,因为它在 .NET Reflector 中打开。不利的一面是程序被混淆了。不像我想反编译程序或任何东西;我只想在我的标题栏上添加一个按钮来做其他事情(例如最小化到系统托盘)。
下面是屏幕截图,证明该程序是一个 .NET 应用程序。
.NET 反射器显示 DisplayFusion

4

3 回答 3

4

只要您愿意重新绘制整个标题栏内容,那么您可以使用 DWM API 的 DwmExtendFrameIntoClientArea 方法,该方法涉及将您的窗口设置为没有标题栏,从而允许 DWM 将其玻璃绘制到您的可用区域以创建一个看起来像标题栏但实际上在您的客户区域中的新空间,以便您可以在其上绘制按钮。

这种方法的缺点是,如果需要,您必须制作标准按钮。最小化、最大化和关闭重新创建没有问题(尽管您会发现最大化按钮是一个切换按钮,其外观基于状态),但您可能会发现重新创建左上角按钮有问题。当然,您还必须重新绘制标题,但我无法想象您对此会有任何问题。

至于读取当前主题的数据以将您的按钮重新设计为他们的,我很抱歉,但我对此一无所知。我的建议是在标题栏上构建按钮时使用透明度,并使用半透明发光效果等。这样您就可以保留玻璃背景,并通过覆盖半透明颜色来简单地修改其外观。此外,如果你这样做,那么玻璃上不断变化的镜面高光会自然移动,而如果你只是获得它们的主题颜色,你会发现要么你无法访问它们的反射,要么它们在内部重新创建按钮的空间。当然,只是我的两分钱——如果你能找到一种方法来做你的主题颜色等等,无论如何。

不过有一个警告 - 还有另一个 StackOverflow 线程(http://stackoverflow.com/questions/2666979/net-framework-4-0-and-drawing-on-aero-glass-issue/4656182#4656182)是描述 DWM API DwmExtendFrameIntoClientArea 方法的问题,所以如果我是你,我会在尝试此解决方案之前通读该问题。

于 2011-01-13T21:24:58.183 回答
4

我对此进行了一些研究,因为我也在自己的多显示器解决方案中实现了这一点。DisplayFusion 和 TeamViewer 实现这一点的方式是在包含按钮的所需窗口上覆盖自定义表单。您可以使用 Spy++ 来确认这一点。

这是一般的想法:

  1. 编写一个 DLL 挂钩,用于在创建、删除、激活、重新定位或调整窗口大小时获得通知。
  2. 使用透明键或使用此alpha-PNG 和 GDI+ 解决方案制作透明表单,并通过自己绘制按钮来模拟按钮。
  3. 在创建/删除窗口时,您显示/隐藏所需窗口的标题栏按钮表单。
  4. 在窗口调整大小/重新定位消息上,您重新对齐标题栏按钮表单。
  5. 在窗口激活时,您设置窗口的 Z 位置,使其位于所需窗口的顶部。

GetWindow通过和SetWindowPosAPI获取和设置 Z 位置。
请注意,DLL 必须以本地语言编写。但是,您可以使用类似的方法来解决该问题: Using Window Messages to Implement Global System Hooks in C#

这是我的结果:

在此处输入图像描述

于 2012-06-11T21:21:27.827 回答
0

好的,系统挂钩是获取更改窗口位置信息的直接方法,Jelle 的透明形式建议非常好。但是您可以使用侵入性较小的方法通过 user32.dll 获取有关桌面窗口的信息

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

您可以使用 EnumDesktopWindows 获得的窗口句柄

[DllImport("user32.dll", EntryPoint = "EnumDesktopWindows", 
ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDelegate lpEnumCallbackFunction, IntPtr lParam);

您可以从线程getting-a-list-of-all-applications-in-c-sharp获得一个示例

于 2015-11-13T11:50:26.260 回答