sábado, 19 de julio de 2008

Reflection - Cambiar la propiedad a asignar dinámicamente

Reflection Oriented Programming es una técnica que se deriva de la programación orientada a objetos y que extienden el poder común de los lenguajes procedurales, el hecho de tomar la meta-information de una clase para generar instancias de nuevos objetos basados en ella permite crear sistemas bastante flexibles y nos da nuevas posibilidades.

Sin embargo trabajar con reflection suele ser una tarea agotadora y que demanda un conocimiento técnico que muchas veces el programador común no posee y cuyo aprendizaje puede ser complicado. Sin embargo .NET cuenta con un namespace especial para trabajar con reflection y que provee una serie de clases bastante sencillas de usar.

Hoy les voy a plantear la solución a un problema que de no ser por reflection parecería muy difícil de hacer.

Imaginemos que tenemos un control cualquiera y queremos modificar su altura o ancho según una configuración dada en un archivo de texto, XML o una tabla en una base de datos. Seguramente obtendremos una cadena con el nombre de la propiedad y el valor que le queremos dar. El problema es que ni en C# ni en VB.Net podemos hacer algo como esto:

Control control = new Control();

string propiedad = "Width";

string value = "10";

control.propiedad = value;

De hacerlo así obtendríamos este error.

'System.Windows.Forms.Control' does not contain a definition for 'propiedad' and no extension method 'propiedad' accepting a first argument of type 'System.Windows.Forms.Control' could be found (are you missing a using directive or an assembly reference?)  

¿Entonces que hacemos?

El siguiente código presenta una ventana sin más controles que un Panel, se creará un timer que cada segundo genere un número aleatorio (1 o 2) si el valor es 1 haremos que el Panel crezca 10 píxeles de altura y si es dos crecerá 10 píxeles de ancho. Lo interesante es que asignaremos la propiedad en base a constantes de tipo String, para ello usaremos la clase PropertyInfo del namespace System.Reflection.

using System;

using System.Windows.Forms;

using System.Reflection;

namespace ReflectionExample

{

    public partial class ReflectionForm : Form

    {

        public Random random;

        public int randomNumber;

        public int semilla = (int)DateTime.Now.Ticks;

        //Constantes que simulan un origen de dato externo

        public const string HEIGHT = "Height";

        public const string WIDTH = "Width";

 

        public ReflectionForm()

        {

            InitializeComponent();

        }

 

        public void ResizePanel()

        {

            //El random nos ayuda a simular un origen de datos externo que en este caso

            //lo hacemos mediante constantes de tipo string

            randomNumber = random.Next(1, 3);

            if (randomNumber == 1)

                SetProperty(resizedPanel, HEIGHT);

            else

                SetProperty(resizedPanel, WIDTH); 

        }

 

        private void SetProperty(Control control, string propertyName)

        {

            PropertyInfo propInfo = control.GetType().GetProperty(propertyName);

            int value = (int)propInfo.GetValue(control,null);

            value+=10;

            propInfo.SetValue(control, value, null);

        }

 

        private void ReflectionForm_Load(object sender, EventArgs e)

        {

            random = new Random(semilla);

            changeValuesTimer = new Timer();

            changeValuesTimer.Interval = 1000;

            changeValuesTimer.Tick += new EventHandler(changeValuesTimer_Tick);

            changeValuesTimer.Start();

        }

 

        void changeValuesTimer_Tick(object sender, EventArgs e)

        {

           ResizePanel();

        }  

    }

}

 

Este tipo de código es bastante útil en múltiples escenarios, uno de ellos podría ser el re-dimensionar una forma dependiendo de una configuración de dada o crear una funcionalidad de skins para nuestros sistemas, entre muchas otras.

Saludos!!

1 comentario:

Maik Lopez dijo...

Me encantó ésta información, solo tengo una duda. Si tuviera varios forms, y quisiera modificar algun control en específico de alguno de los forms, como podría hacerlo???

De antemano, gracias!!!