// // // // // // //

miércoles, 9 de septiembre de 2015

TableLayoutPanel without Flicker

    How many people suffer the flicker in C# Winforms Controls? I think a lot of it. There are a lot of solutions, but almost all of it are subclassing the control. But i found a simplier way, especially when you have your Form/UserControl already designed.

The typical Subclassing solution:

    The subclassing solution is to create a subclass of the Control and use this lines in all the constructors, after InitializeComponents:

DoubleBuffered = true;
SetStyle(ControlStyles.AllPaintingInWmPaint |
              ControlStyles.OptimizedDoubleBuffer |
              ControlStyles.UserPaint, true);

    But this can be really hard when you have a lot of controls or a Form already designed. Instead of this, we will use some reflection.

    I give you two options, set the DoubleBuffered property to true, or use the Styles if there isn't a DoubleBuffered property in the control. Here are the examples:

Setting DoubleBuffered to true:

    I used a DataGridView which actually have a DoubleBuffered property.

typeof(DataGridView).GetProperty("DoubleBuffered",System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(dataGridView, true, null);

Using SetStyle method:

    But what happen if the Control doesn't have DoubleBuffered? We can't use it so, we need a workaround. We should use the SetStyles method to do it but, surprisingly, you can't use it because is not public. So here's come the reflection method:

                BindingFlags.Instance | BindingFlags.NonPublic).Invoke(tableLayoutIngredients,
                new Object[] { ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true });

But I'm so lazy to copy paste all this code so...

Using Extension Methods: 

In order to don't repeat code, i made a class which adds two extension methods. The class is the following:

    public static class ControlExtensions
        public static Boolean SetDoubleBuffered(this Control control, Boolean isDoubleBuffered)
            PropertyInfo property = control.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            if (property != null)
                property.SetValue(control, isDoubleBuffered, null);
                return true;

            return false;

        public static Boolean SetStylesOfControl(this Control control, ControlStyles styles, Boolean value)
            MethodInfo method = control.GetType().GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic);

            if (method != null)
                method.Invoke(control, new Object[] { styles, value });
                return true;

            return false;

You can use it to set the values as follows:

// DataGridView have DoubleBuffered so this method will set it to true and return true.
// TableLayoutPanel doesn't have DoubleBuffered so use SetStyles.
tableLayoutIngredients.SetStylesOfControl(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true); 

But if you are working with Controls, and you want to set recursively this property, you can do it this way:

// If it returns false is beacause can't set DoubleBuffered (Maybe the control don't have it)
    // So we set the styles aaaand it's done.
    control.SetStylesOfControl(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);

This uses the boolean returned by try to set the DoubleBuffered to set the styles (or no). I hope this helps you.

No hay comentarios:

Publicar un comentario