如何在窗口的标题栏中添加一个额外的按钮?

时间:2010-05-15 18:05:06

标签: c# .net user-interface

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

3 个答案:

答案 0 :(得分:35)

更新:添加了适用于Windows Vista和Windows 7的Aero的解决方案

<小时/> 非航空解决方案

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

我从未在.NET中做过这个,但我怀疑你可以覆盖WndProc并处理WM_NC *消息来实现你想要的。

更新:因为我从未尝试过.NET,所以我花了几分钟时间,我想我会试一试。

在Windows 7上尝试这个,我发现如果我想要操作系统来执行非客户区的基本渲染,我需要禁用窗口的主题。所以这是一个简短的测试。我使用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 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));
            }
            int r = ReleaseDC(m.HWnd, hdc);
          }
          break;
      }
    }
  }
}

顺便说一下。我打电话给DisableProcessWindowsGhosting,如果应用程序花费太长时间来响应Windows消息,这将阻止操作系统绘制非客户区域。如果您不这样做,那么在某些情况下,边框将被渲染,但您的装饰品将不会显示。所以这取决于你的要求它是否适合你。

<小时/> Aero支持的解决方案

@TheCodeKing的评论提示,我想我会再看看这个。事实证明,这可以在完全记录的方式完成,同时支持Aero。但它不适合胆小的人。我不会在这里提供一个完整的解决方案,仍有一些问题可以解决,但它确实是基础。

此代码/解决方案基于Win32示例,该示例可在以下位置找到 http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx

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

  • 扩展窗口的客户区域以覆盖框架。这是通过处理WM_NCCALCSIZE消息并返回0来完成的。这使非客户区域的大小为0,因此客户区域现在覆盖整个窗口。
  • 使用DwmExtendFrameIntoClientArea将框架扩展到客户区。这使操作系统在客户区域上渲染帧。

上面的步骤将为您提供一个带有标准玻璃框架的窗口,不包括系统菜单(窗口图标)和标题。最小化,最大化和关闭按钮仍将被绘制并将起作用。你无法做的是拖动或调整窗口大小,这是因为框架实际上并不存在,请记住客户区覆盖整个窗口,我们刚要求操作系统将框架绘制到客户区。< / p>

现在,您可以正常地在窗口上绘图,即使在框架顶部也是如此。您甚至可以将控件放在标题区域中。

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

答案 1 :(得分:4)

我认为这样做的一种方法是处理WM_NCPAINT消息(非客户端绘制)以绘制按钮,并处理非客户端鼠标点击以了解某人点击了“按钮”。

答案 2 :(得分:3)

简单解决方案:

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

-Set Form Border Style to None
-Add whatever controls you would like to this
-I will name this custom form "TitleBarButtons"

步骤2.在您想要在添加中使用此自定义控件的那个

titleBarBtn = new TitleBarButtons();
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
titleBarBtn.Show();
titleBarBtn.Owner = this;

对于你的构造函数......你可以搞砸这个适合我的应用程序的好位置的偏移

步骤3.将移动事件添加到主表单

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

如果您想要更好地解释上述任何代码,请与我们联系。