Подтвердить что ты не робот

Как добавить дополнительную кнопку в заголовок окна?

Я видел, что некоторые приложения (возможно, не .NET-приложения) имеют дополнительную кнопку слева от кнопки "Свернуть" в строке заголовка формы? Как я могу достичь этого в С#?

4b9b3361

Ответ 1

UPDATE: добавлено решение, которое будет работать с Aero для 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;
      }
    }
  }
}

Btw. Я назвал DisableProcessWindowsGhosting, это остановит ОС на рисовании DisableProcessWindowsGhosting области, если приложение слишком долго реагирует на сообщения Windows. Если вы этого не сделаете, то в некоторых ситуациях граница будет отображаться, но ваши украшения не будут показаны. Так что это зависит от ваших требований, которые вам подходят или нет.


Решение, поддерживающее Aero

В ответ на комментарий от @TheCodeKing, я подумал, что еще раз посмотрю на это. Оказывается, это можно сделать полностью документированным способом, поддерживая Aero. Но это не для слабонервных. Я не буду предлагать полное решение здесь, есть еще некоторые изгибы для тренировки, но это делает основы.

Этот код/​​решение основано на примере Win32, который можно найти в следующем месте: http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx

В принципе, вам нужно сделать следующее.

  • Расширьте клиентскую область окна, чтобы закрыть рамку. Это делается путем обработки сообщения WM_NCCALCSIZE и возврата 0. Это дает области Non-Client размер 0, и поэтому область клиента теперь охватывает все окно.
  • Расширьте рамку в клиентской области с помощью DwmExtendFrameIntoClientArea. Это позволяет ОС визуализировать Frame поверх клиентской области.

Вышеупомянутые шаги предоставят вам окна со стандартной стеклянной рамкой, исключая системное меню (значок окна) и заголовок. Кнопки минимизации, максимизации и закрытия по-прежнему будут рисоваться и будут работать. То, что вы не сможете сделать, это перетащить или изменить размер окна, потому что фрейм на самом деле не существует, помните, что клиентская область охватывает все окно, мы просто попросили ОС нарисовать рамку на клиентской области.

Теперь вы можете рисовать на окне как обычно, даже на верхней части рамки. Вы можете даже ввести элементы управления в область надписи.

Наконец, позвольте DWM справляться с тестированием ударов, вызывая DwmDefWindowProc из вашего WndProc (до того, как вы его обработали). Он возвращает логическое значение, указывающее, обрабатывает ли DWM сообщение для вас.

Ответ 2

Я думаю, что способ сделать это - обработать сообщение WM_NCPAINT (неклиентская краска), чтобы нарисовать кнопку, и обрабатывать клики с кликами без клика, чтобы узнать, кто-то нажал на "button".

Ответ 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. В том, что вы хотите использовать этот настраиваемый элемент управления в add

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);
}

Пожалуйста, дайте мне знать, если вы хотите лучше объяснить любой из приведенных выше кодов.