UPDATE : Ajout d'une solution qui fonctionnera avec Aero activé pour Windows Vista et Windows 7
***Non-Aero Solution***
La zone non cliente d'une interaction de fenêtre est gérée par une série de messages spécifiques aux non-clients. Par exemple, le message WM_NCPAINT est envoyé à la procédure de la fenêtre pour peindre la zone non cliente.
Je n'ai jamais fait cela à partir de .NET, mais je pense que vous pouvez ignorer le WndProc et traiter les messages WM_NC* pour obtenir ce que vous voulez.
Mise à jour : Comme je n'ai jamais essayé de le faire à partir de .NET, j'ai eu quelques minutes et j'ai pensé faire un essai rapide.
En essayant cela sur Windows 7, j'ai découvert que je devais désactiver les thèmes de la fenêtre si je voulais que l'OS fasse le rendu de base de la zone non cliente. Voici donc un petit test. J'ai utilisé GetWindowDC pour obtenir le DC de la fenêtre entière plutôt que GetDCEx, c'était juste parce que je pouvais interopérer cela depuis la mémoire et que je n'avais pas à chercher toutes les constantes de drapeaux pour GetDcEx. Et bien sûr, le code pourrait bénéficier de plus de contrôles d'erreurs.
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));
}
ReleaseDC(m.HWnd, hdc);
}
break;
}
}
}
}
Au fait, j'ai appelé DisableProcessWindowsGhosting
Ceci empêchera le système d'exploitation de dessiner la zone non cliente si l'application prend trop de temps pour répondre aux messages de Windows. Si vous ne faites pas cela, dans certaines situations, la bordure sera rendue mais vos ornements ne seront pas affichés. Cela dépend donc de vos besoins si cela vous convient ou non.
***Solution supportée par Aéro***
Suite au commentaire de @TheCodeKing, je me suis dit que j'allais y jeter un nouveau coup d'œil. Il s'avère que cela peut être fait d'une manière entièrement documentée tout en supportant Aero. Mais ce n'est pas pour les âmes sensibles. Je ne fournirai pas une solution complète ici, il y a encore quelques problèmes à résoudre, mais cela fait l'essentiel.
Ce code/solution est basé sur l'exemple Win32 qui se trouve à l'emplacement suivant http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx
En principe, ce que vous devez faire est le suivant.
- Étendre la zone client de la fenêtre pour couvrir le cadre. Pour ce faire, il faut traiter le message WM_NCCALCSIZE et renvoyer 0. La zone non cliente a donc une taille de 0 et la zone cliente couvre désormais la totalité de la fenêtre.
- Prolonger le cadre dans l'espace client en utilisant DwmExtendFrameIntoClientArea . Cela permet au système d'exploitation de rendre le cadre sur la zone client.
Les étapes ci-dessus vous donneront une fenêtre avec le cadre de verre standard, à l'exception du menu système (icône de la fenêtre) et du titre. Les boutons minimiser, maximiser et fermer seront toujours dessinés et fonctionneront. Ce que vous ne pourrez pas faire, c'est faire glisser ou redimensionner la fenêtre, car le cadre n'est pas vraiment là, rappelez-vous que la zone client couvre toute la fenêtre, nous avons simplement demandé au système d'exploitation de dessiner le cadre sur la zone client.
Vous pouvez maintenant dessiner sur la fenêtre comme d'habitude, même sur le dessus du cadre. Vous pouvez même placer des contrôles dans la zone de légende.
Enfin, laissez le DWM s'occuper de la vérification des résultats pour vous, en appelant DwmDefWindowProc de votre WndProc
(avant que vous ne l'ayez traité). Il renvoie un booléen indiquant si le DWM a traité le message pour vous.
2 votes
Notez que vous ne devriez pas faire cela : msdn.microsoft.com/fr/us/library/aa974173(v=MSDN.10).aspx - "N'ajoutez pas de contrôles au cadre d'une fenêtre. Placez plutôt les contrôles à l'intérieur de la fenêtre."
3 votes
@Johannes, j'admets que parfois les apps qui font cela peuvent être ennuyeuses mais certaines apps le font très bien. Comme Ultramon qui met un bouton pour changer de moniteur sur le cadre qui semble très natif dans Windows 7. Il y a donc des applications qui le font avec goût.
3 votes
@Josh : Office 2007 l'a également fait (mais ce n'est pas la première fois que l'équipe d'Office ignore les directives Windows UX). Pourtant, ce document est écrit par des professionnels de l'UX et de simples développeurs (ou singes de code) travaillent dur pour les ignorer. Et pour le bien des utilisateurs, je pense qu'ils devraient se contenter de suivre le mouvement, à moins qu'ils ne puissent faire appel à des personnes ayant de l'expérience, des connaissances et, surtout, des tests d'utilisabilité, pour prouver le contraire.
3 votes
C'est pourquoi ce sont des directives et non des lois. Et si vous lisez toute la page, vous verrez de nombreux exemples de cas où les propres directives de conception de Microsoft, qui étaient autrefois considérées comme la voie à suivre, sont maintenant considérées comme mauvaises.
6 votes
J'adore les articles où MS ignore ses propres directives s3.amazonaws.com/github-images/blog/2012/gh4w/makingof/