46 votes

Contrôles de rendu sur le verre: solution trouvée, besoin de double tamponnage / perfectionnement

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:

Real textbox on glass

Il y a 2 domaines qui doivent être corrigés pour que ce soit vraiment utilisable, cependant.

  1. 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.
  2. 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;
        }
    }
    

6voto

Lazlo Points 1944

Voici une version avec beaucoup moins de scintillement, pas encore parfaite cependant.

 public class GlassControlRenderer : NativeWindow
{
    private Control Control;
    private Bitmap Bitmap;
    private Graphics ControlGraphics;

    private object Lock = new object();

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0x14: // WM_ERASEBKGND
                this.CustomPaint();
                break;

            case 0x0F: // WM_PAINT
            case 0x85: // WM_NCPAINT

            case 0x100: // WM_KEYDOWN
            case 0x101: // WM_KEYUP
            case 0x102: // WM_CHAR

            case 0x200: // WM_MOUSEMOVE
            case 0x2A1: // WM_MOUSEHOVER
            case 0x201: // WM_LBUTTONDOWN
            case 0x202: // WM_LBUTTONUP
            case 0x285: // WM_IME_SELECT

            case 0x300: // WM_CUT
            case 0x301: // WM_COPY
            case 0x302: // WM_PASTE
            case 0x303: // WM_CLEAR
            case 0x304: // WM_UNDO
                base.WndProc(ref m);
                this.CustomPaint();
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    private Point Offset { get; set; }

    public GlassControlRenderer(Control control, int xOffset, int yOffset)
    {
        this.Offset = new Point(xOffset, yOffset);
        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, this.Offset); // -1, -1 for content controls (e.g. TextBox, ListBox)
    }
}
 

1voto

Arie Points 1918

J'ai eu un problème avec le scintillement avant (beaucoup de contrôles sur le formulaire, les contrôles de l'utilisateur). Essayé à peu près tout. C'est ce qui a fonctionné pour moi:

Avez-vous essayé de les mettre dans votre formulaire de classe?

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
            cp.ExStyle |= 0x00080000; // WS_EX_LAYERED
            return cp;
        }
    }

Et dans votre constructeur, vous devez activer le double buffering, sinon ça ne marche pas:

this.DoubleBuffered = true;
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

Il ne fonctionne que lorsque aero est activé, si non, il peut faire le scintillement encore pire.

et vous pouvez également ajouter cette

  protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;

                cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
            return cp;
        }
    } 

pour votre UserControls classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X