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