J'ai (enfin!) trouvé un moyen de rendre Windows.Contrôles de formulaires sur le verre qui ne semble pas avoir d'inconvénient majeur, ni aucun grand temps de mise en œuvre. Il est inspiré par cet article de Codé, qui, fondamentalement, explique comment nativement remplacer le tableau de contrôle de tirer sur eux.
J'ai utilisé cette approche pour rendre le contrôle d'une image et la peinture de retour avec GDI+ et le canal alpha au cours de la NativeWindow de la zone de peinture. La mise en œuvre est simple, mais il pourrait être perfectionnée pour la facilité d'utilisation, mais qui n'est pas le point de cette question. Les résultats sont, cependant, tout à fait satisfaisants:
Il y a 2 domaines qui doivent être corrigés pour que ce soit vraiment utilisable, cependant.
-
Double-buffering, parce que le scintillement entre cette superposition de l'image et le réel, le contrôle est fréquente et horrible (testez-vous avec le code). Réglage de la base de contrôle à double tampon avec
SetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true)
ne fonctionne pas, mais je crois que nous pouvons le faire fonctionner avec un petit essai et d'erreur. -
Certaines commandes ne fonctionnent pas. J'ai été en mesure de faire les travaux suivants:
- Zone de texte
- MaskedComboBox
- Zone De Liste Déroulante (DropDownStyle == DropDownList)
- ListBox
- CheckedListBox
- ListView
- TreeView
- DateTimePicker
- MonthCalendar
Mais je ne peux pas les faire fonctionner, bien que je ne vois pas pourquoi pas. Mon hypothèse est que la classe NativeWindow poignée je suis référencement le contrôle de l'ensemble, alors que j'ai besoin de faire référence à la "entrée" (textuel) de la partie de lui, probablement un enfant. Toute aide de la part WinAPI experts sur la façon d'obtenir que la fenêtre de saisie de la poignée est la bienvenue.
- Zone De Liste Déroulante (DropDownStyle != DropDownList)
- NumericUpDown
- RichTextBox
Mais la fixation de la double mise en mémoire tampon serait l' objectif principal pour la facilité d'utilisation.
Voici un exemple d'utilisation:
new GlassControlRenderer(textBox1);
Voici le code:
public class GlassControlRenderer : NativeWindow
{
private Control Control;
private Bitmap Bitmap;
private Graphics ControlGraphics;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0xF: // WM_PAINT
case 0x85: // WM_NCPAINT
case 0x100: // WM_KEYDOWN
case 0x200: // WM_MOUSEMOVE
case 0x201: // WM_LBUTTONDOWN
this.Control.Invalidate();
base.WndProc(ref m);
this.CustomPaint();
break;
default:
base.WndProc(ref m);
break;
}
}
public GlassControlRenderer(Control control)
{
this.Control = control;
this.Bitmap = new Bitmap(this.Control.Width, this.Control.Height);
this.ControlGraphics = Graphics.FromHwnd(this.Control.Handle);
this.AssignHandle(this.Control.Handle);
}
public void CustomPaint()
{
this.Control.DrawToBitmap(this.Bitmap, new Rectangle(0, 0, this.Control.Width, this.Control.Height));
this.ControlGraphics.DrawImageUnscaled(this.Bitmap, -1, -1); // -1, -1 for content controls (e.g. TextBox, ListBox)
}
}
Je serais vraiment heureux pour résoudre ce problème, et une fois pour toutes ont un vrai moyen de rendre sur le verre, pour tous .NET contrôles, sans WPF.
EDIT: chemins Possibles pour le double-buffering/anti-scintillement:
- Retrait de la ligne,
this.Control.Invalidate()
supprime le scintillement, mais les sauts de la saisie dans un textbox. -
J'ai essayé de la WM_SETREDRAW approche et la SuspendLayout méthode, avec pas de chance:
[DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam); private const int WM_SETREDRAW = 11; public static void SuspendDrawing(Control parent) { SendMessage(parent.Handle, WM_SETREDRAW, false, 0); } public static void ResumeDrawing(Control parent) { SendMessage(parent.Handle, WM_SETREDRAW, true, 0); parent.Refresh(); } protected override void WndProc(ref Message m) { switch (m.Msg) { case 0xF: // WM_PAINT case 0x85: // WM_NCPAINT case 0x100: // WM_KEYDOWN case 0x200: // WM_MOUSEMOVE case 0x201: // WM_LBUTTONDOWN //this.Control.Parent.SuspendLayout(); //GlassControlRenderer.SuspendDrawing(this.Control); //this.Control.Invalidate(); base.WndProc(ref m); this.CustomPaint(); //GlassControlRenderer.ResumeDrawing(this.Control); //this.Control.Parent.ResumeLayout(); break; default: base.WndProc(ref m); break; } }