ОБНОВЛЕНИЕ : добавлено решение, которое будет работать с включенным Aero для Windows Vista и Windows 7
<ч />
Non-Aero Solution
Не-клиентская область оконного взаимодействия управляется серией не клиентских специфических сообщений. Например, сообщение 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
, это остановит ОС от рисования не клиентской области, если приложение слишком долго реагирует на сообщения Windows. Если вы этого не сделаете, то в некоторых ситуациях будет отображаться граница, но ваши украшения не будут отображаться. Так что это зависит от ваших требований, подходит вам это или нет.
<ч />
Решение с поддержкой Aero
В ответ на комментарий @TheCodeKing я подумал, что еще раз взгляну на это. Оказывается, это можно сделать полностью документированным способом, поддерживая Aero. Но это не для слабонервных. Я не буду приводить полное решение здесь, есть некоторые изломы для тренировки, но это делает основы.
Этот код / решение основано на примере Win32, который можно найти в следующем месте
http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx
Принципиально, что вам нужно сделать, это следующее.
- Расширение клиентской области окна, чтобы закрыть рамку. Это делается путем обработки сообщения WM_NCCALCSIZE и возврата 0. Это дает области, не являющейся клиентом, размер 0, и, следовательно, область клиента теперь охватывает все окно.
- Расширение фрейма в клиентскую область с помощью DwmExtendFrameIntoClientArea . Это заставляет ОС визуализировать фрейм в клиентской области.
Приведенные выше шаги предоставят вам окна со стандартной стеклянной рамкой, исключая системное меню (значок окна) и заголовок. Кнопки свертывания, разворачивания и закрытия будут по-прежнему отображаться и будут работать. То, что вы не сможете сделать, это перетащить или изменить размер окна, так как на самом деле фрейма нет, помните, что клиентская область охватывает все окно, мы только что попросили ОС нарисовать фрейм на клиентской области. 1044 *
Теперь вы можете рисовать в окне, как обычно, даже поверх рамки. Вы даже можете поместить элементы управления в область заголовка.
Наконец, позвольте DWM выполнить тестирование для вас, вызвав DwmDefWindowProc из вашего WndProc
(до того, как вы его обработали). Возвращает логическое значение, указывающее, обработал ли DWM сообщение за вас.