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

Ton*_*ony 38 .net c# user-interface

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

Chr*_*lor 37

更新:添加了适用于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;
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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


Aero支持的解决方案

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

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

原则上,您需要做的是以下内容.

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

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

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

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

  • 我有一个非常基本的(有限的)样本,该样本已原型化以测试该概念。您可以从我的skydrive获取代码。这绝不是完整的,仅仅是概念的证明!https://skydrive.live.com/?cid=5bb13275dc6b8248&sc=documents&uc=1&id=5BB13275DC6B8248%21162# (2认同)

hrh*_*hrh 6

简单的解决方案:

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

-Set Form Border Style to None
-Add whatever controls you would like to this
-I will name this custom form "TitleBarButtons"
Run Code Online (Sandbox Code Playgroud)

步骤 2. 在要使用此自定义控件的表单中,添加

titleBarBtn = new TitleBarButtons();
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
titleBarBtn.Show();
titleBarBtn.Owner = this;
Run Code Online (Sandbox Code Playgroud)

对于你的构造函数......你可以弄乱偏移量,这正好适合我的应用程序的一个很好的位置

步骤 3. 将 Move 事件添加到主窗体中

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

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