C# 如何在窗口中添加额外按钮';什么是标题栏?

C# 如何在窗口中添加额外按钮';什么是标题栏?,c#,.net,user-interface,C#,.net,User Interface,我看到一些应用程序(可能不是.NET应用程序)在表单标题栏的最小化按钮左侧有一个额外的按钮?如何在C#中实现这一点?更新:添加了一个解决方案,该解决方案将与支持Aero的Windows Vista和Windows 7配合使用 ***非气动解*** 窗口交互的非客户端区域由一系列非客户端特定消息管理。例如,WM_NCPAINT消息被发送到窗口过程以绘制非客户端区域 我从未在.NET中这样做过,但我怀疑您可以跳过WndProc并处理WM_NC*消息来实现您想要的 更新:因为我从未在.NET上尝试过

我看到一些应用程序(可能不是.NET应用程序)在表单标题栏的最小化按钮左侧有一个额外的按钮?如何在C#中实现这一点?

更新:添加了一个解决方案,该解决方案将与支持Aero的Windows Vista和Windows 7配合使用


***非气动解*** 窗口交互的非客户端区域由一系列非客户端特定消息管理。例如,WM_NCPAINT消息被发送到窗口过程以绘制非客户端区域

我从未在.NET中这样做过,但我怀疑您可以跳过WndProc并处理WM_NC*消息来实现您想要的

更新:因为我从未在.NET上尝试过这个,所以我有几分钟的时间,我想我会快速尝试一下

在Windows7上尝试这一点,我发现如果我想让操作系统对非客户端区域进行基本渲染,我需要禁用窗口的主题。所以这里有一个简短的测试。我使用GetWindowDC来获取整个窗口的DC,而不是GetDCEx,这只是因为我可以从内存中进行互操作,并且没有查找GetDCEx的所有标志常量。当然,代码可以做更多的错误检查

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
  public partial class CustomBorderForm : Form
  {
    const int WM_NCPAINT = 0x85;

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr GetWindowDC(IntPtr hwnd);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern void DisableProcessWindowsGhosting();

    [DllImport("UxTheme.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern IntPtr SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList);

    public void CustomBorderForm()
    {
      // This could be called from main.
      DisableProcessWindowsGhosting();
      InitializeComponent();
    }

    protected override void OnHandleCreated(EventArgs e)
    {
      SetWindowTheme(this.Handle, "", "");
      base.OnHandleCreated(e);
    }

    protected override void WndProc(ref Message m)
    {
      base.WndProc(ref m);
      
      switch (m.Msg)
      {
        case WM_NCPAINT:
          {
            IntPtr hdc = GetWindowDC(m.HWnd);
            using (Graphics g = Graphics.FromHdc(hdc))
            {
              g.FillEllipse(Brushes.Red, new Rectangle((Width-20)/2, 8, 20, 20));
            }
            ReleaseDC(m.HWnd, hdc);
          }
          break;
      }
    }
  }
}
顺便说一句,我调用了
DisableProcessWindowsGhosting
,如果应用程序响应windows消息的时间过长,这将阻止操作系统绘制非客户端区域。如果不执行此操作,则在某些情况下会渲染边框,但不会显示装饰。所以这取决于你的需求,它是否适合你


***航空支持解决方案*** 在@TheCodeKing的评论的推动下,我想我会再看看这个。事实证明,这可以在支持Aero的同时以一种完全记录的方式完成。但这不是为胆小的人准备的。我不会在这里提供一个完整的解决方案,仍然有一些纠结要锻炼,但它做的是基本的

此代码/解决方案基于Win32示例,可在以下位置找到

原则上你需要做的是以下几点

  • 扩展窗口的工作区以覆盖框架。这是通过处理WM_NCCALCSIZE消息并返回0来完成的。这使非客户端区域的大小为0,因此客户端区域现在覆盖整个窗口
  • 使用将框架扩展到客户端区域。这将使操作系统在客户端区域上渲染帧
以上步骤将为您提供一个带有标准玻璃框架的窗口,不包括系统菜单(窗口图标)和标题。最小化、最大化和关闭按钮仍将被绘制并将工作。你将不能做的是拖动或调整窗口大小,这是因为框架并不在那里,记住客户端区域覆盖了整个窗口,我们刚刚要求操作系统将框架绘制到客户端区域

现在,您可以像正常情况一样在窗口上绘制,甚至在框架的顶部。您甚至可以在标题区域中放置控件


最后,允许DWM通过从
WndProc
调用来为您处理命中测试(在您处理之前)。它返回一个布尔值,指示DWM是否为您处理了消息。

我认为一种方法是处理WM_NCPAINT消息(非客户端绘制)以绘制按钮,并处理非客户端鼠标单击以知道有人单击了“按钮”。

简单解决方案:

步骤1:创建Windows窗体(这将是您的自定义标题栏)

第二步。在中,您希望在添加中使用此自定义控件

titleBarBtn = new TitleBarButtons();
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
titleBarBtn.Show();
titleBarBtn.Owner = this;
给你的构造器。。。你可以搞乱偏移量,这正好适合我的应用程序

第三步。将移动事件添加到主窗体

private void Form14_Move(object sender, EventArgs e)
{
    titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
}

如果您想对上述任何代码进行更好的解释,请告诉我。

注意,您不应该这样做:–»不要将控件添加到窗口框架。将控件放在窗口内。«@Johannes,我承认有时这样做的应用程序可能会很烦人,但有些应用程序做得很好。像Ultramon,它在Windows7中看起来非常原生的框架上放置了一个按钮来交换监视器。所以有一些应用程序做得很有品味。@Josh:office2007也做过(但这不是Office团队第一次忽略任何Windows用户体验指南)。不过,该文档是由用户体验专业人士编写的,而仅仅是开发人员(或代码猴子)正在努力忽略它们。为了用户的利益,我认为他们应该随大流,除非他们能带来有经验和知识的人,以及最重要的可用性测试来证明他们不是。这就是为什么他们是指导方针而不是法律。如果你阅读整个页面,你会看到很多例子,微软自己的设计指南曾经被认为是前进的方向,现在却被认为是错误的。我喜欢微软忽视自己的指南的文章。不幸的是,这种技术不适用于aero glass。如果有人知道制作玻璃所需的巫术,我会很感兴趣。到目前为止,我还没有找到任何有效的例子。@TheCodeKing,我已经更新了答案以涵盖您的问题。我还在.NET中创建了原型,只是为了确认它可以正常工作。这是我引用的MSDN文章,它提醒我我确实知道如何做到这一点,我在某处有一些代码,它只是没有在设计模式下呈现它在运行时的样子。你有没有一个你所描述的技术的C#示例,我已经尝试了很多次,但都不起作用?我有一个非常基本(有限)的样本,我用原型来测试这个概念。你可以从我的skydrive获取代码。这绝不是完整的,它只是一个概念的证明@克里斯泰勒:我发现在你有限的样本中控制按钮不起作用,我已经设法解决了。请将
WM\u nchitest
的代码更新为:
if(callDWP){callDWP=(ht==Win32Constants.HTNOWHERE);result=new Int
private void Form14_Move(object sender, EventArgs e)
{
    titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
}