在Windows Vista / 7 Aero Titlebar上绘制自定义按钮

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

我尝试了代码,并且只有在禁用Aero时才能在vista / 7中实现它。 我的问题是,在启用aero的同时 ,有没有办法在标题栏上绘制自定义按钮?

此外,有没有办法从当前主题中读取信息,所以我可以设置我的按钮样式以匹配现有的按钮。


更新

这是我的计算机的屏幕截图,展示了上述概念。 安装DisplayFusion后,我得到了额外的标题栏按钮。
Notepad2显示其他按钮。

我知道DisplayFusion是一个.NET程序,因为它在.NET Reflector中打开。 缺点是程序被混淆了。 不喜欢我想反编译程序或任何东西; 我只想在标题栏上添加一个按钮来执行其他操作(例如,最小化到系统托盘)。
下面是截图,certificate该程序是一个.NET应用程序。
.NET Reflector显示DisplayFusion

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

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

至于阅读当前主题的数据,重新将你的按钮作为他们的按钮,我很抱歉,但我对此一无所知。 我的建议是在标题栏上构建按钮时使用透明度,并使用半透明的发光效果等。 通过这种方式,您将能够保留玻璃背景,并通过覆盖半透明颜色来简单地修改其外观。 此外,如果你这样做,那么玻璃上移动的镜面高光会自然地移动,而如果你只是得到他们的主题颜色你会发现你没有获得他们的reflection或他们重新重新创建按钮的空间。 当然,只需要我的两分钱 – 如果你能找到一种方法来做主题颜色等等。

但是有一点需要注意 – 还有另一个StackOverflow线程(http://stackoverflow.com/questions/2666979/net-framework-4-0-and-drawing-on-aero-glass-issue/4656182#4656182)用DWM API DwmExtendFrameIntoClientArea方法描述一个问题,所以如果我是你,我会在尝试这个解决方案之前仔细阅读。

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

这是一般的想法:

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

通过GetWindowSetWindowPos API获取和设置Z位置。
请注意,DLL必须使用本机语言编写。 但是,你可以使用类似的东西来解决这个问题: 使用窗口消息在C#中实现全局系统挂钩

这是我的结果:

在此处输入图像描述

好的,系统挂钩是获取更改窗口位置信息的直接方法,而Jelle的透明forms的建议非常好。 但是,您可以使用较少侵入性的方法通过使用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); 

一个例子,你可以从线程获得所有应用程序在-c-sharp中的列表