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

C# 在Windows Vista/7 Aero标题栏上绘制自定义按钮,c#,vb.net,winapi,windows-vista,titlebar,C#,Vb.net,Winapi,Windows Vista,Titlebar,我在网上发现了这个问题。基本上,用户希望在标题栏上绘制自定义按钮 我尝试了这段代码,并意识到它只有在禁用Aero的情况下才能在vista/7中工作。我的问题是,当启用aero时,有没有办法在标题栏上绘制自定义按钮 此外,是否有任何方法可以读取当前主题的信息,以便我可以设置按钮样式以匹配现有的按钮 更新 这是我的电脑截图,演示了上述概念。安装DisplayFusion后,我获得了附加的标题栏按钮。 我知道DisplayFusion是一个.NET程序,因为它是在.NET Reflector中打开

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

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

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


更新

这是我的电脑截图,演示了上述概念。安装DisplayFusion后,我获得了附加的标题栏按钮。

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

只要您愿意重新绘制整个标题栏内容,就可以使用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方法的一个问题,所以如果我是你,在尝试此解决方案之前,我已经通读了这些内容。

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

这是总体思路:

  • 编写DLL钩子,以便在创建、删除、激活、重新定位或调整窗口大小时获得通知
  • 使用Transparance键或使用此键创建transparant窗体,并通过自己绘制来模拟按钮
  • 在创建/删除窗口时,显示/隐藏所需窗口的标题栏窗体
  • 在窗口调整/重新定位消息时,重新对齐标题栏窗体
  • 在窗口激活时,设置窗口的Z位置,使其位于所需窗口的顶部
  • 通过
    GetWindow
    SetWindowPos
    API获取和设置Z位置。
    请注意,DLL必须用本机语言编写。但是,您可以使用类似的方法来解决该问题:

    下面是我的结果:


    好的,系统挂钩是获取更改窗口位置信息的直接方法,透明形式的提案非常好。但您可以使用一种侵入性较小的方法,通过使用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);
    

    你可以从线程中得到一个例子

    忘记这一点,它再也不能工作了。我知道这是可能的,因为有一个叫做Display Fusion的多显示器应用程序可以做到这一点。我知道这是一个.NET应用程序,因为我用Lutz Roeder的.NET Reflector查看了它。你的解决方案看起来很有希望。我想出了如何创建透明窗体并检测窗口的打开、移动和关闭。我想知道的是如何创建空白主题按钮,这样我就可以画出自己的按钮。如果可能的话,你能上传你的解决方案吗?你能张贴一些代码来显示你是如何在截图中创建标题栏按钮的吗?@ JelelVelgER,请考虑为“结果”的源代码添加一个链接,我需要这个解决方案…1我将写一篇关于CodeProject如何将其结合在一起的文章,但这可能需要一些时间,因为我现在没有太多的空闲时间。。