在 Windows Vista/7 Aero 标题栏上绘制自定义按钮

发布于 2024-10-01 00:38:55 字数 567 浏览 5 评论 0原文

我在

我尝试了代码,发现只有在 Aero 被禁用时它才可以在 vista/7 中工作。我的问题是,有没有办法在启用 aero 时在标题栏上绘制自定义按钮

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


更新

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

我知道 DisplayFusion 是一个 .NET 程序,因为它在 .NET Reflector 中打开。缺点是程序很混乱。不像我想反编译程序什么的;我只想在标题栏中添加一个按钮来执行其他操作(例如最小化到系统托盘)。
下面是截图,证明该程序是一个.NET应用程序。
显示 DisplayFusion 的 .NET Reflector

I found this question on StackOverflow. Basically, the user wanted to draw custom buttons on the titlebar.

I tried the code and realised it works in vista/7 only when Aero is disabled. My question is, is there any way to draw custom buttons on the titlebar while aero is enabled?

Also, is there any way of reading information from the current theme so I can style my buttons to match the already existing ones.


Update

Here is a screenshot from my computer demonstrating the above concept. I got the additional titlebar buttons after installing DisplayFusion.
Notepad2 showing additional buttons.

And I know DisplayFusion is a .NET program because it opens in .NET Reflector. The down side is that the program is obfuscated. Not like I wanted to decompile the program or anything; I just want to add a button to my titlebar to do something else (like minimise to the system tray for instance).
Below is the screenshot, proving the program is a .NET app.
.NET Reflector showing DisplayFusion

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

叹沉浮 2024-10-08 00:38:55

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

总体思路是:

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

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

这是我的结果:

在此处输入图像描述

I did some research into this as I implemented this too in my own multi-monitor solution. The way DisplayFusion and TeamViewer implemented this is overlaying a custom form over the desired window containing the buttons. You can use Spy++ to confirm this.

This is the general idea:

  1. Write a DLL hook for getting notified when a window is created, removed, activated, repositioned or resized.
  2. Make a transparant form using transparancy key or using this alpha-PNG and GDI+ solution and simulate the button(s) by painting it your own.
  3. On window creation/removal you show/hide your titlebarbutton form for the desired window(s).
  4. On window resize/reposition messages you realign your titlebarbutton form.
  5. On window activation you set the Z position of the window so that it is on top of the desired window.

Getting and setting the Z position through the GetWindow and SetWindowPos API's.
Be aware that the DLL must be written in a native language. However you van use something like this to work around that problem:
Using Window Messages to Implement Global System Hooks in C#

And here is my result:

enter image description here

Bonjour°[大白 2024-10-08 00:38:55

只要您愿意重绘整个标题栏内容,就可以使用 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 方法的问题,因此,如果我是您,我会在尝试此解决方案之前通读该内容。

As long as you're willing to redraw the entire title-bar contents, then you can use the DWM API's DwmExtendFrameIntoClientArea method, which involves setting your window to have no titlebar, allowing the DWM to draw its glass into your usable area to create a new space that looks like the titlebar but is in fact in your client area, so that you can draw buttons onto it.

The downside of this method is that you'll have to make the standard buttons if you want them. Minimize, Maximize and Close are no problem to recreate (though you'll find that the maximize button is a togglebutton with appearance based on the state), but you might find you have issue with recreating the top-left corner button. You'll also have to redraw your title, of course, but I can't imagine you'll have any issue with that.

As for reading the current theme's data back to restyle your buttons as theirs, I'm sorry but I don't know anything about that. My suggestion would be to use transparency when building your buttons onto the titlebar, and to use translucent glow-effects, etcetera. That way you'll be able to retain the glass background, and simply modify its appearance by overlaying a translucent colour. Also, if you do it this way, then the shifting specular highlights on the glass will move naturally, whereas if you simply got their theme colours you'd find either that you didn't get access to their reflections or that they recreated afresh within the space of your buttons. Just my two cents, of course - if you can find a way to do your theme colours etcetera, by all means.

There's one caveat to this though - there's another StackOverflow thread (http://stackoverflow.com/questions/2666979/net-framework-4-0-and-drawing-on-aero-glass-issue/4656182#4656182) which is describing an issue with the DWM API DwmExtendFrameIntoClientArea method, so if I were you, I'd read through that before trying this solution.

婴鹅 2024-10-08 00:38:55

好吧,系统钩子是获取有关更改的窗口位置信息的直接方法,并且 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);

您可以从线程 获取-c-sharp中所有应用程序的列表

Ok, system hooks are the direct approach for getting information on changed window positions and Jelle's proposal with transparent form is very good. But you can use a less invasive method to get information on Desktop windows by using user32.dll

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

The window handles you can get using 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);

An example for this you can get from the thread getting-a-list-of-all-applications-in-c-sharp

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文