Un Viewbox para Silverlight

 

En el pasado MVP Summit, al que tuve el honor de asistir como MVP en ASP.Net (aunque curiosamente las charlas en que he realizado han sido en su mayoría de WPF y Silverlight) comenté de la necesidad de tener el control Viewbox (de WPF) en Silverlight 2.0… tonto de mí no me percaté de que podría realizarlo yo mismo de una forma bastante fácil, ya que no es difícil el crear controles de usuario o clases derivadas en esta nueva versión. Afortunadamente Mi compañero de silla en esta sesión y uno de los mayores expertos en WPF y Silverlight, Miguel Jimenez me dijo “y por que no te lo haces tu mismo?” y bueno, tenía toda la razón así que aquí está… al menos una versión preliminar  que más o menos hace el trabajo que necesito que haga ;)

Básicamente heredé del panel de Canvas (primero intenté heredar de la clase panel, pero me dí cuenta de que sería más fácil haciendo un subclassing de la clase Canvas). Luego quería escalar los contenidos  cuando su espacio asignado (ancho y alto) creciese o decreciese.  Añadí dinámicamente, en el constructor de la clase, un ScaleTransform y un TranslateTransform, agregué estos a un TransformGroup y finalmente acabo asignando este TransformGroup a la propiedad  RenderTransform de la clase (propiedad heredada de la clase Canvas). Esto queda más o menos así:

//Initialization

this.ST_scale = new ScaleTransform();

this.TF_offset = new TranslateTransform();

 

//We add a transform with the Scale and Translate Transforms...

TransformGroup tg_transformViewbox = new TransformGroup();

tg_transformViewbox.Children.Add(this.ST_scale);

tg_transformViewbox.Children.Add(this.TF_offset);

 

//We add this Transform to the ViewBox (Subclassed from the Canvas class)

this.RenderTransform = tg_transformViewbox;

Luego, lo único que quedaba era implementar un override para la función ArrangeOverride (más información sobre esto aquí: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.arrangeoverride(VS.95).aspx ). En esta función determine el ratio de escalado obteniendo el ratio mínimo de los ejes X e Y, en base al tamaño original y el deseado. También ajusto el offset para los ejes X e Y, para ajustar el posicionamiento del layout en el eje que no sea el mínimo, para centrarlo.

        protected override Size ArrangeOverride(Size finalSize)

        {

            //We scale the contents based on the desired size width and height.

            double scaleX = finalSize.Width / this.DesiredSize.Width;

            double scaleY = finalSize.Height / this.DesiredSize.Height;

 

            //By default we only allow an "uniform" Stretch attribute option.. we could add the attribute and held the logic in this function.

            // See: http://msdn2.microsoft.com/en-us/library/system.windows.media.stretch.aspx and http://msdn2.microsoft.com/en-us/library/system.windows.controls.viewbox.stretch.aspx

            // To do this more seriously, we should also add the StretchDirection and Stretch properties too.

            if (scaleX > scaleY)

            {

                //Lowest scale ratio wins (Y axis is lower)

                this.ST_scale.ScaleX = scaleY;

                this.ST_scale.ScaleY = scaleY;

 

                this.TF_offset.X = (finalSize.Width - this.DesiredSize.Width * scaleY) / 2;

                this.TF_offset.Y = 0;

            }

            else

            {

                //Lowest scale ratio wins (X axis is lower)

                this.ST_scale.ScaleX = scaleX;

                this.ST_scale.ScaleY = scaleX;

                this.TF_offset.Y = (finalSize.Height - this.DesiredSize.Height * scaleX) / 2;

                this.TF_offset.X = 0;

            }

 

            return base.ArrangeOverride(finalSize);

        }

Y esto es todo lo que hay… No necesité sobrecargar la función MeasureOverride (ver http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.measureoverride(VS.95).aspx). Por otro lado si queréis investigar algo más sobre la creación de paneles personalizados en Silverlight 2.0, aquí hay un par de enlaces a tener en cuenta:

 

El código está subido aquí: <<<Viewbox.zip>>> pero tiene un pequeño problema del que me di cuenta después… (ahora lo comento)

Como nota, el código está licenciado bajo MS-PL  J (http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx).

El problema al que me refiero es que el Viewbox “original” al que me refiero solamente permite un único control y este que he desarrollado es un Canvas extendido, con lo que permite multiples controles posicionados juntos, así que los resécala todos siguiendo el layout, que es obviamente respetado.

En cualquier caso, este es el comportamiento que quería obtener, aunque no sigue el comportamiento del Viewbox, por lo que no está bien… Para que un Viewbox de WPF hiciese esto, debería incrustarle un Canvas dentro de el y luego posicionar los controles dentro de este Canvas..

 

Bueno, espero que sea de utilidad… [;)]

 

Crossposting desde bcngeeks
Published 30/4/2008 10:00 por Jose Luis Latorre
Comparte este post: