Programacion en Siverligth 2
Programacion en Siverligth 2
Programacion en Siverligth 2
0
Marino Posadas
ADVERTENCIA LEGAL Todos los derechos de esta obra estn reservados a Marino Posadas y Netalia, S.L. El editor prohibe cualquier tipo de fijacin, reproduccin, transformacin o distribucin de esta obra, ya sea mediante venta, alquiler o cualquier otra forma de cesin de uso o comunicacin pblica de la misma, total o parcialmente, por cualquier sistema o en cualquier soporte, ya sea por fotocopia, medio mecnico o electrnico, incluido el tratamiento informtico de la misma, en cualquier lugar del mundo. La vulneracin de cualesquiera de estos derechos podr ser considerada como una actividad penal tipificada en los artculos 270 y siguientes del Cdigo Penal. La proteccin de esta obra se extiende al mundo entero, de acuerdo con las leyes y convenios internacionales. Marino Posadas, 2008 Netalia, S.L., 2008
Programacin en Silverlight 2.0 CuadernoTcnico de dotNetMana n9 Autor: Marino Posadas Responsable editorial: Paco Marn Diseo de cubierta: Silvia Gil (letranorte) y Javier Roldn Maquetacin: Silvia Gil (letranorte) Editado por Netalia S.L. c/ Robledal, 135 28529 - Rivas Vaciamadrid (Madrid -Espaa) Tel. (34) 91 666 74 77 - Fax (34) 91 499 13 64 - http://www.dotnetmania.com Con el patrocinio de MSDN Espaa.
Precio: 24,50 Dposito Legal: M-52558-2008 ISBN: 978-84-934895-8-8 Impreso en Madrid (Espaa) en noviembre de 2008 por Grficas Marte
Creo que parte de mi amor a la vida se lo debo a mi amor a los libros Adolfo Bioy Casares A Milagros, por su paciencia y apoyo. Los amigos son como la sangre, cuando se est herido acuden sin que se los llame Annimo A Paco Morero y a Paco y Pilar, por su amistad. Confiamos demasiado en los sistemas y muy poco en los hombres Benjamin Disraeli A Luismi, alguien de confianza.
Fuera del perro, un libro es el mejor amigo del hombre; dentro del perro, probablemente est demasiado oscuro para leer Groucho Marx
Agradecimientos
Varias personas han aportado, de una forma u otra, ideas, o me han apoyado en la medida de sus posibilidades. Vaya aqu mi agradecimiento a algunos de ellos, y los que habindolo hecho no estn en la lista, que lo achaquen a mi despiste habitual: mea culpa. Entre ellos, quiero recordar a mi grupo favorito en Microsoft Ibrica, los chicos/as del DPE: Alfonso Rodrguez y Beatriz Ordoez que confiaron en m para este trabajo, David Carmona, que me sugiri ideas desde el inicio para la estructura del libro; David Salgado, a quien sigo en su blog, siempre al da, Antonio Gmez, que, pidindonos siempre renovacin, nos mantiene alerta, y muchos otros, como Csar de la Torre, Aurelio Porras, Isabel Gmez... sois geniales! Al grupo de MVP espaoles (especialmente los de desarrollo, con quienes tengo ms contacto), con Cristina Gonzlez a la cabeza. Muchas veces habis apoyado mi trabajo, y quiero recordaros aqu: Jorge Serrano, Octavio Hernndez, Guillermo Som, Miguel Egea, Eladio Rincn, Salvador Ramos, Daniel Seara, Jos Manuel Alarcn, Miguel Jimnez, Jos Miguel Torres, Rodrigo Corral, Ivn Gonzlez, Unai Zorrilla, Alejandro Mezcua, Lluis Franco y no sigo que se me acaba el espacio (y el tiempo). Y a muchos otros compaeros de profesin de diversos sitios con los que me unen intereses comunes o ideas similares: Adolfo Wiernik, Dino Esposito, Miguel Katrib, Brian Wheeler, los chicos de SecondNug, Yamil Hernndez, Luis Fraile, Jess Lpez, Jos Luis Latorre, y en general, a los compaeros de trabajo que me han apoyado, antiguos y actuales. Gracias a todos, perdn por las omisiones involuntarias, y espero que esto os sirva de introduccin a Silverlight 2.0.
ndice
Platform Adaptation Layer (PAL) . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 El modelo desde el punto de vista del desarrollador . . . . . . . . . . . . . . 30
La creacin del control Silverlight . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Ejemplo bsico de programacin con Silverlight 1.0 usando JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Proceso recomendado para la actualizacin del cdigo 1.0 a 2.0 . 33 Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Probando la solucin inicial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .56 Trabajo con objetos de dibujo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .58 Cambio del punto de entrada de la aplicacin . . . . . . . . . . . . . . . . .60 Trabajando con la Ventana de diseo . . . . . . . . . . . . . . . . . . . . . . . . . . .61 La superficie de diseo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .61 Brushes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .63 ImageBrush . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .64 VideoBrush . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .66 Otros Brushes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .67 Otras sub-ventanas de propiedades . . . . . . . . . . . . . . . . . . . . . . . . . . . .69 Bsqueda de propiedades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .70 Un pequeo visor de fotografas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .71 Programando un procedimiento de evento . . . . . . . . . . . . . . . . . . .72 Ajustando el control para efectos especiales . . . . . . . . . . . . . . . . . .74 Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .76
10
pgina
Formas (Shapes) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91 Los objetos Line, PolyLine y Polygon . . . . . . . . . . . . . . . . . . . . . . . .94 Geometras y el objeto Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .96 PathGeometry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .98 Arcos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .99 Curvas Bzier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .101 Una nota sobre la conversin automtica de formatos en ficheros XPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .102 Recortes mediante geometras . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104
5. Controles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .105
Controles disponibles en Silverlight 2.0 . . . . . . . . . . . . . . . . . . . . . . . . .105 Controles que muestran/admiten texto como funcin primaria . . . . .107 Peculiaridades del ListBox y los elementos colectivos . . . . . . . . . . . . .110 Controles para ayuda de la experiencia visual (de uso directo . . . . . .111 Una primera prueba con manejo de eventos . . . . . . . . . . . . . . . . . . . . .115 Controles multimedia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .117 El resto de controles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .117 ContentControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .118 Etiquetas flotantes (ToolTip y ToolTipService) . . . . . . . . . . . . . . . . .120 Los Presenters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .121 InkPresenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .121 Thumb,ToggleButton y RepeatButton . . . . . . . . . . . . . . . . . . . . . . .122 Otros controles disponibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123 Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123
11
El sistema de eventos en Silverlight 2 . . . . . . . . . . . . . . . . . . . . . . . .126 Bubbling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .127 Estilos y plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .130 Uso de estilos para cambiar la apariencia de un conjunto de controles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .131 Asignacin Late Binding de una plantilla . . . . . . . . . . . . . . . . . . . . . .134 ContentPresenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .135 Edicin de estilos y plantillas desde Expression Blend . . . . . . . . . .136 Silverlight 2.0 Animation System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .137 Animaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .137 Animaciones en Silverlight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .138 Transformaciones vs. animaciones . . . . . . . . . . . . . . . . . . . . . . . . . . .138 Transformaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .139 Propiedades RotateTransform y ScaleTransform . . . . . . . . . . . . . . .139 Creacin de transformaciones mediante cdigo . . . . . . . . . . . . . . .141 Matrices de transformacin y TransformGroups . . . . . . . . . . . . . . .141 Animaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .141 Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .142 El trigger ms simple slo en cdigo XAML . . . . . . . . . . . . . . . . . .142 Respuesta a eventos mediante cdigo . . . . . . . . . . . . . . . . . . . . . . .144 Creacin de animaciones mediante cdigo . . . . . . . . . . . . . . . . . . .145 Lneas de tiempo y KeyFrames . . . . . . . . . . . . . . . . . . . . . . . . . . . . .146 Interpolacin no-lineal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .149 Visual State Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .150 El modelo Parts-And-States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .150 Partes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .151 Estados y grupos de estado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .151 Deconstruyendo a Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .152
pgina
12
Utilizacin de Visual State Manager para la personalizacin de una interfaz de usuario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .153 Creacin de estados y transiciones propios con VSM . . . . . . . . . . .154 Construccin de un control personalizado . . . . . . . . . . . . . . . . . . . . . .160 Pasos tpicos en la creacin de un control de usuario . . . . . . . . . .160 El control TextoDNI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .161
13
Buenas prcticas en la construccin de aplicaciones Silverlight 2 .207 Capas de presentacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .207 Controles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .207 Media y grficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .208 Navegacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .208 Gestin de estados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .209 Validacin de datos de entrada . . . . . . . . . . . . . . . . . . . . . . . . . .209 Cachs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .210 Accesibilidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .210 Comunicaciones entre capas . . . . . . . . . . . . . . . . . . . . . . . . . . . .210 Seguridad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .211 Acceso a datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .211 Excepciones y depuracin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .212 Distribucin de las aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .212 Optimacin de la experiencia inicial . . . . . . . . . . . . . . . . . . . . . . . . .212 La experiencia de instalacin . . . . . . . . . . . . . . . . . . . . . . . . . . . .213 Algunas tcnicas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .214 Acceso a recursos de la aplicacin . . . . . . . . . . . . . . . . . . . . . . . . . .214 Tratamiento de recursos en tiempo de compilacin . . . . . . . . . . . .215 Manejo de fuentes adicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .216 Configurando un servidor Web para el alojamiento de Silverlight . . . .216 Valores instalados de forma predeterminada en IIS 7.0 sobre Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .219 Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .219
14
pgina
captulo
Suponemos que el lector ha odo hablar de Silverlight en el pasado. Al menos, de la primera versin (1.0), aparecida de forma oficial en septiembre de 2007. En el momento de escribir estas lneas acaba de aparecer la versin 2.0 final, y aunque iniciamos el trabajo con la primera beta, hemos ido revisando los cambios a medida que se producan, pudiendo, finalmente, contar con la RTW (Release to Web) con tiempo suficiente como para poder verificar todo de nuevo. El propsito de este libro es suministrar al lector los conocimientos necesarios para empezar a construir aplicaciones con la versin 2.0 de Silverlight, abordando para ello todos los procesos fundamentales: la eleccin del entorno de trabajo, el anlisis de la arquitectura y los modelos de desarrollo, la construccin de interfaces de usuario (desde Visual Studio 2008 y tambin desde Expression Blend 2.0 SP1), el acceso a la informacin del sistema y a servicios Web para la lectura/escritura de datos (en sus diferentes opciones) y los mecanismos de instalacin y puesta a punto de las aplicaciones finales. Para todo lo referente a la documentacin on-line, as como los enlaces de descargas relacionados, Microsoft ha creado el sitio http://silverlight.net, donde se pueden encontrar las herramientas necesarias para la instalacin del entorno de desarrollo: SDK, documentacin adicional (en ingls, de momento) en forma de borradores de trabajo, ejemplos (vinculados a su versin correspondiente) y vdeos ilustrativos de su funcionamiento, dirigidos por algunos de los principales evangelistas y responsables del producto: Jessy Liberty, Tim Heuer, Joe Stegman, Scott White, etc. En la parte final de esta obra, se aaden un conjunto de referencias tiles, tanto desde el punto de vista bibliogrfico, como de blogs recomendadas que mantienen informacin actualizada sobre esta tecnologa.
pgina
15
Requisitos previos
Para la compresin del texto, se requiere el conocimiento siquiera superficial de los fundamentos de la programacin con .NET Framework y su herramienta de desarrollo principal, Visual Studio, as como conocimientos generales de la programacin para Internet, y tecnologas asociadas (incluyendo lenguajes de marcas, y concretamente, XML). Respecto a los lenguajes de programacin, puede utilizarse cualquiera de los dos principales soportados por Visual Studio (C# y Visual Basic .NET), si bien nosotros utilizaremos C# en los ejemplos que ilustran esta obra, aunque existe ms cdigo XAML que C#. Para una profundizacin ms provechosa en el contenido, es aconsejable el conocimiento (al menos a nivel introductorio) de la tecnologa de presentacin introducida por .NET Framework 3.0: Windows Presentation Foundation, as como de los mecanismos implicados en el acceso datos mediante servicios Web (tradicionales o basados en Windows Communication Foundation).
16
>>
por lo que preferimos enmarcar la tecnologa en su justo contexto dentro de las aplicaciones RIA (Rich Internet Applications), y definirla como un complemento multi-plataforma para diversos navegadores de Internet que incorpora las capacidades de Windows Presentation Foundation, y otras propias de .NET Framework, para permitir la construccin de aplicaciones RIA.
Aplicaciones RIA
Presentamos, por tanto, Silverlight como una tecnologa para la construccin de una nueva generacin de aplicaciones, denominada aplicaciones RIA, que pretende conjugar lo mejor del mundo Web con las ventajas de las aplicaciones de escritorio: una excelente experiencia de usuario y la distribucin remota de la aplicacin. La riqueza a la que hace mencin el nombre es sobre todo una riqueza de recursos de interfaz de usuario: precisamente lo que ms se echaba de menos en las aplicaciones Web. De hecho, en esta versin est disponible un importante subconjunto de la funcionalidad presentada por WPF, as como soporte de interoperabilidad, acceso a recursos, lenguajes dinmicos, multimedia, acceso a datos, etc. En general, podemos enumerar las ventajas principales de las aplicaciones RIA como las siguientes1: Balance cliente/servidor. Eficiente comunicacin asncrona. Reduccin del trfico de red . No necesitan instalacin (acceso va Web) y las actualizaciones hacia nuevas versiones son automticas. Estn soportadas por las plataformas y navegadores ms populares del mercado. En el caso de Silverlight, estas plataformas son Mac y Windows (y, a travs del proyecto Moonlight, ligado a Mono (http://www.mono-project.com/ Moonlight), se est realizando la implementacin para Linux). Los navegadores que lo soportan: Internet Explorer, Firefox y Safari. Adems, el equipo de desarrollo de Opera trabaja con el de Silverlight, para que en una prxima versin, ya se encuentre disponible para este navegador y, respecto a Google Chrome, aunque las primeras pruebas daban fallos o funcionaban de forma ms o menos aleatoria parece que ya la RTW tiene un alto nivel de compatibilidad con la versin para desarrolladores (Google Chrome Build 1251) y ser totalmente compatible en prximas versiones de este navegador. Es menos probable la infeccin por virus, que utilizando, por ejemplo, programas ejecutables.
Citado de Wikipedia: http://es.wikipedia.org/wiki/Rich_Internet_A pplication
pgina
1
17
Ms capacidad de respuesta, ya que el usuario interacta directamente con el runtime, sin necesidad de recargar la pgina. Ofrecen aplicaciones interactivas que no se pueden obtener utilizando solo HTML, incluyendo arrastrar y pegar, clculos en el lado del cliente sin la necesidad de enviar la informacin al servidor, etc. Obviamente, las aplicaciones RIA tambin tienen sus inconvenientes, aunque se est trabajando en minimizar la mayora de ellos: Ejecucin en SandBox (depende de la configuracin del cliente, aunque tambin puede considerarse una ventaja desde el punto de vista de la seguridad y las acciones permitidas en la mquina del cliente). Que est deshabilitada la opcin de Scripting en el navegador (esto afecta principalmente a la versin 1.0, basada en este lenguaje, aunque el tratamiento de errores predeterminado se gestiona mediante cdigo JavaScript tambin en la versin 2.0). El tiempo de descarga de la aplicacin (si bien es normalmente mnimo y comparable muchas veces al de la descarga de los grficos de una pgina). Cierta prdida de visibilidad en los motores de bsqueda (aunque se est trabajando muy positivamente en ese sentido).
18
>>
Los datos ledos pueden usar XML formateado. Multi-navegador sin necesidad de complementos (add-ins). Como otros RIA, no se adapta bien a la optimizacin de los motores de bsqueda. Problemas con clientes con Scripting deshabilitado. JavaFX Complemento de la familia de herramientas de Java. Sirve para aplicaciones de escritorio, mviles y aparatos electrnicos (con ese soporte). Toda la programacin en Java. (necesita, lgicamente, la JavaVM). Google Gears Acceso desconectado a servicios on-line. Instala un motor de bases de datos basado en SQLite en el cliente, para cachear informacin de la aplicacin y permitir su uso posterior. Para cualquier operacin, la actualizacin se difiere en caso de existir una conexin disponible. No es un RIA, propiamente dicho, pero la promesa es que se acerque mucho, al solventar el problema de las conexiones.
Arquitectura de Silverlight
MSDN, en su pgina http://msdn2.microsoft.com/en-us/library/bb404713.aspx, presenta un esquema grfico muy aclaratorio de los constituyentes principales de la arquitectura de Silverlight 2.0 comparada con la versin anterior. Podemos distinguir en esta arquitectura tres elementos principales: Un ncleo de presentacin (Presentation Core), que dispone de gestin de derechos digitales. Un subconjunto de .NET, que se denomina en el grfico .NET for Silverlight, que, si bien no contiene toda la riqueza que se encuentra en .NET 3.5, asombra por la cantidad de caractersticas incluidas. Como puede verse en el grfico, es la parte del complemento que ms novedades presenta respecto a su antecesor (en acceso a datos, lenguajes dinmicos, controles WPF, soporte de WCF y muchas de las libreras bsicas de .NET 3.5).
pgina
19
El instalador, que se ocupa del proceso de implantacin en el equipo cliente para usuarios iniciales y tambin de la actualizacin a nuevas versiones. Como puede observarse, el nmero de elementos nuevos en la versin 2.0, y la riqueza de stos, la convierten en una autntica plataforma RIA, que adems, se ve potenciada por la posibilidad de utilizar un IDE bien conocido, con todos sus recursos. A tenor del grfico, las diferencias entre ambas versiones son muy considerables, y hay autores que califican de hecho la versin 1.0 como tentativa, o primera aproximacin, con poco ms que algunas capacidades multimedia y de presentacin. El otro inconveniente de la primera versin era el lenguaje de desarrollo (el de presentacin era un pequeo subconjunto de XAML, muy inferior al actual). JavaScript no es para la gran mayora el ms idneo, aunque Visual Studio 2008 haya hecho un esfuerzo en ese sentido, y por primera vez contemos con caractersticas avanzadas para la codificacin con JavaScript como Intellisense y depuracin.
figura 1-1
La tabla siguiente es una explicacin ms detallada de los componentes principales de presentacin descritos en el grfico anterior:
pgina
20
>>
Caracterstica Input UI rendering Media Controls Layout Data Binding DRM XAML
Descripcin Maneja las entradas desde dispositivos hardware como el teclado, ratn, dispositivos de dibujo y otros sistemas de introduccin de datos. Interpreta grficamente vectores, grficos de bitmap, animaciones y texto. Controla la ejecucin y gestin de varios tipos de ficheros de vdeo y audio, como los formatos .wmp y mp3. Soporta controles extensibles que se pueden personalizar mediante estilos y plantillas. Habilita el posicionamiento dinmico de elementos de la interfaz de usuario. Habilita el enlace de objetos de datos a elementos de la IU. Habilita la gestin de derechos digitales de los recursos multimedia. Suministra un analizador de cdigo para XAML. Tabla 1: Capacidades principales de la versin 2.0 de Silverlight
Tecnologa DeepZoom
Otra novedad es la presencia de la tecnologa DeepZoom, que permite a los usuarios profundizar en el detalle de fotografas o collages de fotografas, permitiendo una transicin suave entre ellas, para obtener la sensacin de profundidad. Las imgenes se pueden escalar desde resoluciones de 2 3 mega-pxeles hasta un giga-pxel, pero el usuario no tiene que esperar a la descarga completa, gracias a la estructura subyacente que permite la descarga concreta de las zonas necesarias. Internamente, utiliza el formato de fichero XML.
Comparativa de versiones
Sobre las diferencias concretas entre ambas versiones, baste ver la relacin que incluimos en la tabla 2, para comparar entre ambas. Las mejoras de esta versin no son slo en cantidad (que tambin) sino en calidad. A eso nos referimos la calificar a Silverlight 2.0 como otro producto; en total hay 16 caractersticas fundamentales nuevas, respecto a la anterior.
pgina
21
Caractersticas
Silverlight 1.0 * * * * * * * * * * * * * * * -
Silverlight 2.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Animacin/Grficos 2D Soporte AJAX Multi-navegador (Firefox, IE, Safari, Opera, Google Chrome) Multi-plataforma: Windows, Mac (Linux en beta) Lenguajes de .NET Framework (Visual Basic, Visual C#, IronRuby, Iron Python) Integracin HTML DOM HTTP Networking Almacenamiento Aislado Soporte JavaScript JSON, REST, SOAP/WS-*, POX, y RSS Web Services (y soporte para Sockets) Acceso a redes entre dominios LINQ to Objects Soporte de posicionamiento en Canvas Soporte de posicionamiento en StackPanel, Grid y Panel Marco de controles administrados Completo conjunto de controles (TextBox, RadioButton, Slider, Calendar, DatePicker, DataGrid, ListBox, y otros) Integracin Deep Zoom Soporte de HTML administrado Manejo de excepciones administrado Multimedia: Proteccin de contenido Multimedia: Video 720P de Alta Definicin (HD) Multimedia: Soporte Audio/Video (VC-12 , H.264 WMV, WMA, MP3, AAC) Multimedia: Soporte de imgenes (JPG, PNG) Multimedia: Markers Framework bsico enriquecido (Genricos, colecciones) Mejoras de seguridad Controles Silverlight ASP.NET, como asp:media, asp:xaml, etc. Verificacin de seguridad de tipos Soporte de Windows Media Server Analizador (Parser) de XAML (basado en WPF) Objetos XMLReader y XMLWriter
Tanto HD DVD como Blu-Ray han adoptado VC-1 como estndar de vdeo obligatorio, lo que significa que sus dispositivos de reproduccin de vdeo deben ser capaces de decodificar y reproducir contenido de vdeo comprimido con formato VC-1. Windows Vista soporta reproduccin de HD DVD, incluyendo el decodificador VC-1 y componentes relacionados que son necesarios para la reproduccin de pelculas HD DVD codificadas con VC-1.2, y hay planes, igualmente, para un pronto soporte de Blu-Ray.
22
pgina
>>
Lenguajes dinmicos
Una de las novedades interesantes de esta versin es el soporte de lenguajes dinmicos: JScript, IronPython 2 y IronRuby. Para ello, Silverlight 2.0 incorpora el Dynamic Language Runtime (DLR), que permite la compilacin y ejecucin dinmica de lenguajes de script. Si la aplicacin hace uso de estos lenguajes, su compilador se empaqueta junto con el distribuible de la aplicacin (el fichero .xap). De hecho parece que la prxima versin de Visual Basic (VB 10 o VBx) ofrecer soporte para el DLR3. El lector podr encontrar abundantes ejemplos de utilizacin de estos dos lenguajes en Silverlight en el sitio oficial antes indicado.
23
nible en el sitio Web citado al principio. La buena noticia es que existen herramientas de desarrollo gratuitas: podemos usar Eclipse (mediante un plug-in suministrado por Soyatec o Microsoft Visual Web Developer Express SP1). Respecto al sistema operativo, se requiere Windows 2003 SP4 o superior (XP, Vista). En las demos, utilizaremos Visual Studio 2008 Team Suite Service Pack 1, debido a las ventajas que ofrece para el desarrollo con Silverlight respecto a sus antecesores, si bien pueden utilizarse otras ediciones de Visual Studio 2008: con solo instalar el SDK del producto, nos aparecern dos plantillas de proyecto Silverlight (Proyectos de Aplicacin Silverlight y Librera de Controles Silverlight) que quedan disponibles como un tipo de proyecto ms en los dos lenguajes predeterminados. El entorno es el usual de Visual Studio: tecnologa Intellisense, tanto en la edicin del cdigo XAML, como en JavaScript (caso de usar este lenguaje), junto a las ya tpicas capacidades de depuracin para todos los lenguajes disponibles, soporte sncrono de diseo segn se van editando elementos de la interfaz de usuario (y est anunciado un nuevo diseador visual al estilo del que ya contamos para ASP.NET o Windows Forms), ayuda local y en lnea, y los tpicos gadgets que son el da a da de Visual Studio.
24
>>
a. Navegar al directorio Temp (o abrir una ventana de comandos y teclear la secuencia: CD %Temp%. b. Crear desde ah un directorio para la instalacin. c. Copiar Silverlight.2.0_Developer.exe a ese directorio y ejecutar el instalador Silverlight_tools.exe. Con uno de estos procesos toda la maquinaria necesaria para el desarrollo, debiera de quedar lista para funcionar.
nota
En caso de que el usuario que lee estas lneas quisiera acceder al SDK e instalarlo antes de la aparicin de la versin definitiva del producto, debe tenerse en cuenta que, para minimizar los problemas de instalacin que pudieran surgir existen secuencias recomendadas de desinstalacin de versiones anteriores y se invita a seguir la propia gua de instalacin disponible en el sitio oficial antes citado.
pgina
25
El CoreCLR
El CoreCLR es un motor de ejecucin de muy poco peso en trminos de volumen de descarga (6 Mb), con una funcionalidad muy parecida a su versin superior. Las dos DLL principales de .NET (mscorwks.dll y mscorelib.dll), ya miden cada una casi lo mismo que todo el CoreCLR. La labor de reduccin y simplificacin de cdigo ha sido muy notable, siempre con la mirada puesta en garantizar la mxima compatibilidad. Precisamente por esta razn, el motor de ejecucin y la mquina virtual son iguales. Eso incluye el sistema de tipos, el Garbage Collector, el compilador JIT, el pool de hilos de ejecucin (thread pool), y otras partes fundamentales del motor de ejecucin. Como contrapartida, y dada la distinta naturaleza de las aplicaciones Web (ms simples y de corta ejecucin), el compilador JIT se ha enfocado principalmente en minimizar los tiempos de carga, en lugar de realizar otras optimizaciones ms complejas. Y se ha actuado de la misma forma respecto a otros modelos de soporte y optimizacin. Pero el cdigo MSIL y los metadatos usados en las aplicaciones Silverlight son los mismos que en las aplicaciones de escritorio. Adems, el hecho de que Silverlight no pretenda reemplazar al CLR principal, ha supuesto un gran cambio en el motor: el CoreCLR puede ejecutarse paralelamente (side-by-side) al CLR estndar5.
pgina
4 5
26
No confundir con el popular webmaster Guillermo Som el Guille...) Esto puede tener implicaciones futuras, como apuntamos despus.
>>
nota
Antes, era imposible ejecutar dos versiones del CLR desde el mismo proceso. Hay varias razones para esto, pero baste pensar que cada instancia del CLR asume que es la nica capaz de acceder a sus datos estticos. Si una variable existiera en dos versiones del CLR (pongamos 2.0 y 3.5), y ambas versiones fueran cargadas por el mismo proceso al mismo tiempo, ninguna de ellas podra cambiar datos en esa variable sin afectar al estado de la otra.
Por otra parte, hay poderosas razones para que ambas versiones del runtime (el estndar y el CoreCLR) puedan ejecutarse desde el mismo proceso: por ejemplo, sera imposible escribir una aplicacin WPF que alojase un control WebBrowser, si ste navegase hacia una pgina que contuviera un control Silverlight. De esa forma, y para otros escenarios, se garantiza una compatibilidad completa.
27
figura 1-2
De esta forma, el cdigo Transparente, solo puede llamar a otro cdigo Transparente o a cdigo Crtico con Seguridad. Y el cdigo con Seguridad, solo puede llamar al cdigo Crtico por encargo del cdigo de Transparente (de usuario). Ser labor del cdigo Crtico con Seguridad, el mantener las salidas y entradas de forma controlada para mantener la seguridad del sistema.
28
>>
lizando el Dispatcher, podemos actualizar la interfaz de usuario desde un hilo de ejecucin que no sea de interfaz de usuario (por ejemplo que ejecute otra accin en segundo plano). Aunque por compatibilidad se soportan API de bajo nivel para el tratamiento de hilos de ejecucin, tales como System.Threading.ThreadPool.QueueUserWorkItem y System.Threading.Monitor.Enter, se recomienda para esas tareas la utilizacin de System.ComponentModel.BackgroundWorker, ya que encapsula la actualizacin de la interfaz de usuario cuando concluye su tarea (dos trabajos en uno). Otro aspecto importante de las BCL es la utilizacin del Almacenamiento Aislado (Isolated Storage). Es la forma adecuada para que una aplicacin que se ejecuta en sandbox tenga acceso al sistema de ficheros y, aunque estaba presente desde la primera versin del Framework, aqu se potencia su utilizacin por razones de seguridad. Al igual que sucede con las cookies en aplicaciones Web, esto permite mantener estado entre distintas invocaciones en casos necesarios, y, aunque no est pensado para almacenamiento de informacin crtica como contraseas y dems, su ubicacin es segura, y su acceso se limita a la aplicacin que posee dicho almacenamiento.
29
ras (definiciones formales de las funciones). En otros casos, existe funcionalidad en las API de Windows que no est presente en MacIntosh, por lo que las funciones deben ser implementadas en su totalidad. Hay que notar que buena parte de esta implementacin se inspira en lo creado para Rotor (la API abierta de .NET, de nombre tcnico SSCLI (Shared Source Common Language Infrastructure)6, que funciona en varias versiones de sistemas operativos tipo UNIX, as como en Windows. Como consecuencia de sus objetivos ms limitados, PAL solo soporta el subconjunto funcional de Win32 necesario para el funcionamiento de Silverlight. No hay necesidad de soporte de registro, GDI+, o COM/COM+, con lo que se mantiene pequeo y rpido. Incluso algunas de las decisiones que se han tomado en la construccin del CLR podrn implementarse en la versin superior, ms adelante, como la ejecucin en paralelo.
figura 1-3
Modelo del esquema de objetos en una pgina que aloja un control Silverlight
30
pgina
Ver bibliografa.
>>
Pgina HTML -> Control host de Silverlight -> Contenido XAML dentro del control, comportndose de una forma similar a como lo hacen los Data Islands de XML, solo que produciendo la salida interpretada visualmente (rendered) que se espera de XAML (la figura 1.3 muestra un grfico de esta situacin). De forma que una pgina que contenga un complemento Silverlight, es una pgina Web clsica (HTML), con un elemento contenedor que sirve de host para el control Silverlight, y que establece en su definicin la versin del runtime que requiere para su funcionamiento.
31
listado 1-1 Documento HTML contenedor del control Silverlight <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Demo de Silverlight con JavaScript</title> <script type="text/javascript" src="Silverlight.js"></script> </head> <body> <!- Ubicacin del control Silverlight --> <div id="SilverlightControlHost"> </div> <script type="text/javascript"> // Funcin que crea el control Silverlight en el navegador crearControlSilverlight(); </script> </body> </html>
listado 1-2 Cdigo fuente de la funcin que instancia el control Silverlight function crearControlSilverlight() { // Cacheamos el control contenedor, para pasarlo a la funcin de // // instanciacin var Contenedor = document.getElementById("SilverlightControlHost"); Silverlight.createObjectEx({ source: "Diseo.xaml", // Fichero fuente XA ML parentElement: Contenedor, // Etiqueta DIV que aloja el control id: "SilverlightControl", // ID nico para el control properties: { // Propiedades iniciales del control (se width: "100%", // incluye la versin requerida) height: "100%", version: "1.0" }, events: { // Manejador del evento onLoad onLoad: Sys.Silverlight.createDelegate(Diseo, Diseo.handleLoad) } }); }
32
pgina
>>
listado 1-3 Cdigo XAML sencillo, que dibuja una elipse con relleno en la superficie del control <Canvas Width="300" Height="300" xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Ellipse Height="200" Width="200" Canvas.Left="30" Canvas.Top="30" Stroke="Black" StrokeThickness="10" Fill="LightBlue" /> </Canvas>
El resultado, muy simple en este caso, sera una imagen como la que vemos en la figura 1-4 (se asume que la figura dibujada es la inscrita en un cuadrado de 200 pxeles de ancho por otros 200 de alto, dando origen a un crculo y no a una elipse, al tratarse de un contenedor cuadrado):
figura 1-4
33
Si el usuario conoce Silverlight 2 Beta 1, podr comprobar igualmente que el nmero de cambios en la versin final respecto a aquella es considerable, y puede que parte de lo hecho tenga que ser retocado, o la funcionalidad haya sido asumida por otros elementos, o eliminada por aproximaciones distintas al mismo problema.
Conclusin
Vamos a continuar a partir de este punto con el estudio de lo que ofrece la versin 2.0 exclusivamente. El esfuerzo de divulgacin que est haciendo Microsoft a este respecto y la repercusin social en la blogosfera, nos indica que es un camino a seguir, y sin entrar en disquisiciones sobre diversas tecnologas competidoras para los desarrolladores de .NET, el poder abordar este tipo de desarrollos con todas las herramientas y bagaje tecnolgico al que estamos acostumbrados, es un motivo ms para perderle el miedo.
34
pgina
captulo
Veamos al final del captulo anterior que la programacin con la versin 1.0 se basa fundamentalmente en un fichero HTML o ASPX en el cual mediante cdigo JavaScript se instancia un control Silverlight, pasndole en la llamada los valores de configuracin inicial. Una vez instanciado el control, ste aloja un fichero XAML que es interpretado visualmente segn los patrones establecidos por el subconjunto de Windows Presentation Foundation presente en el runtime de Silverlight, disponiendo as de una riqueza visual de la que la carecamos anteriormente. Tambin apuntbamos que el desarrollo con la versin 2.0 del producto iba a ser abordado utilizando Visual Studio 2008 Service Pack 1 y Expression Blend 2.0 Service Pack 1. Vamos a comenzar por analizar la estructura de una aplicacin Silverlight tal y como la construye Visual Studio 2008 Service Pack 1, trabajando con la versin .NET Framework 3.5 Service Pack 1 (ambos en sus versiones finales).
35
La primera opcin que debe decidir el usuario en este escenario, es la de si quiere hacer las pruebas mediante una pgina predeterminada que se crea dinmicamente, o prefiere que el IDE construya un sitio Web completo, con todo lo necesario para proceder con la depuracin. Nosotros vamos a optar por la opcin de la creacin del sitio Web completo, para analizar la estructura de ficheros que genera el IDE. La caja de dilogo de seleccin que se le presentar al usuario es la de la figura 2-1:
figura 2-1
Donde la opcin Copy to configuration specific folders ofrece la posibilidad de copiar el fichero distribuible final (de extensin .xap), a una subcarpeta de configuracin concreta (como Debug o Release), o bien a la carpeta de destino predeterminada (ClientBin). Como resultado de este proceso inicial, contaremos con una solucin que incluye dos aplicaciones: un sitio Web de prueba que utilizar localhost como servidor por defecto1 y la aplicacin Silverlight 2.0. Se genera un sitio Web en el sistema de archi1
36
pgina
>>
vos del equipo local que tendr igual nombre que el de la aplicacin Silverlight ms el sufijo _Web, y se establecen las referencias necesarias. En cualquiera de los dos casos, el modelo de las plantillas de Silverlight implica la creacin de un sitio Web para la depuracin del componente; el hecho de que se prefiera trabajar con una simple pgina de pruebas, al estilo de la versin 1.0, no afecta a las capacidades de depuracin desde Visual Studio. Podemos ver en la figura 2-2 el resultado completo de todo este proceso.
figura 2-2
La aplicacin de inicio predeterminada ser la del sitio Web creado para pruebas, donde encontramos 3 pginas Web: dos con el nombre de la aplicacin ms el sufijo TestPage, y extensiones .aspx y .html, ms la pgina Default.aspx y un conjunto de referencias a libreras, incluyendo la de la aplicacin Silverlight que est construyendo. Adems, es posible aadir aplicaciones Silverlight a sitios Web existentes, tanto si son aplicaciones ya hechas, como si queremos aadir una nueva para complementar el sitio.
pgina
37
nota
Sobre el tema de servidores y alojamiento, ver el captulo final acerca de la configuracin en servidores y otros aspectos relacionados.
38
>>
se hace dentro de un elemento <div> donde se sita una etiqueta Object que es la encargada de llamar al complemento y pasarle los parmetros iniciales. Este proceso se maneja con el cdigo del listado 2-1.
listado 2-1 <!-- Runtime errors from Silverlight will be displayed here. This will contain debugging information and should be removed or hidden when debugging is completed --> <div id='errorLocation' style="font-size: small;color: Gray;"></div> <div id="silverlightControlHost"> <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> <param name="source" value="ClientBin/SilverlightA pplication1.xap"/> <param name="onerror" value="onSilverlightError" /> <param name="background" value="white" /> <param name="minRuntimeVersion" value="2.0.31005.0" /> <param name="autoUpgrade" value="true" /> <a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"> <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/> </a> </object> <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe> </div>
De todos estos parmetros pasados en la instanciacin, algunos valores son fundamentales: data que establece el tipo de objeto que se desea (Silverlight), y type, donde le indicamos la versin del control. Lo recomendable es poner la versin mnima que se necesita para su funcionamiento, si bien podemos indicar este aspecto con ms detalle en el parmetro de nombre minRuntimeVersion. Aqu, la secuencia application/x-silverlight2 significa Siverlight 2.0 versin final, como podr imaginarse el lector.
nota
39
pginas distintas para cada versin del control. En este caso, el objeto A pplication puede comprobar cul es la versin instalada del control y, en funcin de eso, lanzar una u otra pgina, o cargar distintas versiones de un control en la misma pgina (recordemos que el CoreCLR puede ejecutarse en paralelo). No obstante, debemos tener en cuenta que la versin 2.0 soporta toda la funcionalidad de la anterior sin necesidad de cambios, por lo que un cliente con Silverlight 2.0 instalado puede ver perfectamente elementos enriquecidos con Silverlight 1.0 sin ningn aviso por parte del navegador. Lo contrario no es cierto, por supuesto.
nota
listado 2-2
La presencia de la etiqueta IFrame vaca obedece a un comportamiento especial del navegador Safari. Se consigue as que Safar no site la pgina en cach, y por lo tanto permite que el control vuelva a cargarse si se retorna a una pgina Silverlight previamente visitada.
En el caso de la pgina ASP.NET, se utiliza una control especial de servidor para realizar la instanciacin: <asp:Silverlight>, que se encarga de las mismas labores que el anterior, y adems, permite otras posibilidades, como especificar qu queremos que vea el usuario en el caso de que Silverlight no se encuentre instalado en la mquina del cliente, y redirigir la salida hacia una interfaz amigable si se opta por la instalacin del complemento, como veremos a continuacin. El cdigo de esta etiqueta anterior tal como es creada por el IDE de Visual Studio es el siguiente:
<div style="height:100%;"> <asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SL-C2-1.xap" MinimumVersion="2.0.31005.0" Width="100%" Height="100%" /> </div>
40
pgina
>>
El elemento <asp:Silverlight>
Hay varias ventajas aadidas en la utilizacin del control <asp:Silverlight>: primero la presencia del atributo MinimumVersion, que permite establecer la versin del complemento mnima para soportar la funcionalidad de nuestra pgina. Por supuesto, el atributo Source hace referencia al fichero una vez compilado y listo para distribucin, pero podemos establecer todo un conjunto de propiedades visuales, como la anchura, altura, y muchas otras, como se muestra en el Intellisense asociado al elemento, que vemos en la figura 2-3.
figura 2-3
Y para los casos en los que el cliente no tenga instalado el complemento, si, optamos por convertir la etiqueta en contenedora de otros elementos (aadimos la etiqueta de cierre </asp:Silverlight>), veremos que el Intellisense nos ofrece un nico elemento posible: <PluginNotInstalledTemplate> donde podemos colocar cualquier secuencia de controles ASP.NET (o HTML) para elaborar una interfaz de usuario personalizada y que sea sta la que se le ofrezca al lector como alternativa en lugar de la predeterminada (el tpico logo de la aplicacin, con el enlace al sitio de descarga en Microsoft, etc.)2.
nota
Observe igualmente que el fichero de configuracin Web.config, contiene un buen nmero de referencias a libreras del CLR y otros valores de inters.
2 Por ejemplo, indicando al usuario qu aspecto tendra la pgina (o el control) tras instalar el control Silverlight 2.
pgina
41
Existen otras propiedades a tener en cuenta, de las cuales me gustara comentar las ms interesantes: EnableFrameRateCounter: permite visualizar en la parte inferior de la ventana del navegador los marcos por segundo que est procesando nuestro complemento Silverlight. til en depuracin de ciertas situaciones. Aparece como en la siguiente captura de pantalla: . EnableRedrawRegions: tambin til para depuracin. Si se establece su valor a True muestra cuando el control redibuja una regin. Por ejemplo, para un crculo animado se obtendra una salida similar a la siguiente:
figura 2-4
SplashScreen: permite cambiar la ventana de splash que muestra Silverlight por defecto cuando est cargando el complemento. El proceso no es trivial y Microsoft ha publicado una pgina explicativa que lo detalla paso a paso: http://www.silverlight.net/QuickStarts/BuildUi/SplashScreen.aspx. InitParameters: Es la forma de pasar variables de inicializacin. Por ejemplo, podemos incluir lo siguiente:
listado 2-3 InitParameters="BaseUrl=http://live.com,ResourceUrl=http://www.microsoft.com" //Y, ms adelante, en la clase A pp.xaml.cs private void A pplication_Startup(object sender, StartupEventA rgs e) { string baseUrl = e.InitParams["BaseUrl"]; string reasourceUrl = e.InitParams["ResourceUrl"]; this.RootVisual = new Page(); } // Los parmetros se pueden pasar en el constructor de la clase Page
42
pgina
>>
figura 2-5
De forma que el fichero .XA P al que referencia el control <asp:Silverlight>, es un entregable ms de los producidos por el proceso de compilacin. Esto es debido a que la DLL solo alberga el cdigo compilado, pero no otros recursos que podran estar disponibles para el control Silverlight, como pueden ser imgenes o ficheros multimedia. Esto tambin depender de cmo declaremos el elemento. La figura 2-53 muestra la relacin de inclusin e interoperabilidad de los componentes, junto a la estructura de ficheros.
3
interoperabilidad-de-java.html
pgina
43
De hecho, ya comentamos que el fichero .XA P es un fichero comprimido ZIP con la extensin cambiada. Si renombramos la extensin a .ZIP veremos que se puede descomprimir y contiene la DLL ms el fichero .manifest correspondiente. El resto de recursos que se usen en la aplicacin se compilan junto con la DLL que se distribuye, y la forma en que pueden ser accedidos depende un tanto de la propiedad Build Action asociada a cada recurso. En el captulo final dedicado a la instalacin y configuracin de aplicaciones, dedicamos un apartado a este tema.
44
>>
listado 2-4 public partial class A pp : A pplication { public A pp() { this.Startup += this.A pplication_Startup; this.Exit += this.A pplication_Exit; this.UnhandledException += this.A pplication_UnhandledException; InitializeComponent(); } private void A pplication_Startup(object sender, StartupEventA rgs e) { this.RootVisual = new Page(); } private void A pplication_Exit(object sender, EventA rgs e) {} private void A pplication_UnhandledException(object sender, A pplicationUnhandledExceptionEventA rgs e) { // If the app is running outside of the debugger then report the // exception using the browser's exception mechanism. On IE this will // display it a yellow alert icon in the status bar and Firefox will // display a script error. if (!System.Diagnostics.Debugger.IsA ttached) { // NOTE: This will allow the application to continue running after // an exception has been thrown but not handled. // For production applications this error handling should be // replaced with something that will // report the error to the website and stop the application. e.Handled = true; Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); }); } } private void ReportErrorToDOM(A pplicationUnhandledExceptionEventA rgs e) { try { string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace; errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n"); System.Windows.Browser.HtmlPage.Window.Eval( "throw new Error(\"Unhandled Error in Silverlight 2 A pplication " + errorMsg + "\");"); } catch (Exception) { } } }
pgina
45
Si compilamos la aplicacin tal cual est, el IDE nos indicar que la compilacin se ha realizado con xito, y la primera vez que se ejecuta, nos preguntar si deseamos habilitar la depuracin Web (ver figura 2-6). Si es as, se incluir automticamente en el fichero Web.config la informacin necesaria para permitirlo (bsicamente, el cambio de valor del atributo Debug a true, deshabilitado inicialmente). Por lo dems, no hay diferencias significativas en cuanto a las posibilidades de depuracin de que se dispone respecto a otras aplicaciones clsicas (ya sean Windows Forms o ASP.NET).
figura 2-6
46
>>
MessageBox.Show(this.Host.IsVersionSupported("2.1").ToString());
En este ejemplo, mostraramos si una hipottica versin 2.1 est, o no, soportada, obteniendo un resultado False. Las opciones posibles en la actualidad son las que describimos en la tabla adjunta, con sus respectivas referencias en la Web, y nmeros de versin.
Versin Silverlight 1.0 Silverlight 2 Beta 1 Silverlight 2 Beta 2 Silverlight 2.0 Tipo MIME application/x-silverlight application/x-silverlight-2-b1 application/x-silverlight-2-b2 application/x-silverlight-2 Nmero de versin 1.0 2.0.30226 2.0.30523 URL para instalacin http://go2.microsoft.com/fwlink/?LinkId=110408 http://go2.microsoft.com/fwlink/?LinkId=108182 http://go2.microsoft.com/fwlink/?LinkID=115261
2.0.31005.0 http://go2.microsoft.com/fwlink/?LinkID=108181
47
nota
A este respecto hay una recomendacin oficial con la que estamos totalmente de acuerdo: respetar el deseo del usuario de realizar la salida completa de la pgina, teniendo cuidado de no aadir en el manejador de ese evento cdigo cclico o re-entrante, tal como reiniciar la propiedad Source del control Silverlight: si el usuario quiere cerrar la ventana, evitemos la tentacin de despedirnos con un numerito.
48
pgina
captulo
Continuamos hablando de desarrollo en Silverlight 2, pero esta vez con Expression Blend 2.01 SP1 como herramienta. A partir de este momento, siempre que nos refiramos a Blend, estaremos haciendo mencin de esta edicin del producto. Empezaremos por hacer una pequea aplicacin que nos vaya mostrando las caractersticas de funcionamiento y comparando la organizacin de su interfaz de usuario (y la forma de operar en ella) con la de Visual Studio.
49
figura 3-1
Oferta inicial de proyectos en Blend 2.0 tras la instalacin del SDK 2.0 de Silverlight
En la figura 3-2, vemos la ventana Project: la misma estructura de ficheros y directorios que cabra esperar de una aplicacin Silverlight 2 iniciada con Visual Studio, incluyendo las referencias a las libreras (ver la seccin References de la figura 3-2), el fichero A pplication.manifest, que establece comportamientos de la aplicacin, y, claro est, los ficheros principales A pp.xaml y Page.xaml, con sus contrapartidas de clases en C#.
figura 3-2
50
pgina
>>
Veremos la diferencia respecto al proyecto creado con Visual Studio al abrir uno de los dos ficheros C#, ya que se lanza inmediatamente Visual Studio 2008, cargando el fichero, y manteniendo ambos sincronizados al cambiar de un entorno a otro (siempre tras un aviso al usuario de que el fichero se ha modificado en otra aplicacin exterior). Esto es as, porque, por el momento, Blend no soporta la edicin de ficheros para C# o VB.NET. En la parte central del IDE, el entorno nos recuerda mucho al de Visual Studio, con ventanas de edicin y diseo, que pueden compartirse para mostrar ambos aspectos simultneamente (opcin Split).
figura3-3
51
A la izquierda, en lugar de la tpica barra de herramientas con controles, disponemos de una serie de iconos que despliegan (con el botn derecho) mens laterales agrupados por acciones comunes de diseo y dibujo. Ah se encuentran las referencias a los controles principales, por categoras, junto con una ventana denominada Objects & Timeline, que nos ayuda a seleccionar los objetos y crear animaciones en el diseo. En el captulo 6, veremos cmo manejar esta opcin y la creacin de Transformaciones y Animaciones.
Controles y geometras
El desarrollador de Visual Studio est acostumbrado a que la Caja de herramientas contenga todo lo que la IU de su aplicacin pueda mostrar. Blend dispone (por el momento) de menos controles, pero, como contrapartida, permite crear otro tipo de objetos de IU: las Geometras. Son elementos de dibujo de carcter vectorial creados por el usuario en una sesin de dibujo, y por lo tanto no pueden estar predeterminados. Blend habilita para ello las herramientas Pen (dibujo a mano alzada), Pencil (dibujo vectorial asistido), Ellipse, Rectangle y Line. El resultado de estas sesiones de dibujo se expresa como elementos XAML vectoriales y por tanto de alta calidad grfica. El trabajo conjunto con ambos elementos permite conseguir interfaces creativas con gran sencillez.
nota
El lector puede comprobar esto ltimo practicando con los objetos Pen y
Pencil
52
pgina
>>
Y dnde estn los controles de interfaz de usuario? Al final de la lista de herramientas, en el apartado Assets, encontramos una ventana desplegable con la oferta de todos los controles disponibles, teniendo en cuenta que es de esperar que sigan creciendo paulatinamente a medida que el producto evolucione (as lo han anunciado oficialmente los jefes de producto, y de hecho ya est disponible el nuevo Silverlight Toolkit, como apuntamos al final del captulo 5.). Seleccionando la opcin All controls en la ventana Assets, accedemos a la lista completa de recursos visuales (algunos elementos no pueden ser considerados como controles, sino, ms bien, como partes integrantes de otros controles). Esta lista es la que nos muestra la figura 3-4.
nota nota
En Visual Studio 2008, no es posible arrastrar un control Silverlight directamente a la ventana de diseo. De momento, ha de hacerse en la ventana de cdigo, y el XAML generado se interpreta visualmente en la ventana de diseo.
Con Expression Blend disponemos de todos los recursos tpicos de una herramienta grfica, cosa que comprobamos con solo arrastrar un control a la ventana de diseo y observar los utensilios visuales que se activan automticamente y el cdigo XAML generado por el editor.
Un truco para conseguir elementos repetidos, consiste en usar la combinacin [CTRL]+[Drag&Drop] (Control + Arrastrar el elemento a otra zona visual). Al terminar el proceso se genera una copia del elemento arrastrado.
pgina
53
nota
Aunque no tiene que ver directamente con el desarrollo en Silverlight, la herramienta Expression Design 2, permite exportar diseos complejos en un formato utilizable por Blend.Y lo mismo sucede con algunas herramientas de terceros como Adobe Illustrator 3.
figura3-4
Librera de controles (Asset Library) disponible para una aplicacin Silverlight 2.0
54
pgina
>>
Categoras de controles
Desde el punto de vista iconogrfico, existen dos categoras de controles: aquellos que poseen su propio glifo diferenciador, y los que comparten un solo icono general ( ), como es el caso de HyperLinkButton, MediaElement, y otros. En todos los casos, el IDE generar el cdigo XAML correspondiente, si bien la opcin de Intellisense, se reduce por el momento al editor XAML de Visual Studio 2008. En realidad, esta diferenciacin iconogrfica no est hecha al azar. Solo poseen icono propio los elementos que, de forma predeterminada, muestran una interfaz bien definida. La documentacin de Blend, divide los controles en ms categoras: los que tienen un texto de cabecera (Headered Controls), los que contienen colecciones de otros elementos (Items Controls), los que poseen una propiedad Content que permite mostrar cualquier contenido incrustado en ella ( Content Controls), etc. Una vez elegido un elemento para trabajar con l, la ventana de propiedades muestra los valores que ese objeto tiene asignados: tanto explcitamente (por haberlo dibujado en la ventana de diseo), como los predeterminados. Y esto es aplicable tambin a otras capacidades, como el enlace a datos a travs de atributos DataContext y similares. Con estos sencillos principios puede el lector comenzar a hacer sus diseos visuales en Blend 2.0, y observar cmo el IDE siempre responde actualizando automticamente los cambios hechos en cualquiera de las ventanas de edicin.
Ejemplo Inicial
Empezamos con una prueba muy simple. Hemos creado un par de directorios en la aplicacin para albergar grficos y vdeos. Podemos hacerlo desde el men Project y seleccionar Create New Folder o directamente sobre el Explorador de proyectos con el botn derecho, tal y como muestra la figura 3-5. Una vez creado el directorio, copiamos y pegamos algunas imgenes para hacer referencia a ellas al programar el evento Click. Esto es as, porque, si seleccionamos la opcin Add Existing Item, lo que vamos a obtener es una referencia al fichero en cuestin, pero ste seguir estando su ubicacin original, y no dispondremos de una copia local para distribuir. Ms adelante volveremos en detalle a los recursos embebidos. Lo ms sencillo para tener una imagen en pantalla rpidamente es hacer doble clic sobre una de ellas: el diseador crear automticamente un objeto imagen (elemento <Imapgina
55
figura 3-5
Hay que recordar, que literalmente podemos aadir cualquier clase de fichero al directorio creado, por lo que los formatos soportados son algo muy a tener en cuenta. No todos los formatos grficos estn soportados por Silverlight 2 (por ejemplo, .Gif no lo est, de momento por problemas de integracin de sus capacidades de animacin, que Microsoft est considerando). Ver el captulo 1 para ms detalles sobre formatos soportados.
ge>) con la referencia correspondiente y unos valores de posicin y tamao acordes con las
medidas del grfico (el lector que tenga conocimiento del lenguaje XAML de WPF ver inmediatamente que se trata de un subconjunto funcional de ste). Si queremos probar con un elemento multimedia de tipo vdeo, podemos hacer lo mismo. Una vez seleccionado uno, comprobaremos cmo se genera automticamente un elemento <MediaElement>, que apunta al vdeo seleccionado, listo para reproduccin automtica nada ms cargar el control. Es ms, con las herramientas de la superficie de diseo tenemos la posibilidad de modificar intuitivamente el aspecto de cualquiera de los estos controles: tamao de las imgenes, rotar el vdeo, aplicar transformaciones, etc.
56
nota
>>
trol con una imagen y un vdeo funcionando. Blend 2.0 generar una nueva pgina para probar el control (llamada Default.htm), que contiene lo bsico para la instanciacin de un control Silverlight 2.0 tal y como se ha visto en el captulo anterior. Si el lector quiere verificar cul es exactamente el cdigo que se genera, puede hacerlo lanzando la aplicacin y abriendo el cdigo fuente desde el navegador. Con lo hecho hasta el momento, el cdigo producido tiene el aspecto que puede ver en el listado 3-1 (tngase en cuenta que incluye transformaciones como la rotacin del vdeo).
listado 3-1 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="SilverlightA pplication1.Page" Width="720" Height="496" Background="#FFD5D237"> <Grid x:Name="LayoutRoot" Background="#FFBDBC9D" > <Image Margin="352,144,-89.1989974975586,156.057998657227" Source="Imagenes/08.jpg" Stretch="Fill" RenderTransformOrigin="0.5,0.5"> <Image.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform A ngle="89.616"/> <TranslateTransform/> </TransformGroup> </Image.RenderTransform> </Image> <MediaElement Margin="52.2299995422363,94.7099990844727,232,112" x:Name="Bear_wmv" Source="Videos/Bear.wmv" Stretch="Fill" RenderTransformOrigin="0.5,0.5"> <MediaElement.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform A ngle="-19.41"/> <TranslateTransform/> </TransformGroup> </MediaElement.RenderTransform> </MediaElement> </Grid> </UserControl>
pgina
57
Y, si ejecutamos la aplicacin, obtendremos la salida visual del control Silverlight mostrado por la pgina en Internet Explorer 8 beta 2 que puede ver en al figura 3-6.
figura3-6
Vemos que Blend es muy intuitivo de manejar, y casi siempre nos garantiza una aproximacin al problema grfico a resolver desde el punto de vista del diseo. Por otro lado, la fidelidad del resultado en tiempo de ejecucin es idntica a la ofrecida por el IDE.
58
>>
Por ejemplo, supongamos que vamos a dibujar un crculo que contenga otra figura (un rectngulo de bordes redondeados), y que ste, a su vez, contenga (visualmente, no mediante cdigo) un control TextBox, que permita la edicin de texto. Esto implica el uso de tres controles XAML: un objeto Ellipse que, al coincidir los dos radios focales se convierte en una circunferencia, un rectngulo con los bordes redondeados (manipulando las propiedades RadiusX y RadiusY) y un Textbox, al que le hemos aplicado algunas propiedades visuales (en total, dos geometras y un control). Por lo dems, la contrapartida XAML de este diseo es simple, como se muestra en el listado 3-2.
listado 3-2 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="SilverlightA pplication1.Dibujos" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot" Background="White" > <Ellipse HorizontalA lignment="Stretch" VerticalA lignment="Stretch" Fill="#FF652DC2" Stroke="#FF000000" RenderTransformOrigin="3.20000004768372,2.48000001907349" Margin="112,56,144,56"/> <Rectangle HorizontalA lignment="Stretch" Margin="176,168,208,168" VerticalA lignment="Stretch" Fill="#FFD0C590" Stroke="#FF000000" RadiusX="40" RadiusY="40"/> <TextBox Margin="216,224,240,216" Text="TextBox" TextWrapping="Wrap" FontSize="24"/> </Grid> </UserControl>
Hay que tener en cuenta que no hemos creado celdas en el Grid contenedor. Por tanto, se asume la existencia de una sola fila y una sola columna. Tampoco hemos utilizado controles contenedores (elementos que hereden de Panel), por lo que la visibilidad en condiciones de solapamiento es la predeterminada en la, propiedad z-order, esto es, el primero que se dibuja va ms al fondo, y los dems se sitan por encima, y as sucesivamente. Esto puede cambiarse en cualquier momento.
pgina
59
En ejecucin, como en este ejemplo el control central es un TextBox, el usuario puede modificar el texto inicial, pero esa es toda la funcionalidad disponible (ver figura 3-7).
figura 3-7
Ventana de edicin y salida del ejemplo de grficos (la salida es idntica para FireFox y Safari)
No hemos codificado nada para recoger la entrada del usuario en el TextBox. Se trata, simplemente, de demostrar que el aspecto y el funcionamiento es equivalente a sus contrapartidas en Windows Presentation Foundation.
60
>>
listado 3-3 private void OnStartup(object sender, StartupEventA rgs e) { // Load the main control here this.RootVisual = new Dibujos(); //this.RootVisual = new Page(); (modificamos esta entrada) }
figura 3-8
La superficie de diseo
La superficie de diseo, muestra 3 modos de trabajo: XAML, Diseo y Split (mixto). Por defecto, se crea un elemento Grid (dentro de otro elemento UserControl) que presenta 2 modos operativos: modo Canvas y modo Grid, que pueden alternarse pulsando en el smbolo . La diferencia estriba en la forma de ubicar los elementos dentro del Grid. En modo Canvas, se utiliza posicionamiento absoluto, colocando los elementos mediante coordenadas respecto a su contenedor. En modo Grid (predeterminado), se utiliza el posicionamiento por fila y columna.
pgina
61
nota
Mediante la combinacin de teclas [CTRL]+[Rueda de Ratn] podemos cambiar el tamao del elemento UserControl en edicin. De la misma forma, si pulsamos la barra espaciadora, el cursor se convierte en una mano con la que podemos mover la superficie diseada en cualquier direccin. Adems, pulsando la tecla [Tab] aparecen y desaparecen las ventanas laterales.
Si trabajamos en modo rejilla (grid), lo ms probable es que necesitemos definir filas y columnas. Una vez establecido el modo, el proceso es muy sencillo: basta con pasar el cursor por cualquiera de las bandas que delimitan la superficie del control para que aparezca un cambio en el cursor, indicndonos que podemos sealar una lnea divisoria. Hay que observar el candado al lado de la lnea: la marca de bloqueo. Cuando pulsamos sobre l para cerrarlo, eso indicar que esa fila (o columna) tiene un alto o ancho fijo, como se comprueba en el cdigo XAML generado. El efecto final, junto al cdigo fuente, puede verse en la figura 3-9.
figura 3-9
Las marcas anteriores producen como salida el cdigo XAML del listado 34 (tengamos en cuenta que los nmeros de las filas y columnas en un grid comienzan por 0).
pgina
62
>>
listado 3-4 <Grid x:Name="LayoutRoot" Background="White" > <Grid.ColumnDefinitions> <ColumnDefinition Width="496"/> <ColumnDefinition Width="A uto" MinWidth="144"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="83.179"/> <RowDefinition Height="*"/> <RowDefinition Height="111.952"/> </Grid.RowDefinitions> <Grid Height="39" HorizontalA lignment="Left" Margin="0,-0.179,0,0" VerticalA lignment="Top" Width="0" Grid.Row="1"/> </Grid>
Brushes
En parte, Silverlight sigue los patrones de diseo visual que establece la especificacin de documentos XPS2, basada en XAML y presente en Windows Vista/2008 (y descargable para Windows XP). La tabla 1 recoge los tipos de brochas (brushes) definidos por esta especificacin, todos ellos disponibles para WPF, y, a excepcin de Visual Brush, tambin para Silverlight 2.0. Silverlight 2.0 presenta cinco3 tipos de brushes: SolidColorBrush, LinearGradientBrush, RadialGradienBrush, ImageBrush y VideoBrush, pero vamos a empezar por los ms mediticos: ImageBrush y VideoBrush, y comprobaremos lo sencillo que resulta su uso desde este entorno.
Nombre Solid Color Brush Image Brush Visual Brush Linear Gradient Brush Radial Gradient Brush Descripcin Rellena una regin con un color slido Rellena una regin con una imagen Rellena una regin con un dibujo Rellena una regin con un gradiente lineal Rellena una regin con un gradiente radial Tabla 1. Tipos de Brushes
2 3
Ver http://en.wikipedia.org/wiki/XML_Paper_Specification En realidad existe un sexto tipo: TileBrush, pero su uso parece dar algn problema de memoria.
pgina
63
ImageBrush
ImageBrush permite dibujar la superficie de un control o geometra utilizando para ello una imagen de la que dispongamos como recurso en nuestra aplicacin. Vamos a probarlo con un ejemplo sobre dos posibles destinatarios: un control Button, y una geometra en forma de dos tringulos conectados por una de sus aristas, de manera que podamos probar las intersecciones con la imagen. Lo nico que puede no resultar muy intuitivo, es que se precisa haber cargado la imagen que vamos a utilizar, antes de convertirla en un recurso de la aplicacin (o de la pgina activa). Para ello, basta con un doble clic sobre la imagen, y dejar que Blend nos cree el elemento asociado. A continuacin, con la imagen (elemento Image) seleccionada, desde el men Tools, optamos por Make Brush Resource -> Make ImageBrush Resource, y Blend nos crear un recurso asociado al control contenedor principal (o a toda la aplicacin, si optamos por ello), aadiendo una entrada XAML similar a la siguiente:
listado 3-5 <UserControl.Resources> <ImageBrush x:Key="ImageBrush1" ImageSource="Graficos/deepzoomcomposer.png"/> </UserControl.Resources>
La comprobacin visual aparecer en la solapa Resources de la ventana superior derecha, como se ve en la figura 3-10.
figura 3-10
Posteriormente, resultar muy sencillo hacer que cualquier control adopte esa imagen como fondo utilizando una propiedad adecuada del control (como Background), y estableciendo un enlace dinmico con l. Veremos todo esto con ms detalle ms adelante, pero el cdigo para vincular uno a otro es intuitivo y tiene esta forma:
pgina
64
>>
listado 3-6 <Button Height="184" HorizontalA lignment="Stretch" Margin="144,0,144,80" VerticalA lignment="Bottom" Content="Button" Background="{StaticResource ImageBrush1}"/>
Si preferimos que Blend realice la vinculacin por nosotros, tenemos dos opciones: arrastrar el recurso sobre el elemento al que queremos aplicarlo (y un men contextual nos permitir seleccionar a qu propiedad nos interesa vincular), o bien, con el elemento seleccionado, abrir el men contextual disponible al lado de cada propiedad que est sealado por un pequeo cuadrado (ver figura 3-11), y seleccionar el recurso (aqu, ImageBrush1):
figura 3-11
El mecanismo de vinculacin utiliza sintaxis de binding presentada en WPF e indica que existe un recurso accesible en el propio control que estamos creando, de nombre ImageBrush1 que es el que queremos usar. Para el caso de las geometras, el proceso es conceptualmente parecido, solo que la propiedad a vincular con el recurso grfico es la propiedad Fill del objeto Geometry que usemos, y en este caso no lo hacemos como un recurso de la aplicacin,sino indicndolo directamente en el cdigo XAML del objeto ImageBrush. Volveremos al tema del las geometras en el captulo 4, pero el aspecto que tiene la vinculacin por cdigo es el siguiente:
pgina
65
listado 3-7 <Path Stroke="#008000"> <Path.Fill> <ImageBrush ImageSource="Graficos/Silverlight2.0.png" Stretch="UniformToFill"/> </Path.Fill> <Path.Data> <PathGeometry> <PathFigure IsClosed="True" StartPoint="150,50"> <LineSegment Point="150,275" /> <LineSegment Point="500,50" /> <LineSegment Point="500,275" /> </PathFigure> </PathGeometry> </Path.Data> </Path>
Donde Path es el objeto tipo Geometry a usar en el dibujo, y est compuesto de dos atributos principales: Path.Fill, que determina como se rellena su interior, y Path.Data que define el dibujo en s (el contorno). El cdigo anterior produce una salida como la de la figura 3-12:
figura 3-12
VideoBrush
Como extensin de lo anterior, una capacidad de esta forma de relleno de elementos grficos es la de hacer que la brocha a utilizar sea un vdeo, y por lo tanto el contenidode fondo pase a ser dinmico. En principio, cualquier objeto del que podamos establecer su propiedad de fondo es vlido, incluyendo un TextBlock.
pgina
66
>>
listado 3-8 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="SilverlightDemo_blend.Demo_VideoBrush" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot" Background="Transparent" Width="450" Height="310" > <Border Background="Transparent" BorderBrush="Maroon" BorderThickness="12" CornerRadius="24" /> <MediaElement Source="Videos/B.wmv" x:Name="Video_Brush" Opacity="0" /> <TextBlock Text="dot Net Mania" TextWrapping="Wrap" FontSize="120" FontFamily="A rial" Margin="15,20,-210,0" FontWeight="Bold" Width="640" HorizontalA lignment="Center" > <TextBlock.Foreground> <VideoBrush SourceName="Video_Brush" Stretch="Fill" /> </TextBlock.Foreground> </TextBlock> </Grid> </UserControl>
El nico truco est en no mostrar el vdeo a pesar de que lo declaramos como MediaElement; para ello, establecemos su valor de Opacity a 0. Lo dems, resulta bastante evidente a la vista del cdigo, y similar al ejemplo anterior. En la salida, las letras del texto "dotNetMana" son pintadas con un popular vdeo de demostracin. Volveremos sobre el tema de los recursos ms adelante, pero sirva esta demo para comprobar las posibilidades de esta tcnica. Adems, el lector que pruebe el cdigo apreciar un efecto curioso: cada lnea completa de texto (aqu hay dos, pero funcionara con cualquier nmero de lneas) causa una reproduccin individual del vdeo (todas sincronizadas, ver figura 3-13).
Otros Brushes
Si optamos por rellenar un elemento mediante colores, el proceso es tambin muy intuitivo. Solo tenemos que escoger el objeto que queremos colorear, y observar el Panel de propiedades a la derecha. En la figura 3-14 puede verse la sub-ventana Brushes (el editor de brochas) y cmo podemos seleccionar la propiedad de destino (Background, Borpgina
67
figura 3-13
derBrush, Foreground y OpacityMask) y el tipo de brocha que vamos a usar para ello (el lec-
tor apreciar, si ha hecho el proceso anterior, que aparecen las brochas creadas por cdigo XAML como un recurso utilizable igualmente, en la solapa Resources). La oferta de propiedades a colorear depender del tipo de objeto con el que estamos trabajando. En este caso, para no mezclar ideas, hemos limpiado la superficie de diseo. Una vez seleccionado un elemento, elegimos en la banda inferior el tipo de brocha (las flechas en la imagen). Puede ser de un color solido (SolidColorBrush) o de tipo gradiente entre dos o ms colores (GradientBrush).
figura 3-14
68
pgina
>>
Tambin podemos establecer el valor exacto de un color a travs de los valores ARGB que lo componen, o introducir un valor hexadecimal. En el caso de optar por un gradiente de color, justo a continuacin tenemos la banda para sealar los puntos de variacin del gradiente. Cada una de las marcas que hagamos en esa banda va a definir en el cdigo un objeto GradienStop, que establece el final de un gradiente y el comienzo del siguiente, para los casos en que hay ms de dos. Por ltimo, en la zona ms inferior del grfico, se presentan los botones de seleccin de tipo de gradiente (puede ser lineal o radial). Disponemos tambin de una gama de opciones dentro de la ventana de edicin de brochas, para establecer las pautas de dibujo de los gradientes.
figura 3-15
69
Seleccionemos uno en el conjunto de controles. Pongamos que queremos situarlo en la fila y columna inferior derecha del control. Como se ve en la figura 3-16, Layout nos ofrece todas las propiedades disponibles, reconociendo dnde se encuentra ubicado el control.
figura 3-16
Los nombres de las propiedades resultan explicativos y en muchos casos son idnticos a sus contrapartidas en Windows Forms. No obstante, tenga en cuenta que la flecha que aparece en la parte inferior de esa sub-ventana, despliega otra (continuacin de sta) que permite hacer un ajuste ms fino de otras propiedades: anchuras y alturas mximas y mnimas, y propiedades del contenido del botn (el texto).
Bsqueda de propiedades
Hasta el momento, no hemos modificado el texto del botn para adaptarlo a la aplicacin que estamos haciendo. Aqu entra en juego una propiedad interesante del panel: como existen tantas propiedades, podemos utilizar la caja de texto para bsquedas, y segn pulsamos el nombre de la propiedad, el entorno ir mostrndonos exclusivamente aquellas cuyo nombre empieza por lo tecleado (figura 3-17). Ah, cambiamos el contenido del botn para que diga simplemente Pulse. Si queremos modificar las propiedades del texto escrito (tipo de letra, tamao, grosor, etc.), la siguiente sub-ventana (Text) nos habilita esa posibilidad (figura 3-18). Resulta igualmente intuitivo establecer el formato del prrafo ( ) y ajustar las caractersticas de sangrado ( ). Todos los elementos visuales que podemos utilizar aqu se encuentran representados por estas ventanas.
pgina
70
>>
figura 3-17
Caja de seleccin de propiedades en la parte superior del Panel y resultado de una bsqueda
figura 3-18
Finalmente, la sub-ventana Miscelnea, contiene referencias a un conjunto de propiedades varias que pueden ser muy distintas para cada control.
71
La verdad es que podemos usar varios controles distintos para mostrar una imagen. Desde el control lmage, que supuestamente es el recomendado, hasta Buttons, Rectangles, Borders, ListBoxes, etc. Bastar con que dibujemos el control correspondiente en la superficie de diseo, le asignemos el tamao y la ubicacin adecuada, y ms adelante, asignemos la pulsacin sobre el botn en el cdigo subyacente. El aspecto del control despus de aadir un botn y ubicarlo en la parte inferior derecha del Grid podra ser similar al siguiente:
figura 3-19
Puede ver el correspondiente cdigo XAML generado hasta el momento por Blend en el listado 3-10. Para asignar el evento correspondiente a la pulsacin del botn, seleccionaremos el smbolo , que abre la Ventana de eventos del control. En el evento Click, y nada ms escribir el nombre del manejador (Siguiente_Portada), veremos cmo se abre Visual Studio 2008 con el mismo proyecto y la ventana de cdigo correspondiente a Page.xaml.cs, donde ya aparece creado el procedimiento de evento.
72
>>
listado 3-10 <UserControl xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml x:Class="SL_Desarrolladores1.Page" Width="640" Height="480"> <Grid x:Name="LayoutRoot"> <Grid.Background> <LinearGradientBrush EndPoint="320,480" StartPoint="320,0" SpreadMethod="Pad" MappingMode="A bsolute"> <GradientStop Color="#FF000000"/> <GradientStop Color="#FF484259" Offset="1"/> <GradientStop Color="#FF4616A 8" Offset="0.26800000667572021"/> <GradientStop Color="#FF22906A " Offset="0.48199999332427979"/> <GradientStop Color="#FF6D316F" Offset="0.69599997997283936"/> </LinearGradientBrush> </Grid.Background> <Grid.ColumnDefinitions> <ColumnDefinition Width="496"/> <ColumnDefinition Width="144"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="83.179"/> <RowDefinition Height="*"/> <RowDefinition Height="111.952"/> </Grid.RowDefinitions> <Button HorizontalA lignment="Stretch" Margin="0,0,8,8" Width="A uto" Content="Pulse" Grid.Row="2" Grid.Column="1" VerticalA lignment="Stretch" x:Name="Botn" FontFamily="Verdana" FontSize="24" FontWeight="Light" FontStretch="Condensed" FontStyle="Italic" BorderThickness="3,3,3,3" TextA lignment="Center"/> </Grid> </UserControl>
listado 3-11 //Declaraciones de variables de control int TotalPortadas = 7; int contador = 0; . private void Siguiente_Portada(object sender, RoutedEventA rgs e) { contador++; if (contador == TotalPortadas + 1) contador = 1; Uri uri = new Uri("Imagenes/portada00" + contador.ToString() + ".jpg", UriKind.Relative); ImageSource logo = new System.Windows.Media.Imaging.BitmapImage(uri); Portada.SetValue(Image.SourceProperty, logo); }
pgina
73
Normalmente, los atributos de imgenes se relacionan con elementos del tipo Bitmap representados por la clase BitmapImage. Pero en el caso del elemento Image ("Portada"), se solicita directamente una propiedad Source, que vemos con el nombre de SourceProperty una propiedad de dependencia4, con la que poder generar el bitmap. A su vez, ImageSource requiere una ubicacin vlida para la que el cdigo tenga permiso de acceso (Uri). Ms adelante veremos otros mtodos similares, aunque alternativos, de asignar imgenes a controles de la interfaz de usuario.
figura 3-20
74
pgina
>>
Lo nico que falta en el cdigo es rellenar la imagen del reflejo (tambin de forma dinmica), y recordar que cada nueva imagen ser mostrada con las caractersticas visuales de su contenedor.
listado 3-12 //Imagen Reflejada ImageSource logo2 = new System.Windows.Media.Imaging.BitmapImage(uri); Portada_Copy.SetValue(Image.SourceProperty, logo2);
En el cdigo fuente definitivo del evento Siguiente_Portada, bastar con aadir un par de lneas para cargar otro Bitmap que pueda ser usado por el control Portada_Copy que se encarga del reflejo: Tngase en cuenta que la misma imagen no puede servir de origen de informacin para los dos controles.
Conclusin
Hay otros aspectos importantes en los que Blend es de gran ayuda para la creacin de interfaces de usuario: Transformaciones, Animaciones, gestin de estados visuales mediante la herramienta Visual State Manager y enlace a Objetos de Negocio proveedores de datos. En el captulo 6 veremos lo concerniente a Animaciones y Transformaciones y en el captulo 7 dedicado al tratamiento de datos, cmo utilizar Blend para generar interfaces que enlacen con datos de una forma visual.
pgina
75
captulo
Ya hablamos en el primer captulo del tremendo esfuerzo de simplificacin y miniaturizacin que supone haber reducido todo el potencial presente en el lenguaje XAML de Windows Presentation Foundation hasta conseguir un runtime de poco ms de 4 Mb y de las experiencias aprendidas en el proceso. Esto no solo es un trabajo de optimizacin, sino que requiere la reduccin o eliminacin de algunas caractersticas que no estn presentes en la versin de XAML que presenta Silverlight 2 (como los grficos 3D, por ejemplo). No obstante, el desarrollador acostumbrado a WPF, quiz se sorprenda de la cantidad de elementos disponibles aqu, para lo reducido del complemento. Por otro lado, en los tres primeros captulos, casi todo el cdigo XAML que hemos utilizado ha sido generado por Expression Blend o el propio Visual Studio. Nos hemos centrado ms en las herramientas y su capacidad de produccin de cdigo que en el cdigo en s. Vamos, por tanto a revisar el potencial de la versin de XAML disponible para Silverlight 2, dividiendo sus elementos en dos grupos principales: los del propio lenguaje y su capacidad de expresin para representar elementos de la IU, y en el captulo siguiente, aquellos que ya estn construidos y presentan una funcionalidad encapsulada en forma de control.
El lenguaje XAML
Se trata, en muchos sentidos, de un lenguaje de marcas similar a los estndares utilizados en Internet: HTML, XHTML, etc. Un fichero XAML es, simplemente, un fipgina
77
chero de texto plano con sintaxis XML, y el nico requisito formal inicial es que sea un documento XML bien formado1. Un documento XAML puede tener cualquier extensin, aunque, por convencin, asumimos que .xaml es la ms descriptiva. XAML, por otra parte, es un lenguaje orientado a objetos (no olvidemos que cada elemento representa una clase definida en las libreras del runtime), y usualmente (pero no obligatoriamente) dispone de una clase parcial complementaria, escrita en un lenguaje .NET vlido: C#, VB.NET o un lenguaje dinmico, aunque los dos primeros son los predeterminados. Cualquier elemento definido en la parte XAML es reconocido por su clase subyacente. Tambin es perfectamente posible, y en ocasiones se hace as, crear elementos de la interfaz de usuario utilizando exclusivamente cdigo .NET. No obstante, la perfecta conjuncin de ambos se produce cuando cada uno ocupa el rol para que el que ha sido pensado: XAML para definir interfaces de usuario, y .NET para codificar los comportamientos y acciones de esa interfaz, al menos de forma general. Otra cuestin a tener en cuenta es que todo dibujo o elemento de la IU creado con XAML es de carcter vectorial, y no un mapa de bits. Eso significa que no hay prdidas de calidad por el cambio de tamao (escalados) en ninguno de los dos sentidos. De eso se encarga el ncleo Presentation Core que podemos ver en la figura 4-1, junto al resto del framework de Silverlight, sirviendo de base a todos los elementos visuales.
figura 4-1
Modelo del Framework de Silverlight 2.0, subrayando los elementos visuales de la IU programables en lenguaje XAML
Los documentos XML admiten dos niveles de conformidad para garantizar su coherencia. Se dice que un documento XML est bien formado cuando cumple con los requisitos sintcticos del estndar XML publicado por la W3C. Se dice que es vlido, cuando, estando bien formado, puede contrastarse su contenido con un esquema que lo define, sea del tipo XMLSchemas o DTD (Document Type Definition).
78
pgina
>>
En la lista de tems disponibles para la construccin de la interfaz de usuario tambin podramos diferenciar los elementos por el propsito para el que sirven. As, tendramos elementos dinmicos y estticos, contenedores y contenidos, etc. Comenzamos nuestra revisin con los elementos estticos de la interfaz de usuario.
Este UserControl, recibe un tamao inicial predeterminado establecido por las propiedades Width y Height del control, y contiene un elemento de layout o disposicin visual (un Grid, de nombre LayoutRoot), que sirve de contenedor del resto de elementos, y que aparece vaco por defecto. Un Grid definido de esa forma es como una tabla que contiene una sola celda que ocupa todo su espacio disponible, al que podemos hacer referencia en el cdigo subyacente por el nombre asignado en su atributo x:Name, y que tal y como est tiene fondo de color blanco, por lo que no resulta visible aunque lancemos la aplicacin (obtendremos una ventana del navegador aparentemente vaca).
pgina
79
figura 4-2
80
pgina
>>
figura 4-3
Cualquiera de los dos nos sirve, pero no es una mala prctica que nos acostumbremos a utilizar cada cosa para el propsito para el que fue creada. Ya que el texto que deseamos es de solo-salida (el usuario no interactuar cambindolo), el ms apropiado es el TextBlock. Introducimos la siguiente lnea dentro del elemento <Grid>:
<TextBlock Text=Hola desde Silverlight 2.0 FontFamily=A rial FontSize=24 VerticalA lignment=Center HorizontalA lignment=Center/>
La compilacin producir el fichero .xap correspondiente, el servidor de IIS local asignar un puerto para la ejecucin, y el .xap ser cargado por la pgina que hayamos establecido como inicial. El resultado evidente (nos podemos ahorrar un grfico).
Layout
Una revisin del cdigo anterior nos lleva a una primera diferenciacin entre los elementos XAML de Silverlight 2: los que pueden hacer las veces de contenedores y los que no. A su vez, en los primeros, podemos dividirlos en dos partes: los que solo
pgina
81
admiten un elemento contenido (aunque este pueda ser, a su vez, un contenedor) y los que no. Afortunadamente para nosotros, el sistema de Intellisense de Visual Studio 2008 y el analizador sintctico, reconocern estas circunstancias, indicando lo que sucede en cada situacin.
Elementos contenedores
Un control Silverlight solo puede albergar un elemento. Para conseguir interfaces con mltiples elementos, existen los contenedores (tambin llamados layouts). Se trata de los objetos que heredan de System.Windows.Controls.Panel. A continuacin mostramos la jerarqua de elementos que heredan de Panel, tal y como la muestra la herramienta Reflector. Los 3 contenedores disponibles son Grid, Canvas y StackPanel (con Silverlight Toolkit, se dispone de 2 ms: WrapPanel y DockPanel):
figura 4-4
Si analizamos un poco ms el elemento TextBlock utilizado, veremos que dispone de un nmero de atributos programables, relativos al texto a mostrar, a su diseo visual, y a sus relaciones con el entorno. Este modelo se repite en la inmensa mayora de los controles y elementos visuales, y hay algunos de ellos que varan dependiendo del contexto del control (de su contenedor). Recordemos que podemos abrir cualquier fichero XAML en Blend. Si seleccionamos el men contextual del fichero Page.xaml, tenemos la opcin de abrirlo como aparece en la figura 4-5. Avancemos algo ms. Supongamos que queremos tambin un pequeo ttulo, un fondo distinto del blanco y que nuestro letrero se muestre en uno de esos bonitos rectngulos con los bordes redondeados. En este caso no hay filas y columnas definidas pero bastara definir dos filas para poder separar ttulo de contenido. Convendra igualmente, hacer que el ttulo estuviera en una fila inicial de tamao fijo, y el texto, ocupando la otra fila, centrado. El cdigo del listado 4-2 se encarga de ello.
pgina
82
>>
figura 4-5
listado 4-2 <Grid.RowDefinitions> <RowDefinition Height="56.1"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Text="Primera aplicacin Silverlight 2.0" FontFamily="A rial" FontSize="18" VerticalA lignment="Center" HorizontalA lignment="Center" Grid.Row="0"/> <Border CornerRadius="25" Margin="40,87.9,40,84" Grid.Row="1" BorderThickness="5,5,5,5" Background="Blue" BorderBrush="Yellow" HorizontalA lignment="Stretch" VerticalA lignment="Stretch"/> <TextBlock Text="Hola desde Silverlight 2.0" FontFamily="A rial" FontSize="24" VerticalA lignment="Center" HorizontalA lignment="Center" Grid.Row="1" Foreground="#FFE4EC12"/>
Propiedades adjuntas
Advirtase la presencia de atributos que representan propiedades relativas al contenedor de ese elemento. Se trata del concepto de Propiedades Adjuntas (Attached Properties). En el elemento contenido se establece una propiedad que, realmente, compete al contenedor. Concretamente, Grid.Row establece en qu fila se mostrar el elemento (siempre comenzando por 0), y de igual forma podemos asignar atributos Grid.Column, en caso de haber definido columnas. El cdigo es bastante autoexplicativo. Y un apunte sobre el efecto de biselado: se consigue con la propiedad CornerRadius del objeto Border, y el mismo efecto se pueden obtener igualmente en otros elementos (Rectangle, etc.).
pgina
83
figura 4-6
Las rejillas (grids) se utilizan por su similitud con las tablas y la facilidad que nos ofrecen para ubicar elementos en pantalla, manteniendo el conjunto bien separado gracias a su divisin en filas y columnas. Es el ms flexible de todos los contenedores, y define 3 estrategias de asignacin de tamao para sus filas y columnas: Tamao absoluto: donde a cada definicin se le indica el tamao exacto en pxeles (Ej. <RowDefinition Height=100 /> ) Tamao automtico: el contenedor asigna a cada fila/columna el tamao que necesita y nada ms. (Ej. <RowDefinition Height=A uto /> ) Tamao proporcional: el contenedor asigna a cada fila/columna el tamao disponible dependiendo del total de espacio utilizable. (Ej. <RowDefinition Height=* />). Este modo tiene otras posibilidades pudindose asignar porcentajes del total. Por ejemplo, la siguiente definicin asigna a la primera fila, la mitad del espacio que asigna a la segunda pero el doble del que asigna a la tercera:
<RowDefinition Height=* /> <RowDefinition Height=2* /> <RowDefinition Height=0.5* />
Adems, estos modos siempre pueden mezclarse para conseguir la combinacin para un escenario concreto, y de forma muy similar a como sucede en HTML con las tablas, un elemento puede ocupar ms de una fila o columna. Para ello se utilizan los atributos RowSpan y ColumnSpan.
pgina
84
>>
Por ltimo, cabe mencionar la capacidad de los Grids de funcionar de forma similar a los controles Splitter, capaces de dividir una superficie en dos partes que el usuario puede cambiar de tamao de forma dinmica. Para ello, se dispone del objeto GridSplitter, pensado con esta idea. Su funcionamiento es algo complejo, y lo mejor para su manejo es utilizar el diseador de estados Visual State Manager, por lo que remitimos al lector interesado a la documentacin del producto.
listado 4-3 <Canvas x:Name="LayoutRoot" Background="A zure"> <TextBlock Text="Primera aplicacin Silverlight 2.0" FontFamily="A rial" FontSize="18" VerticalA lignment="Center" HorizontalA lignment="Right" Canvas.Left="63.844" Canvas.Top="16"/> <Border CornerRadius="25" Margin="0,0,0,0" BorderThickness="5,5,5,5" Background="Blue" BorderBrush="Yellow" HorizontalA lignment="Stretch" VerticalA lignment="Stretch" Canvas.Left="16" Canvas.Top="128" Width="360" Height="66"/> <TextBlock Text="Hola desde Silverlight 2.0" FontFamily="A rial" FontSize="24" VerticalA lignment="Center" HorizontalA lignment="Center" Foreground="#FFE4EC12" Canvas.Top="148.402" Canvas.Left="63.844"/> </Canvas>
pgina
85
nota nota
listado 4-4
Recuerde que en Expression Blend 2.0, la ventana de diseo muestra dos modos de trabajo (modo Grid y modo Canvas), entre los que conmutamos pulsando sobre el icono de la parte superior izquierda de esa ventana.
Con este cdigo, obtendramos una salida idntica a la anterior, y el posicionamiento pasa a ser absoluto, pudiendo precisar un gran nmero de decimales para la ubicacin exacta de cada elemento. Al objeto de ganar en flexibilidad a la hora de organizar los elementos visuales cuando trabajamos con contenedores Canvas, un objeto Canvas puede estar contenido dentro de otro y ser a su vez contenedor de un conjunto diverso de controles.
En situaciones reales, cuando es necesario averiguar el tamao actual (altura y anchura) de un elemento, las propiedades Width y Height no son de gran ayuda, ya que solamente expresan la altura y anchura deseadas, pero por razones de cambio de tamao de otros elementos es muy posible que esos valores no coincidan con los actuales. Los valores requeridos son suministrados por las propiedades ActualHeight y ActualWidth.
Esto puede verse en ste sencillo ejemplo que nos suministra la documentacin oficial:
<Canvas Width="300" Height="300" Background="White"> <Canvas Width="250" Height="250" Canvas.Left="30" Canvas.Top="30" Background="blue"> <Rectangle Canvas.Left="30" Canvas.Top="30" Fill="red" Width="200" Height="200" /> </Canvas> </Canvas>
86
>>
figura 4-7
Objeto Canvas anidado dentro de otro que contiene un rectngulo (Grfico de MSDN)
En situaciones de diseos complejos, lo mejor es jugar con ambos mundos, ya que pueden convivir perfectamente (un Grid puede contener objetos Canvas en cualquiera de sus celdas o en todas ellas, y al revs). A modo de ilustracin, el ejemplo siguiente muestra un Grid cuadrado (mismo nmero de filas y columnas) en el que cada celda tiene distintos tipos de contenido: otro Grid, un control TextBlock, un Canvas, y un control Image con su configuracin cambiada para que la imagen contenida ocupe toda su superficie. En el caso del Canvas (Fila 1, Columna 0) su contenido se ubica de forma relativa al contenedor directo (el propio Canvas, y no al Grid que hace de contenedor general (el cdigo corresponde al del listado 4-5). En caso de no especificar valores para Grid.Row y Grid.Column, el sistema asume que su valor es 0. Hay un montn de posibilidades, desde las ms intuitivas a las ms complejas. La salida del listado 4-5 se corresponde con la figura 4-8:
figura 4-8
Salida del cdigo anterior mostrando un Grid con cuatro celdas y un objeto distinto en cada una de ellas.
pgina
87
listado 4-5 <UserControl x:Class="SL_C2_1.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="#FFDBC8C8"> <Grid Background="#FFDBC8C8"> <Grid.RowDefinitions> <RowDefinition Height="0.48*"/> <RowDefinition Height="0.52*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.5*"/> <ColumnDefinition Width="0.5*"/> </Grid.ColumnDefinitions> <Canvas Canvas.Left="30" Background="#FF0FA 22B" Canvas.Top="30" HorizontalA lignment="Stretch" Margin="32,16,40,20" Grid.Row="1" Grid.Column="0" > <Rectangle Canvas.Left="32" Canvas.Top="30" Fill="#FFFFF500" Width="74" Height="74" /> </Canvas> <TextBlock Margin="0,0,0,0" Text="Hola Mundo" TextWrapping="Wrap" Grid.Column="1" Foreground="#FF3512A E" Width="A uto" Height="33" HorizontalA lignment="Center"/> <Grid Margin="24,24,24,24" x:Name="Grid_Interior" > <Grid.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FF000000"/> <GradientStop Color="#FFE57676" Offset="1"/> </LinearGradientBrush> </Grid.Background> <Grid.RowDefinitions> <RowDefinition Height="0.48*"/> <RowDefinition Height="0.52*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="A uto"/> <ColumnDefinition Width="0.5*"/> </Grid.ColumnDefinitions> </Grid> <Image Margin="32,16,32,20" Grid.Column="1" Grid.Row="1" Source="05.jpg" Stretch="Fill"/> </Grid> </UserControl>
88
pgina
>>
El Grid en la posicin (0,0), no tiene ningn contenido. En lugar de eso, hemos escogido dibujar su fondo con una brocha de tipo gradiente, que veremos con ms detalle despus. En el Canvas con el rectngulo incluido se ha disminuido de tamao y cambiado el color, el TextBlock se ha configurado para que est completamente centrado en la celda que lo contiene y la imagen se ha alargado modificando el atributo Fill (modo de relleno de la imagen), de manera que ocupe todo el espacio disponible en el control Image que lo carga.
Mrgenes y alineacin
El control de la posicin de los elementos que no se ubican mediante posicionamiento absoluto, se realiza mediante las propiedades Margin, HorizontalA lignment y VerticalA lignment, principalmente. Margin permite establecer un valor de margen para los 4 valores de separacin: (en este orden) izquierda, superior, derecha e inferior, o bien un valor distinto para cada uno de ellos. Normalmente, la combinacin adecuada de ambos ofrece unas excelentes posibilidades de posicionamiento sin tener que recurrir al posicionamiento absoluto (el problema de este ltimo estriba en que a veces tenemos que crear interfaces que cambien su tamao dinmicamente al cambiar la superficie de la pgina que los contiene). Respecto al contenido de los textos dentro de los controles que los pueden albergar, adems de los anteriores, disponemos de la propiedad Padding que permite establecer la distancia del texto contenido a los bordes de su contenedor con una sintaxis similar a la utilizada para los mrgenes (su valor por defecto es 0).
89
dirse con los anteriores (existe un RectangleGeometry, un EllipseGeometry, un PathGeometry, etc). Ambas categoras tienen cosas en comn y varias de ellas pueden producir efectos muy similares, pero se diferencian en varios aspectos.
figura 4-9
90
pgina
>>
Vamos a analizar someramente las formas disponibles con las propiedades comunes de que disponen y a poner algunos ejemplos significativos de su utilizacin. Ms adelante, trataremos las diferencias y modo de uso de las geometras.
Formas (Shapes)
En el apartado de las formas distinguimos los siguientes objetos: Ellipse Line Path Polygon Polyline Rectangle Con la adecuada combinacin de los objetos Shape, podemos construir cualquier dibujo que nos propongamos, siendo especialmente til para las formas irregulares el objeto Path que, literalmente, puede expresar cualquier combinacin visual. Todos ellos disponen de un conjunto de propiedades comunes entre las que destacamos las siguientes: Stroke: que describe cmo se dibuja el borde de la forma. StrokeThickness: que describe el grosor del borde de la forma. Fill: que describe como se rellena el interior de las formas. Propiedades de datos que permiten especificar coordenadas y vrtices medidos en pxeles independientes del dispositivo. Las formas suelen estar ubicadas dentro de objetos Canvas, ya que el posicionamiento absoluto es, a menudo, necesario para el dibujo, pero no hay problema para que se encuentren incrustadas en otros contenedores vlidos. Ya hemos visto antes el cdigo para dibujar un rectngulo segn estos principios, y podemos aplicarlo tambin al resto de elementos. Por ejemplo para obtener una elipse sin relleno, (dentro de un Grid), nos bastara con esto:
listado 4-6 <Grid x:Name="LayoutRoot" Background="White" > <Ellipse Margin="128,104,208,224" Fill="#FFFFFFFF" Stroke="#FF000000" StrokeThickness="3"/> </Grid>
pgina
91
Y si queremos ubicar varios objetos Shape en un contenedor tipo StackPanel, para que los presente verticalmente repartiendo el espacio, el problema no es muy complicado:
listado 4-7 <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True" > <Grid.ColumnDefinitions> <ColumnDefinition Width="0.5*"/> <ColumnDefinition Width="0.5*"/> </Grid.ColumnDefinitions> <StackPanel Margin="8,8,8,8" Orientation="Vertical"> <Ellipse Fill="#FF354A 91" Stroke="#FF000000" StrokeThickness="3" Height="150" Width="275" /> <Ellipse Fill="#FF9F5913" Stroke="#FF000000" StrokeThickness="3" Width="100" Height="100" /> <Ellipse Fill="#FF88C634" Stroke="#FF000000" StrokeThickness="3" Width="150" Height="200" /> </StackPanel> </Grid>
figura 4-10
92
pgina
>>
Tambin podemos utilizar la propiedad Stretch para determinar la manera en que la forma aprovecha el espacio de que disponga. Sus valores pueden ser: None: Ninguno Fill: Relleno completo del contenedor modificando las propiedades Width y Height Uniform: Se cambia el tamao de la anchura y la altura, pero, proporcionalmente a la figura, hasta que uno de los dos llegue a los bordes del contenedor. UniformToFill: Se cambia el tamao de la anchura y la altura hasta que la forma rellena todo el espacio disponible. A continuacin vemos este ejemplo, donde las 3 elipses anteriores se ubican cada una en una fila de un Grid, y no se asigna ningn valor a las propiedades Width y Height, pero se establecen distintos modos de relleno:
listado 4-8 <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True" > <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Ellipse Grid.Row="0" Fill="#FF354A 91" Stroke="#FF000000" StrokeThickness="3" Stretch="Fill" /> <Ellipse Grid.Row="1" Fill="#FF9F5913" Stroke="#FF000000" StrokeThickness="3" Stretch="Uniform" /> <Ellipse Grid.Row="2" Fill="#FF88C634" Stroke="#FF000000" StrokeThickness="3" Stretch="UniformToFill" /> </Grid>
La diferencia resultante puede apreciarse perfectamente incluso en la ventana de diseo de Visual Studio (figura 4-11). Como vemos en el grfico, la elipse inicial establece un crecimiento no uniforme hasta alcanzar los lmites de la fila; como sta es un rectngulo, obtenemos la elipse inscrita correspondiente. La elipse del centro, ha crecido uniformemente en ambas propiedades, lo que ha resultado en un crecimiento radial a partir de un foco nico, convirtindose en un crculo. Y, por ltimo, la tercera, es como una mezcla de las dos anteriores: el crecimiento es uniforme (radial), pero en la figura resultante solo se muestra la parte que cabe en su contenedor.
pgina
93
figura 4-11
94
>>
listado 4-9 <Canvas x:Name="LayoutRoot" Background="White" > <Line Stroke="Maroon" StrokeThickness="5" X1="10" Y1="10" X2="220" Y2="100"></Line> <Line Stroke="Red" StrokeThickness="5" X1="0" Y1="0" X2="110" Y2="100" Canvas.Left="25" Canvas.Top="100"></Line> <Line Stroke="Blue" StrokeThickness="5" X1="25" Y1="100" X2="110" Y2="100"></Line> <Polyline Stroke="Navy" StrokeThickness="5" Points="265,50 400,200 375,100 500,150"></Polyline> <Polyline Stroke="Navy" StrokeThickness="5" Points="300,300 415,370 475,200 500,250 300,300"></Polyline> <Polygon Stroke="Violet" StrokeThickness="5" Fill="A qua" Canvas.Left="100" Canvas.Top="175" FillRule="EvenOdd" Points="15,200 68,70 110,200 0,125 135,125" /> </Canvas>
figura 4-12
Hemos dejado para el final el objeto Path por ser el ms complejo, pero tambin el ms completo de todos, ya que tcnicamente hablando un Path puede dibujar cualquiera de los objetos anteriores ms otros (como la construccin de curvas) que, aquellos, no tienen posibilidad de hacer.
pgina
95
96
pgina
>>
De la igual forma, podemos usar objetos UIElement para aplicar un objeto Geometry a su propiedad Clip, como mecanismo de recorte. Por ejemplo, para recortar una imagen, utilizando un objeto Image y esta tcnica, podramos construir lo siguiente:
listado 4-12 <Grid x:Name="LayoutRoot" Background="Beige"> <Canvas> <Image Source="imagenes/gracias.jpg" Width="200" Height="150" Canvas.Top="25"> <Image.Clip> <EllipseGeometry RadiusX="100" RadiusY="75" Center="100,75"/> </Image.Clip> </Image> </Canvas> </Grid>
Que nos ofrecera el resultado visual de la figura 4-13. Realmente, aqu estamos jugando con dos conceptos distintos para producir el recorte: con el objeto EllipseGeometry para obtener los recortes superior e inferior, y con el tamao de la foto respecto al del Canvas para producir los recortes laterales. Como podr darse cuenta el lector cuando pruebe todo esto, las posibilidades son innumerables. Y de la misma forma, podemos utilizar otros objetos Geometry para generar casi cualquier tipo de composicin. Llegados aqu, la cuestin es y qu pasa con el dibujo complejo de tipo vectorial?, es suficiente con estos recursos grficos? Ignoro si, con mucha paciencia, podra llegar a hacerse cualquier cosa, pero en ciertas reas, como en el Diseo Industrial, Interiorismo, Publicidad, etc., el trabajo sera muy arduo. Afortunadamente, tenemos otro poderossimo recurso para sacarnos del atolladero: El objeto Path cuando se usa conjuntamente con un objeto PathGeometry.
figura 4-13
pgina
97
PathGeometry
Es el objeto de dibujo por excelencia. Puede dibujar lo que sea, si bien, la sintaxis es ms complicada y tambin ms largo el proceso de definicin del contenido. Por suerte, muchas herramientas de dibujo preparadas para trabajar con XAML como Expression Blend, Expression Design o Adobe Ilustrator, pueden almacenar sus datos en formato XAML y permitirnos usarlos despus de forma muy sencilla. Al final de este captulo, comentaremos cmo generar estos ficheros ms complejos, pero vamos a ver, al menos, lo fundamental del uso de este objeto. Cada objeto PathGeometry dispone de una propiedad llamada Figures (una coleccin) que puede almacenar tantos objetos PathFigure como necesite. Y cada PathFigure puede contener cualquier nmero de lneas o curvas tanto cerradas como abiertas. Cada uno presenta 4 propiedades fundamentales: StartPoint: un elemento Point que indica el comienzo de la figura. Segments: una coleccin de objetos PathSegment usados para dibujar. IsClosed: valor booleano, que si es verdadero, hace que Silverlight aada una lnea recta para conectar los puntos de inicio en fin, en el caso de que no coincidan. IsFilled: valor booleano, que si es verdadero, hace que el rea interior de la figura se rellene utilizando el valor indicado en la propiedad Path.Fill. A su vez, cada uno de los segmentos de que consta la figura puede ser de alguna de las clases siguientes:
LineSegment: crea una lnea recta entre dos puntos. A rcSegment: crea un arco de elipse entre dos puntos. BezierSegment: crea una curva Bzier entre dos puntos. QuadraticBezierSegment: crea una curva Bzier con un punto de control en lu-
gar de dos, con lo que resulta ms fcil (y rpido) de calcular. PolyLineSegment: crea series de lneas rectas. Se puede hacer lo mismo con mltiples objetos LineSegment, pero esta solucin es ms concisa. PolyBezierSegment: crea series de curvas Bzier. PolyQuadraticBezierSegment: crea series de curvas Bzier ms simples. Con esta tcnica, el cdigo siguiente dibuja un tringulo rectngulo mediante dos lneas muy simples y despus fuerza el cierre de la figura mediante el atributo IsClosed:
pgina
98
>>
listado 4-13 <Canvas> <Path Stroke="Blue" StrokeThickness="3"> <Path.Data> <PathGeometry> <PathFigure IsClosed="True" StartPoint="50,50"> <LineSegment Point="50,150" /> <LineSegment Point="100,150" /> </PathFigure> </PathGeometry> </Path.Data> </Path> </Canvas>
figura 4-14
Lgicamente, no era necesario recurrir a un objeto Geometry para dibujar un tringulo, pero en el cdigo se aprecia una forma sencilla de programar este tipo de objetos.
Arcos
Los arcos se consiguen asumiendo que cada arco que se va a dibujar, es, en realidad, un segmento de una elipse, de la que tenemos que suministrar su tamao (A rcSegment.Size), el punto de inicio a partir del que queremos obtener el segmento, y el punto que define el final del segmento. El punto final se identifica mediante el objeto A rcSegment.Point y el tamao de la elipse. Como vemos en el ejemplo siguiente, una vez entendido lo anterior, el cdigo resulta sencillo de entender:
pgina
99
listado 4-14 <Path Stroke="Blue" StrokeThickness="3"> <Path.Data> <PathGeometry> <PathFigure IsClosed="False" StartPoint="10,10" > <A rcSegment Point="250,150" Size="200,300" /> </PathFigure> </PathGeometry> </Path.Data> </Path>
Dibujo de un arco a partir de los puntos inicial y final y el tamao de la elipse asociada. La segunda imagen se obtiene duplicando el cdigo anterior y cambiando la propiedad SweepDirection
Hay dos propiedades que se han asumido de forma implcita para seleccionar (en este ejemplo) el segmento ms corto de la curva posible en lugar del ms largo y para el sentido de la curvatura. En el primer caso, se asumi por parte del sistema el valor A rcSegment.IsLargeA rc predeterminado (o sea, falso), con lo que se seleccion el arco ms corto. Incluso as, hay dos formas de dibujar ese arco entre los dos puntos y cada una produce una curvatura opuesta a la anterior. Este valor se decide con la propiedad A rcSegment.SweepDirection que puede adoptar los valores ClockWise (sentido de las agujas del reloj) y CounterClockWise (sentido contrario). De hecho, para ver esta diferencia basta con duplicar el cdigo del arco y cambiar esta propiedad para obtener dos arcos iguales, pero complementarios, como muestra la figura 4-15b.
pgina
100
>>
Curvas Bzier
Se trata de una curva paramtrica estudiada en Anlisis Numrico, muy importante en computacin grfica y sus campos relacionados2. Las curvas Bzier conectan dos segmentos lineales usando una funcin matemtica compleja que incorpora dos puntos de control (o n puntos, generalizando para otras situaciones) para determinar cmo se dibuja la curva. Son muy flexibles y, a partir de un punto de entrada y uno final y dos puntos de control, podemos construir una variedad enorme de curvas suaves (continuas o derivables). La imagen de la figura 4-16 da una idea siquiera intuitiva del proceso a seguir en la construccin de este tipo de curvas.
figura 4-16
El control del dibujo se realiza a partir de 3 puntos suministrados a la curva (ms el inicial que lo aporta el contenedor): Los dos puntos de control, llamados BezierSegment.Point1 y BezierSegment.Point2, y el punto final (BezierSegment.Point3). En el siguiente ejemplo (listado 4-15), se utilizan dos curvas en las que la segunda da comienzo en el punto donde termina la primera para generar una curva totalmente irregular, como la de la figura 4-17.
figura 4-17
pgina
101
listado 4-15 <Path Stroke="Blue" StrokeThickness="5" Canvas.Top="20"> <Path.Data> <PathGeometry> <PathFigure StartPoint="100,150"> <BezierSegment Point1="20,90" Point2="40,240" Point3="50,50" /> <BezierSegment Point1="50,50" Point2="40,140" Point3="210,50" /> </PathFigure> </PathGeometry> </Path.Data> </Path>
Advierta el lector que el punto 3 de la primera curva es el mismo que el punto 1 de la segunda, haciendo que sta comience el proceso de dibujo donde termina aquella. La primera curva tiene un perfil suave de tipo paraboloide mientras que la segunda forma un trazo ms abrupto debido a la posicin de uno de los puntos de control. El trabajo con estas estructuras es complejo incluso para el diseador avezado. Lo idneo es utilizar, como apuntbamos antes, herramientas pensadas para ello, como Expression Design, o cualquier otra capaz de exportar sus datos a formato XAML, como Adobe Illustrator.
nota
De hecho, Expression Design tiene la capacidad de leer ficheros generados por Adobe Illustrator con extensin -.ai-, que, luego, pueden ser exportados fcilmente a XAML.
Precisamente con el propsito de manejar estos elementos cuando la complejidad de los grficos crece, se ha creado un mini-lenguaje especial para los objetos PathGeometry, que permite abreviar la descripcin de los grficos pensando en las herramientas citadas. En el manual de referencia dispone el lector de una explicacin detallada de este mini-lenguaje si es de su inters.
102
>>
zacin. Est soportado por Office 2007, como una opcin a la hora de guardar un fichero (si no dispone de ese complemento, puede descargarlo de http://tinyurl.com/y69y7g) y por Windows Vista. Pues bien, Word (u otro programa de Office 2007) es capaz de guardar en ese formato cualquiera de sus documentos. En el caso de que se haya incluido en el documento un fichero grfico de carcter vectorial, como por ejemplo, ficheros de la galera de imgenes con formato .WMF (Windows Metafile), u otro formato vectorial compatible, el fichero WMF ser convertido a XAML y podremos usarlo en nuestras aplicaciones sin ms que un mnimo retoque.
nota
El formato XPS es, en realidad, una forma de encapsular un conjunto de ficheros y recursos asociados a un documento. Su formato comprimido es de tipo ZIP, de forma que si cambiamos su extensin a .zip y lo descomprimimos, veremos un montn de carpetas con los recursos y datos que contiene.
encontrar un fichero con la extensin .fpage. Ese es el grfico en formato XAML. Solo hay que hacer dos pequeos retoques: eliminar el elemento raz (llamado FixedPage), que Silverlight no reconoce y eliminar todos los atributos BidiLine que se encuentren (si hay alguno). El resto, es un conjunto de objetos Canvas y el grfico en puro formato XAML. Una vez hecho eso funcionar perfectamente. Incluso ficheros complejos como el de la figura 4-18, pueden suponer apenas 200 Kb en tamao, y si el lector hace esta prueba ver la cantidad ingente de cdigo que se produce usando objetos Geometry y sintaxis de mini-lenguaje.
figura 4-18
103
104
pgina
captulo
Controles
Hasta ahora hemos visto los elementos XAML ms simples y tambin hemos indicado cmo gestionar parte de la funcionalidad que presentan al usuario. En el captulo anterior, tambin vimos la creacin de nuevos elementos visuales mediante las posibilidades de dibujo que ofrecen los conjuntos de elementos Shapes y Geometries. Si bien hemos usado controles sencillos, como TextBlocks o Buttons, es momento de revisar ms a fondo el apartado de los controles. Son componentes terminados (pero abiertos), que generalmente contienen una estructura visual ms elaborada, tanto en su comportamiento grfico, como en el funcional: eventos programables, propiedades, etc. La creacin de interfaces de usuario modernas asume la existencia de estos controles a los que todos estamos acostumbrados y de los que conocemos su propsito (grfico y operativo). Cajas de texto (TextBoxes), cuadros combinados (ComboBoxes), botones de opcin (OptionButtons), TreeViews y rejillas de datos (DataGrids) son elementos tpicos de estas interfaces y Silverlight no poda obviar una hecho as. Con la versin 2.0 del producto, se introduce un conjunto muy completo de ellos, que se encuentra en constante crecimiento. Existen planes de continuar la creacin de controles para Silverlight 2 y adems, compaas de terceros han iniciado ya la creacin de sus propias suites de controles1 para esta plataforma, y algunas, anuncian ya juegos completos, que semejan y, muchas veces, mejoran sus contrapartidas disponibles para Windows.
105
de herramientas), como en Expression Blend 2.0 (Ventana Assets, en la parte inferior izquierda de este IDE), aunque hay presencias y ausencias en cada uno de ellos debidas, simplemente, al hecho de que algunos controles se encuentran en las libreras del SDK de Silverlight, y no forman parte del runtime, como sucede con Datagrid, Calendar, DatePicker y otros.. Hay elementos visuales que Expression Blend agrupa con los anteriores, sin que realmente se puedan considerar controles, como los Glyphs o los PopUp, que heredan de FrameworkElement, pero que aparecen vinculados con la interfaz de usuario, por tener un componente visual innegable. La figura 5-1 muestra la lista de controles disponible en Visual Studio 2008 SP1.
figura 5-1
Conjunto de controles disponibles en Silverlight 2.0, presente en la Caja de herramientas de Visual Studio 2008
Lo positivo de esta aproximacin es que todos ellos tienen una sintaxis homognea y una manera afn de declarar sus atributos y contenidos. De forma que vamos a
Un glifo es un signo grabado o, por extensin, pintado. En tipografa, un glifo es una representacin grfica de un carcter, de varios caracteres o de parte de un carcter. Un carcter es una unidad textual mientras que un glifo es una unidad grfica. En Silverlight 2.0 existe el concepto de Glyph, pero no es un control propiamente dicho, sino solamente un elemento que se utiliza en la interpretacin visual de texto fijo, o para presentar unidades de texto de forma ms elaborada. Es ms un elemento de diseo que de desarrollo.
106
pgina
Controles en Silverlight 2
>>
dividirlos por grupos funcionales y veremos su comportamiento a travs de ejemplos que agrupen varios de ellos en una misma demo.
nota
Obviamos el caso del DataGrid, ya que, por ser uno de los ms importantes en el tratamiento de datos, lo trataremos aparte, en el captulo 7.
Estrictamente hablando, solo los tres primeros tienen como funcin el manejo de texto, aunque estamos acostumbrados a que otros controles como CheckBox o RadioButton, presenten un texto explicativo que podemos asignar al propio control (la documentacin se refiere a ellos como headered controls), o que muestren conjuntos discretos y bien definidos de texto (como DatePicker). Veamos un ejemplo en el que ponemos en funcionamiento un grupo de ellos, creando un formulario de entrada de datos, al que por completar el contexto le aadimos un botn (sin funcionalidad ninguna). El cdigo completo puede verlo en el listado 5.1. El resultado visual es el que vemos en la figura 5-2. Observe el lector la presencia de un espacio de nombres que hemos aadido manualmente a la declaracin del UserControl: System.Windows.Controls. Aunque el IDE de Visual Studio muestra todos los controles, los de fecha y algunos otros se encuentran en esas libreras que no se referencian por defecto en el proyecto.
pgina
107
listado 5-1 <UserControl x:Class="Cap5_1.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ctlEx="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" Width="420" Height="320"> <Border BorderThickness="12" BorderBrush="Firebrick" CornerRadius="12" Width="420" Height="300"> <Grid x:Name="LayoutRoot" Background="Gainsboro"> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition Height="30" /> <RowDefinition Height="75" /> <RowDefinition Height="40" /> <RowDefinition Height="50" /> </Grid.RowDefinitions> <TextBlock x:Name="txbNombre" HorizontalA lignment="Left" VerticalA lignment="Center" Height="24" Width="70" Text="Nombre:" TextWrapping="Wrap" Margin="20,0,0,0" Grid.Row="0"/> <TextBox Height="24" HorizontalA lignment="Right" VerticalA lignment="Center" Text="TextBox" TextWrapping="Wrap" Width="244" Margin="0,0,20,0" Grid.Row="0"/> <CheckBox Height="24" HorizontalA lignment="Left" VerticalA lignment="Center" Margin="20,0,0,0" Width="261.695" Content="Coche propio" IsThreeState="False" Grid.Row="1"/> <TextBlock x:Name="txbPWD" HorizontalA lignment="Left" VerticalA lignment="Center" Height="24" Width="70" Text="Contrasea:" TextWrapping="Wrap" Margin="135,0,0,0" Grid.Row="1" /> <PasswordBox Grid.Row="1" Width="90" Height="20" VerticalA lignment="Center" Margin="120,-10,0,0" Background="A qua" Foreground="Black" /> <ctlEx:DatePicker Text="Seleccione Fecha de entrada" Grid.Row="2" HorizontalA lignment="Left" VerticalA lignment="Center" Margin="20,0,0,0" Width="160" /> <ListBox Grid.Row="2" Height="64" Width="170" Margin="200,0,0,0"> <ListBoxItem> <TextBlock>A nalista Senior</TextBlock> </ListBoxItem> <ListBoxItem> <TextBlock>Programador Junior</TextBlock> </ListBoxItem> <ListBoxItem> <TextBlock>Especialista en IT</TextBlock>
108
pgina
Controles en Silverlight 2
>>
listado 5-1 (Cont.) </ListBoxItem> </ListBox> <Border BorderBrush="Firebrick" BorderThickness="2" CornerRadius="7" Grid.Row="3" Margin="20,10,20,0" /> <RadioButton Grid.Row="3" Content="Soltero" Margin="50,15,0,0"></RadioButton> <RadioButton Grid.Row="3" Content="Casado" Margin="170,15,0,0"></RadioButton> <RadioButton Grid.Row="3" Content="Viudo" Margin="280,15,0,0"></RadioButton> <HyperlinkButton Content="Pulse aqu para visitar dotNetMania" NavigateUri="http://www.dotnetmania.com" TargetName="_blank" Margin="20,15,0,0" Grid.Row="4" Height="12" VerticalA lignment="Center" /> <Button Content="Enviar" Grid.Row="4" Width="100" HorizontalA lignment="Right" Margin="0,15,20,0" /> </Grid> </Border> </UserControl>
nota
Hay otra opcin ms sencilla que consiste en arrastrar el control desde la Caja de herramientas hasta el editor de cdigo, con lo que ste hace esa labor por nosotros asignando al espacio de nombres my de forma predeterminada.
figura 5-2
pgina
109
nota
Recuerde que la propiedad Content de un ListBoxItem (o ComboBoxItem) solo puede albergar un elemento, por lo que, si necesita mostrar varios en uno de los tems, puede aadir un control de la familia Panel e introducir all lo que sea necesario.
El ejemplo siguiente contiene un ListBox cuyos elementos muestran la imagen de unos populares bloggers y tambin su nombre. Ambos elementos (Image y TextBlock) se han ubicado en sendos controles StackPanel con orientacin horizontal (listado 5-2). Y, en realidad, podramos hacer todas las combinaciones que nos interesaran sin ms limitaciones que las que impone su propia arquitectura. Por ejemplo, no hay inconveniente para que cada ListItem contenga un control MediaElement, que represente un vdeo, y as sucesivamente. El resultado en la Ventana de Diseo de VS2008 sera el de la figura 5-3:
figura 5-3
pgina
110
Controles en Silverlight 2
>>
listado 5-2 <UserControl x:Class="Cap5_Bloggers.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="230"> <Grid x:Name="LayoutRoot" Background="Gainsboro" ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="20" /> <RowDefinition Height="210" /> </Grid.RowDefinitions> <TextBlock Text="Bloggers" Grid.Row="0" HorizontalA lignment="Center" VerticalA lignment="Center" FontSize="21"/> <ListBox Grid.Row="1" Width="240" Height="160" Background="A liceBlue" > <ListBoxItem> <StackPanel Orientation="Horizontal"> <Image Source="Fotos/LMB.png" /> <TextBlock VerticalA lignment="Center" Margin="7"> Luis Miguel Blanco</TextBlock> </StackPanel> </ListBoxItem> <ListBoxItem> <StackPanel Orientation="Horizontal"> <Image Source="Fotos/A larcon.png" Width="48" /> <TextBlock VerticalA lignment="Center" Margin="7"> Jos Manuel A larcn</TextBlock> </StackPanel> </ListBoxItem> <ListBoxItem> <StackPanel Orientation="Horizontal"> <Image Source="Fotos/Dino.png" Width="48"/> <TextBlock VerticalA lignment="Center" Margin="7"> Dino Esposito</TextBlock> </StackPanel> </ListBoxItem> </ListBox> </Grid> </UserControl>
111
controles personalizados. Visitamos primero algunos de los usuales en el mundo del desarrollo con Windows Forms que tienen equivalente aqu. Son los siguientes: GridSplitter, ScrollBar, Slider, TabControl y ProgressBar. La funcionalidad de cada uno queda resumida en la siguiente tabla:
Nombre
GridSplitter
Funcionalidad Tambin considerado como un sub-control ya que su funcionalidad depende del objeto Grid que lo aloja. Permite redistribuir el espacio entre filas y columnas de un control Grid. Clase que representa de forma ms genrica un rea desplazable que contenga otros elementos visibles. Representa un control que permite al usuario seleccionar un valor entre un rango controlado, mediante el desplazamiento de un marcador entre los lmites inicial y final. Control que maneja solapas (TabItems) representando cada uno de ellos una superficie contenedora de otros controles. Consta de un rectngulo con un indicador que puede ir mostrando el progreso de una operacin.
ScrollViewer
Slider
TabControl
ProgressBar
Vamos a crear otro ejemplo para ver la operativa de este grupo entero de controles (excepto ProgressBar). Para ello, creamos una pgina con un Grid con dos filas y dos columnas. En la primera fila, colocaremos un ScrollViewer (columna 0), que ocupe dos columnas. En la segunda fila, situaremos cuatro Sliders con un elemento Rectangle cuyo color de fondo se modificar segn los valores ARGB que van proporcionado cada uno de los Sliders. En la ltima celda, situamos un TabControl con 3 solapas (TabItems), cada uno conteniendo una informacin diferente (ver la figura 5-4 con el resultado en ejecucin para tener una idea de conjunto).
listado 5-3 <UserControl xmlns:my="clr- namespace:System.Windows.Controls; assembly=System.Windows.Controls.Extended" x:Class="SL_Controles1.Exp_Visual" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="600" Height="500" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
112
pgina
Controles en Silverlight 2
>>
listado 5-3 (Cont.) mc:Ignorable="d"> <Border BorderThickness="12" BorderBrush="Firebrick" CornerRadius="12" Width="595" Height="495"> <Grid x:Name="LayoutRoot" Background="Gainsboro" ShowGridLines="True" > <Grid.RowDefinitions> <RowDefinition Height="0.352*"/> <RowDefinition Height="0.648*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.4*"/> <ColumnDefinition Width="0.6*"/> </Grid.ColumnDefinitions> <!ScrollViewer --> <ScrollViewer HorizontalScrollBarVisibility="A uto" VerticalScrollBarVisibility="A uto" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" > <TextBlock Text="dotNetMana" FontFamily="A rial Black" FontSize="120" VerticalA lignment="Center" Margin="20, 0, 20, 0" /> </ScrollViewer> <!Sliders --> <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="1"> <Slider x:Name="SldA lpha" Width="150" VerticalA lignment="Top" Margin="20,10,0,0" Minimum="0" Maximum="255" ValueChanged="Cambio_Valor" /> <TextBlock FontFamily="A rial" FontSize="12" Text="A " VerticalA lignment="Top" Margin="20,10,0,0"/> </StackPanel> <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="1"> <Slider x:Name="SldRed" Width="150" VerticalA lignment="Top" Margin="20,60,0,0" Minimum="0" Maximum="255" ValueChanged="Cambio_Valor" /> <TextBlock FontFamily="A rial" FontSize="12" Text="R" VerticalA lignment="Top" Margin="20,60,0,0"/> </StackPanel> <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="1"> <Slider x:Name="SldGreen" Width="150" VerticalA lignment="Top" Margin="20,110,0,0" Minimum="0" Maximum="255" ValueChanged="Cambio_Valor" /> <TextBlock FontFamily="A rial" FontSize="12" Text="G" VerticalA lignment="Top" Margin="20,110,0,0"/> </StackPanel>
pgina
113
listado 5-3 (Cont.) <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="1"> <Slider x:Name="SldBlue" Width="150" VerticalA lignment="Top" Margin="20,160,0,0" Minimum="0" Maximum="255" ValueChanged="Cambio_Valor" /> <TextBlock FontFamily="A rial" FontSize="12" Text="B" VerticalA lignment="Top" Margin="20,160,0,0"/> </StackPanel> <Rectangle Stroke="Navy" x:Name="Rec" Grid.Row="1" Grid.Column="0" Margin="0,160,0,0" Height="50" Width="82" Fill="White"/> <!TabControl --> <my:TabControl Grid.Row="1" Grid.Column="1" Margin="15,15,15,15"> <my:TabItem Header="D. Esposito"> <StackPanel Orientation="Vertical"> <Image Source="Fotos/Dino.png" Margin="10, 50, 0, 0" VerticalA lignment="Bottom" Stretch="None"/> <TextBlock Margin="100, 70, 0,0"> <Run Text="El Blog de Dino Esposito"/> </TextBlock> </StackPanel> </my:TabItem> <my:TabItem Header="L.M. Blanco"> <StackPanel Orientation="Vertical"> <Image Source="Fotos/LMB.png" Margin="10, 50, 0, 0" VerticalA lignment="Bottom" Stretch="None"/> <TextBlock Margin="100, 70, 0,0"> <Run Text="El Blog de Luis Miguel Blanco"/> </TextBlock> </StackPanel> </my:TabItem> <my:TabItem Header="J.M. A larcn"> <StackPanel Orientation="Vertical"> <Image Source="Fotos/alarcon.png" Margin="10, 50, 0, 0" VerticalA lignment="Bottom" Stretch="None"/> <TextBlock Margin="100, 70, 0,0"> <Run Text="El Blog de J.M. A larcn"/> </TextBlock> </StackPanel> </my:TabItem> </my:TabControl> </Grid> </Border> </UserControl>
114
pgina
Controles en Silverlight 2
>>
Hay varios aspectos que merece la pena recalcar de este cdigo. Primero, que hemos dejado la propiedad ShowGridLines del Grid activada para que se vea mejor la estructura del contenedor y donde est ubicado cada elemento. Adems, estamos utilizando dos columnas para un solo control (ScrollViewer) mediante la propiedad Grid.RowSpan. El comportamiento de ScrollViewer es simple: en funcin de su tamao habilitar sendas barras de desplazamiento (vertical, horizontal o ambas) para permitir que el usuario tenga acceso a su contenido. Aqu, es un TextBlock con un tipo de letra muy grande el que fuerza a que sea as, pero podra ser cualquier otro. Veamos la ltima celda, que maneja 2 controles de la familia de los Tab*. TabControl es un control contenedor de objetos TabItem que definen reas en pantalla, de las que slo una es visible en un momento dado. Los TabItem (solapas) pueden tener una cabecera con texto, y cualquier contenido (una foto y un texto, representado aqu por un objeto Run, ya que su nica funcin es mostrar algn texto con formato). Por lo dems, la funcionalidad de los TabControl es automtica, como sucede con sus homlogos de Windows Forms, esto es, no hay que codificar nada separadamente para que muestren u oculten su contenido (al pulsar sobre otra solapa). El objeto TabPanel (no presente aqu), pertenece a la misma familia, y se utiliza para la creacin de plantillas personalizadas de visualizacin, y en esos casos, es el encargado de interpretar visualmente el contenido definido en las plantillas. Como ya hemos apuntado, en el prximo captulo veremos ms sobre creacin de estilos y plantillas.
pgina
115
La idea es que el usuario cambie el color de fondo del rectngulo al mover los Slider. Al hacerlo, se produce el evento Value_Changed, y nosotros nos hemos suscrito a l declarando un manejador de evento comn para cuando uno de los 4 cambie de valor. Al mover uno cualquiera, se pasar por el evento indicado y generaremos un nuevo color de fondo a partir de la clase Color del Framework. Como la accin es la misma en los 4 casos, y en todos ellos tenemos acceso a los valores de los Slider, solo necesitamos un procedimiento de evento con este cdigo:
listado 5-4 private void Cambio_Valor(object sender, RoutedPropertyChangedEventA rgs<double> e) { Color colRect = Color.FromA rgb((byte)SldA lpha.Value, (byte)SldRed.Value, (byte)SldGreen.Value, (byte)SldBlue.Value); Rec.Fill = new SolidColorBrush(colRect); }
El procedimiento de evento regenera el valor de la propiedad Fill del rectngulo con cada cambio en uno de los controles Slider. Para ello, creamos un objeto Color que responda a los cambios de estado, y lo asignamos dinmicamente. La ejecucin de este ejemplo ofrece una salida como la de la figura 5-4.
figura 5-4
pgina
116
Controles en Silverlight 2
>>
Controles multimedia
En captulos anteriores hemos visto cmo utilizar dos de los 3 controles disponibles especficamente para mostrar este tipo de informacin: Image y MediaElement. Y, no tema el lector, no vamos a crear otro reproductor de vdeo o visor de fotografas. Demasiados hay ya por la red, bien explicados y, seguramente, resueltos con ms conocimientos de diseo de lo que podramos hacer aqu. Su uso es bastante sencillo, como hemos podido comprobar en captulos anteriores. Pero merece la pena citar un tercer control creado expresamente para dar soporte a una tecnologa que hace su debut con esta versin de Silverlight, llamada DeepZoom. Permite ampliar un conjunto de imgenes de tamaos cualesquiera, incluyendo alejamientos y aproximaciones espectaculares, y manteniendo un rendimiento excelente en todo el proceso. Adems, admite jugar con escenarios tridimensionales formados a partir de fotos individuales de un entorno, hasta formar un contexto de rotacin de 360. El tratamiento de las imgenes de gran tamao se realiza mediante la creacin de una pirmide de imgenes, cuyo tamao y resolucin individual puede ser generado con la herramienta del SDK Deep Zoom Composer, teniendo en cuenta que sta herramienta solo soporta el proceso de imgenes cuyo formato sea el manejado por la clase BitmapImage. Para la tarea de creacin de escenarios tridimensionales, tambin se puede contar con la herramienta de Microsoft PhotoSynth (ver http://photosynth.com). La programacin de estos efectos se consigue con el control MultiScaleImage, que permite almacenar un nmero indeterminado de objetos MultiScaleSubImage. La documentacin oficial es bien explcita sobre el funcionamiento de esta tcnica e incluye ejemplos, junto a una solucin completa descargable.
El resto de controles
Otros elementos de la interfaz de usuario que no aparecen en la caja de herramientas de Visual Studio pero s en Blend, tienen ciertas peculiaridades respecto a los vistos hasta aqu. O no poseen un aspecto visual predeterminado (sino que son meros contenedores de otros), o su tipo de contenido es especfico, o son utilizados para formar parte de estructuras ms complejas, o su aspecto visual debe ser definido por otros medios. En muchos casos, se usan como clases abstractas, pensadas para heredar de ellos y aprovechar su funcionalidad por esta va. Vamos a repasar algunos de ellos.
pgina
117
ContentControl
Se trata de un control muy especial dentro de esa jerarqua, ya que sirve de base a muchos otros controles y sub-controles. Esto podemos verlo ms detalladamente en el diagrama 1. ContentControl hereda de FrameworkElement, y aunque no es lo ms usual llega a convertirse en origen de clases abstractas, como CalendarButtonBase. Entre la jerarqua a que da lugar, cabe destacar una buena cantidad de versiones de la idea de Button, que a su vez da origen a otros elementos, etc.
diagrama 1
No es lo ms normal utilizarlo directamente en una interfaz, pero no hay inconveniente para ello. El ejemplo siguiente muestra dos objetos ContentControl individuales en funcionamiento (listado 5-5). En el primero, su contenido se asigna mediante el atributo Content, ya que se trata de texto solamente, mientras que en el segundo, es un CheckBox. La salida visual sigue los patrones esperados (figura 5-5). La propiedad Content de un ContentControl puede ser literalmente cualquier cosa, pero tiene algunas limitaciones:
pgina
118
Controles en Silverlight 2
>>
listado 5-5
<Grid x:Name="LayoutRoot" Background="Gainsboro" ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="A uto" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Text="Controles contenedores" Margin="0,20,10,20" FontFamily="A rial" FontSize="21" FontWeight="Bold" Foreground="Navy" HorizontalA lignment="Center" Grid.Row="0"/> <ContentControl Margin="3" Grid.Row="1" Background="Black" BorderBrush="Red" VerticalA lignment="Center" Content="Control contenedor con texto simple." HorizontalA lignment="Center" /> <ContentControl Margin="3" Grid.Row="2"> <CheckBox Content="Este contiene un control CheckBox" HorizontalA lignment="Center" Background="Gainsboro" /> </ContentControl> </Grid>
figura 5-5
a) Debe ser un elemento que herede de UIElement. b) Puede incluir otros objetos (pero entonces, simplemente, se llamar al mtodo ToString() de cada objeto contenido). c) Puede incluir otros objetos con plantillas: si la propiedad ContentTemplate del control se asigna a una plantilla de datos, se usar esa plantilla, junto a las expresiones que contenga. Muy til para colecciones de objetos.
pgina
119
Esto crea una etiqueta flotante a la que se le aplica un estilo en la configuracin del elemento ToolTip de ToolTipService. Y la versin ms simple del anterior, solamente incluye el elemento a mostrar:
listado 5-7 <ContentControl Margin="3" Grid.Row="2"> <Border HorizontalA lignment="Center" BorderThickness="4" BorderBrush="Brown" CornerRadius="15"> <TextBlock Margin="12,0,12,0" VerticalA lignment="Center" > Este contiene un elemento Border</TextBlock> <ToolTipService.ToolTip> <StackPanel Orientation="Horizontal"> <TextBlock Text="Etiqueta flotante estilo predeterminado"/> </StackPanel> </ToolTipService.ToolTip> </Border> </ContentControl>
120
pgina
Controles en Silverlight 2
>>
As pues, tenemos dos formas de modificar el aspecto de la etiqueta flotante: una es con plantillas de estilo que pueden ser aplicadas como recurso esttico; la otra, tal y como hemos hecho aqu, con la inclusin de un elemento ToolTip dentro de la declaracin de ToolTipService. Si se hace as, podremos crear toda una interfaz compleja como parte de la etiqueta.
figura 5-6
Los Presenters
Aunque lo parece, no se trata de un grupo musical, sino del conjunto de controles que lleva ese sufijo, y que comparten algunos elementos comunes. Son 4: ContentPresenter, InkPresenter, ItemsPresenter y ScrollContentPresenter (que hereda directamente del primero). Todos pertenecen al espacio de nombres System.Windows.Controls2. Estn pensados para servir de contenedores de otros, y especialmente, para suministrar una infraestructura comn sobre la que aplicar estilos a los controles con los que se asocian, pero aadimos unos comentarios sobre uno un tanto especial: InkPresenter.
InkPresenter
Como sabr probablemente el lector, Ink (literalmente, tinta) es un tipo de dato en .NET. Permite programar entradas de informacin por parte usuarios de dispositi2
121
vos tctiles, como PDAs, PocketPCs, TabletPCs, etc. El objeto InkPresenter, dispone de una propiedad colectiva llamada StrokeCollection, que se compone de elementos Stroke (trazo). Cuando se aade un elemento a la coleccin, el objeto lo interpreta visualmente de forma inmediata. InkPresenter hereda de Canvas y dispone de una de las colecciones de miembros pblicos ms grandes entre los objetos de la jerarqua a la que pertenece. Del grupo de los Presenters, es quiz el que ms se utiliza individualmente, sin jugar el papel de mero ladrillo constructor de otros controles ms complejos.
Thumb,ToggleButton y RepeatButton
Concluimos este captulo con una descripcin de 3 tpicos sub-controles. El control Thumb, est especialmente preparado para permitir el manejo de procesos de usuario del tipo Arrastrar-y-Soltar (Drag&Drop). Se utiliza como parte de otros controles como ScrollBar, donde adopta la forma de un rectngulo desplazable a lo largo de la superficie limitada en ambos extremos por dos controles RepeatButton, pero puede utilizarse independientemente, sobre todo, en la construccin de controles de usuario (ver figura 5-7).
Controles RepeatButton
Control Thumb
figura 5-7
122
Controles en Silverlight 2
>>
DragCompleted y DragDelta, que se lanzan cuando el usuario comienza el proceso, lo termina o utiliza la rueda del ratn teniendo el foco. Al igual que los anteriores, su utilizacin real es mediante el concurso de estilos y plantillas, como tambin lo suele ser otro de los controles que nos quedan por comentar: RepeatButton. RepeatButton sirve para recoger entradas de usuario de tipo continuo. Para ello, lanza una secuencia de eventos Click desde el momento que se pulsa hasta que se libera, y el ejemplo tpico es el de la estructura de un Scrollbar. Por su parte, ToggleButton es un botn con dos estados visuales estticos: presionado y liberado (normal). Dispone de una definicin de plantillas que permiten distinguir claramente cundo se encuentra en un estado u otro y una propiedad IsChecked, que permite determinar su estado. Adems, puede configurarse para que se comporte como un control tri-estado, igual que CheckBox.
Resumen
Operando de esta forma, Microsoft permite una aproximacin mucho ms personalizada a la creacin y modificacin de controles de la que tenamos con Windows Forms, y esa es una gran aportacin de WPF. Disponemos de los elementos constituyentes y de los mecanismos programticos para idear la creacin de nuevos tipos de controles, para cualquier contexto, por muy espgina
123
pecfico que sea y con escaso esfuerzo, ya que, mediante herencia, accedemos a la funcionalidad bsica proporcionada por sus ladrillos constructores. En el prximo captulo veremos ejemplos de cmo usar estas familias de controles constituyentes creando escenarios visuales personalizados mediante estilos y plantillas. Tambin veremos cul es el proceso de creacin de un control reutilizable.
nota
A la conclusin de esta obra, Microsoft ha hecho pblico un Silverlight Toolkit, con nuevos controles y herramientas, disponible en CodePlex: http://www.codeplex.com/Silverlight.
124
pgina
captulo
125
A estas propiedades, se les conoce como Dependency Properties (DP) y su funcin principal es permitir calcular el valor de una propiedad en funcin del valor de otras entradas, pudindose incluso establecer retro-llamadas (callbacks) para comprobar los cambios producidos. En Silverlight 2, las propiedades se exponen, tpicamente, como propiedades clsicas del CLR. Podramos estar utilizando muchas de ellas sin saber que se trata en realidad de propiedades DP. De esa forma, se suministra un mecanismo de evaluacin del valor de una propiedad basndose en otros datos, como pueden ser propiedades del sistema, datos obtenidos mediante tcnicas de DataBinding, recursos (como los estilos, por ejemplo), o valores conocidos, como las relaciones padre-hijo con otros elementos. Se reconocen por la presencia de un campo cuyo nombre est basado en la propiedad a la que mapean, seguido de la palabra reservada Property, y hay muchos escenarios en Silverlight donde su presencia es fundamental. En la parte final de este captulo, mostramos esta tcnica como parte de la definicin de un control de usuario. La sintaxis de declaracin de una DependencyProperty es la siguiente:
listado 6-1 private static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CajaTextoDNI),null); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }
La clase esttica DependencyProperty registra la propiedad Text, anotando su tipo, su clase, la cadena que se utilizar para nombrarla y eventualmente un valor para el parmetro PropertyMetadata, que aqu pasamos como null, pero que puede recibir el nombre de una funcin por la que pasar el flujo de ejecucin cuando el valor de esa propiedad se modifique.
126
>>
tos llamados Routed Events: eventos encaminados o dirigidos desde un origen hasta un posible destino que no puede ir ms all del nodo raz del rbol XAML, en un fenmeno denominado bubbling, de manera similar a como sucede en el modelo de objetos de DOM con elementos HTML1. Silverlight, por el momento, solo soporta unos pocos eventos de este tipo, relacionados con el teclado (KeyDown y KeyUp) y el ratn (varios), adems del evento Loaded. Este ltimo, est vinculado a la idea de trigger que veremos ms adelante a propsito de las animaciones y es el nico evento cuyo manejador puede ser declarado en puro lenguaje XAML. En Silverlight 2, la principal razn para la existencia de RoutedEvents es la compatibilidad con WPF.
Bubbling En los eventos que utilizan un mecanismo de bubbling, su segundo argumento de evento (e), posee siempre una propiedad Handled que permite que se detenga el proceso de burbujeo hacia nodos superiores, cuando el valor de la propiedad se asigna a True (se indica que el evento ha sido manejado). Pero los nodos XAML, pueden lanzar otros eventos que pueden ser manejados al puro estilo .NET por procedimientos de evento adecuados. Incluso se puede provocar ms de una respuesta, o permitir que varios eventos tengan un nico manejador comn. Veamos el funcionamiento de un solo manejador comn en el captulo anterior, dentro de ejemplo de los Sliders. Tambin hemos comprobado que crear un manejador resulta inmediato en el entorno del IDE de Visual Studio (y de Expression Blend), ya que los nombres de los eventos disponibles para un objeto (o elemento), aparecen como atributos en el cdigo XAML (o se presentan en un listado separado en la ventana de propiedades en Blend). Resumiendo, podemos declarar los manejadores de eventos de dos formas: como siempre hemos hecho, desde el cdigo C#, o desde el propio XAML, como ya vimos antes. En el ejemplo siguiente, declaramos dos procedimientos de evento para dos rectngulos, dentro de un StackPanel. Ambos cambiarn de color al entrar el ratn en las reas visuales de cualquiera de ellos, y ambos volvern a asumir el que tenan, cuando el cursor salga. Las declaraciones de los eventos MouseEnter y MouseLeave son equivalentes, aunque una est en XAML y la otra en C#:
En WPF esto no tiene por qu suceder siempre de abajo-arriba en la jerarqua, sino que puede ser igualmente de arriba- abajo, mediante el mecanismo de "tunnelling".
1
pgina
127
listado 6-2 <Grid x:Name="LayoutRoot" Background="Beige"> <Border Background="BurlyWood" Width="400" Height="250"> <StackPanel Orientation="Horizontal" Margin="8,8,8,8"> <Rectangle x:Name="Thumb1" Fill="Blue" Width="110" Margin="8,8,8,8" HorizontalA lignment="Left" MouseEnter="Thumb1_MouseEnter" /> <Rectangle x:Name="Thumb2" Fill="Blue" Width="110" Margin="8,8,8,8" HorizontalA lignment="Right" MouseEnter="Thumb1_MouseEnter" /> </StackPanel> </Border> </Grid>
listado 6-3 public Page() { InitializeComponent(); Thumb1.MouseLeave += new MouseEventHandler(Thumb1_MouseLeave); Thumb2.MouseLeave += new MouseEventHandler(Thumb1_MouseLeave); } void Thumb1_MouseLeave(object sender, MouseEventA rgs e) { Brush Fondo = new SolidColorBrush(Colors.Red); Thumb1.Fill = Thumb2.Fill = Fondo; } private void Thumb1_MouseEnter(object sender, MouseEventA rgs e) { Brush Fondo = new SolidColorBrush(Colors.Blue); Thumb1.Fill = Thumb2.Fill = Fondo; }
Para indagar un poco ms en profundidad lo que sucede, un vistazo con .NET Reflector2 a la clase Rectangle, nos muestra algunas cosas interesantes:
El producto ha sido adquirido recientemente por Red Gate quien se ha comprometido a continuar su desarrollo. No obstante, sigue siendo gratuito (www.RedGate.com).
128
pgina
>>
listado 6-4 public sealed class Rectangle : Shape { // Campos public static readonly DependencyProperty RadiusXProperty; public static readonly DependencyProperty RadiusYProperty; // Mtodos static Rectangle(); public Rectangle(); // Propiedades public double RadiusX { get; set; } public double RadiusY { get; set; } }
La clase dispone de dos constructores: uno esttico y otro dinmico, (algo muy comn en .NET), pero sobre todo sus dos miembros pblicos modificables (RadiusX y RadiusY), se corresponden con sendas variables estticas (RadiusXProperty y RadiusYProperty) que se declaran como DependencyProperty (si seguimos profundizando, veremos que toda la jerarqua de UIElement se basa precisamente en ellos). Por otro lado, la plataforma reconocer automticamente el tipo de evento que estamos declarando y construir igual que en .NET clsico dos argumentos para el procedimiento de evento que nos indiquen quin lo ha disparado y qu argumentos se le pasan. Pongamos el caso de las entradas de teclado. Si queremos controlar el comportamiento de dos cajas de texto desde un control de nivel superior que contenga ambas cajas, podemos codificarlo de la siguiente manera:
listado 6-2 <Grid x:Name="LayoutRoot" Background="White"> <Canvas Width="400" Height="150" Background="BlanchedA lmond" KeyUp="ControlTeclado"> <TextBox x:Name="T1" Width="100" Height="40" Margin="15,20"/> <TextBox x:Name="T2" Width="100" Height="40" Margin="15,70"/> <TextBlock Name="txtInfo" Canvas.Top="120" Canvas.Left="15" Text="Informacin:"/> </Canvas> </Grid>
pgina
129
Con lo que averiguamos cul de los TextBox se encuentra activo (figura 6-1). El truco reside en que no utilizamos el argumento Sender, sino la propiedad Source del segundo argumento (e). La misma tcnica puede extenderse para todos los casos en los que sea conveniente manejar lo que sucede dentro de un colectivo de controles anidado en otro, a travs de su control contenedor.
figura 6-1
Estilos y plantillas
Una de las herramientas ms poderosas para disear el aspecto visual de un elemento en Silverlight 2, es la definicin de estilos y/o plantillas propias. Esta tcnica permite cambiar la apariencia y la estructura grfica de los elementos XAML en sus dos tipos de comportamiento: esttico y dinmico. Podemos definir un estilo como un recurso de toda la aplicacin o de forma individual para una sola pgina o un simple elemento. El mbito depender del lugar donpgina
130
>>
de se declare el estilo, y de algunos atributos opcionales como TargetType, que indicar el tipo de objeto al que es aplicable. Ms adelante, podemos utilizar ese mismo estilo en todos los elementos similares o que cumplan una condicin establecida por nosotros. Y si lo que se desea es cambiar la apariencia de un control ms all de sus propiedades directas, la tcnica consiste en crear un ControlTemplate, que define la apariencia y el comportamiento para todos sus estados posibles. Siempre que se trate de un elemento que herede de FrameworkElement, podemos asociarle un estilo (Style).
131
listado 6-7 <Grid x:Name="LayoutRoot" Background="White"> <StackPanel> <StackPanel.Resources> <Style x:Key="EstiloBoton" TargetType="Button"> <Setter Property="Height" Value="20"/> <Setter Property="Margin" Value="12"/> <Setter Property="Background" Value="Magenta"/> <Setter Property="FontFamily" Value="Verdana" /> <Setter Property="FontSize" Value="14"/> <Setter Property="FontWeight" Value="Bold"/> </Style> </StackPanel.Resources> <Button Content="Uno" Style="{StaticResource EstiloBoton}" /> <Button Content="Dos" Style="{StaticResource EstiloBoton}" /> <Button Content="Tres" Style="{StaticResource EstiloBoton}" Background="Blue"/> <Button Content="Cuatro" Background="Blue"/> <Button Content="Cinco" x:Name="BotonCinco" Loaded="BotonCinco_Loaded" /> <Button Content="Seis" /> </StackPanel> </Grid>
probarlo). Finalmente, el sexto botn no tiene ningn estilo ni atributo asociado, por lo que presenta su apariencia por defecto, para servir de comparacin. Este es el cdigo asociado al evento Loaded del quinto botn:
listado 6-8 private void BotonCinco_Loaded(object sender, RoutedEventA rgs e) { Random aleatorio = new Random(DateTime.Now.Millisecond); int R = aleatorio.Next(0, 255); int G = aleatorio.Next(0, 255); int B = aleatorio.Next(0, 255); BotonCinco.Background = new SolidColorBrush(Color.FromA rgb(255, (byte)R, (byte)G, (byte)B)); }
132
pgina
>>
No obstante, el lector puede ver en la salida del cdigo anterior, que, los botones, con o sin estilo aplicado, heredan una cierta cualidad visual predeterminada (incluyendo cambios visuales al pasar el cursor por encima): son parte de la plantilla que define ese control. Muchas veces, las plantillas no slo definen la apariencia (esttica), sino el comportamiento visual (dinmico) de un control en sus distintos estados posibles. Podemos crear un estilo que reescriba el contenido de la propiedad ControlTemplate, y, al aplicarlo a un control, habremos cambiado totalmente su aspecto grfico.
figura 6-2
Vamos a continuar con el caso anterior. Si ahora queremos hacer un botn, con los bordes redondeados y apariencia plana, podemos aadir lo siguiente a la definicin del estilo anterior:
listado 6-9 <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Width="95" Height="35" BorderThickness="3" BorderBrush="Maroon" CornerRadius="16" Background="Beige" > <TextBlock Text="Botn" Foreground="Navy" HorizontalA lignment="Center" VerticalA lignment="Center"/> </Border> </ControlTemplate> </Setter.Value> </Setter>
pgina
133
Esto redefine la plantilla del botn, y por tanto, anula la predeterminada, pasando a tener una apariencia totalmente dependiente de la nueva (hemos cambiado la orientacin del StackPanel contenedor de vertical a horizontal, pero esto no afecta significativamente a la apariencia grfica de cada botn):
figura 6-3
En la imagen, se muestran los 4 ltimos botones. El cambio tan drstico se produce porque toda la apariencia est empotrada dentro de ControlTemplate. Si queremos permitir que se modifique manualmente un solo atributo, dejando que la plantilla se haga cargo del resto, y ese atributo est definido y recibe un valor en la plantilla, nuestra asignacin manual no funcionar: seguir tomando los valores de la plantilla. Para resolver este problema se utiliza la asignacin tarda mediante la palabra reservada TemplateBinding.
Asignacin Late Binding de una plantilla La tcnica del enlace tardo permite contar con dos niveles de personalizacin (general y especfico). Se puede utilizar la extensin de marcado TemplateBinding (solo dentro de la plantilla de un control), asignando como valor una propiedad accesible. Por ejemplo, al cambiar el cdigo anterior, aadiendo Background ={TemplateBinding Background} a los atributos del objeto Border, la salida grfica s que reconocera el cambio de fondo que habamos aplicado al tercer botn de la serie, como podemos ver en la figura 6-4:
figura 6-3
134
pgina
>>
Ahora, el tercer botn tiene un fondo individualizado, aunque toma el resto de sus propiedades de la plantilla. Adems, ControlTemplate podra ser mucho ms complejo y contener, a su vez, otro control: un Image, para aadir un grfico o cualquier otro diseo que consideremos conveniente. ContentPresenter Relacionado con esta operativa est el elemento ContentPresenter, que permite establecer la propiedad Content, en las plantillas de aquellos controles que la poseen. ContentPresenter debe utilizar un atributo TemplateBinding para asociar la propiedad Content de ContentControl con su equivalente de ContentPresenter. Mejor verlo con un ejemplo: el cdigo siguiente muestra un botn que define su ControlTemplate y su ContentPresenter. Adems, incluye una imagen, y un fondo personalizados. El texto del botn est vinculado mediante la tcnica que acabamos de comentar:
listado 6-10 <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="170"/> </Grid.RowDefinitions> <TextBlock Text="Demo de ContentPresenter" FontFamily="Verdana" FontSize="18" FontWeight="Bold" Foreground="#FF5C9A C9" Grid.Row="0" HorizontalA lignment="Center" /> <Button Content="Botn" FontFamily="Verdana" FontSize="18" Grid.Row="1" Background="BurlyWood" Width="230" Height="90" HorizontalA lignment="Center" VerticalA lignment="Center"> <Button.Template> <ControlTemplate TargetType="Button" x:Name="ButtonTemplate"> <Grid> <Border BorderThickness="5" BorderBrush="Navy" CornerRadius="16" Background="{TemplateBinding Background}" /> <ContentPresenter x:Name="ButtonPresenter" Content="{TemplateBinding Content}" HorizontalA lignment="Center" VerticalA lignment="Center" /> <Image Source="Graficos/serv.png" HorizontalA lignment="Left" VerticalA lignment="Center" Margin="15,12,0,12"/> </Grid> </ControlTemplate> </Button.Template> </Button> </Grid>
pgina
135
Este cdigo produce la salida de la figura 6-5, y es claro que el contenido de ContentPresenter, puede ser cualquier combinacin que el usuario desee (hemos escogido mantener el fondo de la imagen para que se aprecie bien la posicin). ContentPresenter hereda de FrameworkElement.
figura 6-5
Por tanto, todo control puede ser modificado a partir de su plantilla original de presentacin visual, adems de los cambios que hagamos en su comportamiento mediante cdigo. Eso significa que tenemos la capacidad de alterar el aspecto grfico de cualquier control de una aplicacin (predeterminado o no), o crear diseos que se conviertan en controles personalizados. Expression Blend se presta especialmente bien para trabajar con las plantillas originales de los controles. Nos permite cambiarlos, crear una copia nueva solo para la pgina que estemos haciendo o generar todo un diseo desde cero, pudiendo manejar sus comportamientos en la interaccin con el usuario, si le aadimos algo de trabajo con la herramienta Visual State Manager.
136
>>
figura 6-6
junto a cada elemento una serie de vnculos a esos recursos donde vemos la utilizacin de elementos que hemos estado estudiando en ejemplos anteriores: ContentTemplates, TemplateBindings, etc. Omitimos reproducir el cdigo XAML correspondiente por las restricciones de espacio, pero esos son los ladrillos constituyentes de que hablbamos antes. Como el mismo proceso puede repetirse para cualquier control predeterminado, tenemos una libertad sin precedentes a la hora de construir controles. Vamos a continuar con el sistema de animaciones y, al terminar, estaremos en disposicin de crear nuestros propios controles (o versiones de los existentes) que incorporen todo esto.
Animaciones
Las nuevas interfaces de usuario se caracterizan por su dinamismo. Ese ha sido uno de los elementos diferenciadores de Adobe Flash, y sa es una de las grandes
pgina
137
virtudes de esta versin de Silverlight. Entendemos por animacin una ilusin visual, creada mediante la proyeccin rpida y continua de varias imgenes o grficos, cada uno de ellos ligeramente distinto del anterior. El mecanismo de creacin de vdeo no es ms que un sistema de animaciones de fotogramas. Pero no vamos a ocuparnos aqu de los vdeos, sino de las animaciones de grficos generadas por nosotros.
Animaciones en Silverlight En Silverlight, animamos un objeto XAML aplicando elementos de animacin a sus propiedades individuales. Por ejemplo, para que un elemento de la interfaz de usuario crezca, procedemos a aumentar progresivamente sus propiedades Width o Height. Si queremos que se oculte poco a poco, vamos reduciendo el valor de su propiedad Opacity, y as sucesivamente. La versin de XAML soportada por Silverlight contiene muchos objetos cuyas propiedades son susceptibles de animarse, en este sentido. Ahora bien, solo pueden animarse aquellas cuyos valores sean del tipo double, color o point. La excepcin es que tambin pueden animarse valores de propiedades mediante la denominada interpolacin discreta (el salto de un valor a otro sin pasar por estados intermedios, que realmente, no es una forma de interpolacin). Veremos despus algo ms sobre los dos tipos de interpolaciones soportadas (lineales y no-lineales) y la forma de programarlos.
Aunque el concepto de transformacin es similar, no debe confundirse con las conocidas Transformadas de Fourier (para ms datos al respecto de este aparato matemtico ver http://es.wikipedia.org/wiki/Transformada_de_Fourier y tambin (en ingls) An Intuitive Explanation of Fourier Theory, en
3
138
http://enteos2.area.trieste.it/russo/LabInfoMM2005-2006/ProgrammaEMaterialeDidattico/daStudiare/009FourierInSpace.html.
pgina
>>
Vamos a comenzar por stas ltimas. Transformaciones Las transformaciones se crean aplicando propiedades de transformacin a distintos tipos de objetos. Dependiendo del elemento, las transformaciones soportadas sern, tambin, distintas (por ejemplo, los objetos Brush admiten varias formas, los objetos Geometry utilizan su propiedad Geometry.Transform y los que pertenezcan a la categora UIElement, utilizarn la propiedad RenderTransform). Son objetos declarados en XAML, y por tanto, si los identificamos con un nombre, podremos modificarlos posteriormente mediante cdigo C# o VB.NET. Veamos algunos ejemplos.
Propiedades RotateTransform y ScaleTransform La primera, permite girar un elemento un ngulo especificado alrededor de un punto dado en el sentido de las agujas del reloj. Por ejemplo, el cdigo siguiente produce una rotacin de un texto tal y como vemos en la figura 6-7. Como puede verse en el cdigo de la figura adjunta, aadimos el atributo RenderTransform al cuerpo del control (en este caso, el TextBlock), y especificamos una transformacin por rotacin, con un ngulo de 45 empezando el proceso en las coordenadas 150, 30, a partir de la esquina superior izquierda del control. Una imagen visual
figura 6-7
Cdigo fuente y resultado visual del fragmento de cdigo correspondiente a una rotacin
pgina
139
para darse cuenta exactamente del proceso, consiste en clavar un alfiler mentalmente en las coordenadas indicadas, y rotar los grados que se indiquen toda la superficie del control afectado. En el caso de la transformacin de escala (ScaleTransform), se sigue un patrn similar al anterior, pudiendo indicarse la direccin del escalado y el punto a partir del cual deseamos que se haga. El cdigo siguiente, producira un escalado en el eje de las X de un rectngulo (tenga en cuenta que RenderTransform solo se puede establecer una vez):
listado 6-10 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid> <Rectangle Width="150" Height="50" Canvas.Left="150" Fill="Blue"> <Rectangle.RenderTransform> <ScaleTransform ScaleX="1.5" CenterX="150" /> </Rectangle.RenderTransform> </Rectangle> </Grid> </Page>
De la misma forma, para aplicar las Transformaciones de Elongacin y Translacin la propiedad RenderTransform aplicada al mismo rectngulo podra tomar los siguientes valores:
listado 6-11 <Rectangle.RenderTransform> <SkewTransform A ngleX="45 /> </Rectangle.RenderTransform>
140
pgina
>>
...para un desplazamiento de 30 pxeles a la izquierda y arriba respecto a su posicin inicial. Creacin de transformaciones mediante cdigo Tambin es posible crear transformaciones de forma dinmica, sin ningn tipo de diseo previo. Para ello, solo necesitamos un elemento que sirva de contenedor, e ir creando los objetos de la transformacin con sus propiedades, para incluirlos finalmente en dicho contenedor. La tcnica es muy similar a la utilizada para la creacin dinmica de animaciones, por lo que remito al lector a ese aspecto que tratamos ms adelante con un ejemplo completo. Matrices de transformacin y TransformGroups Para los casos en los que deseamos realizar una transformacin mltiple, disponemos de dos opciones: una basada en la matemtica matricial, que se fundamenta en definir una Matriz de Transformacin, mediante el elemento MatrixTransform y la otra, mucho ms sencilla, consistente en utilizar el elemento TransformGroup, que permite exactamente eso: agrupar en un solo proceso un conjunto de transformaciones diversas. En el siguiente ejemplo, tenemos un grupo mltiple de transformacin que produce simultneamente las transformaciones de rotacin, escalado y elongacin:
listado 6-13 <Rectangle.RenderTransform> <TransformGroup> <RotateTransform A ngle="45" CenterX="150" CenterY="30" /> <ScaleTransform ScaleX="1.5" CenterX="150" /> <SkewTransform A ngleX="45" /> <TransformGroup> </Rectangle.RenderTransform>
Animaciones
Indicbamos al comienzo de este apartado que las animaciones se basan en transiciones de estado. Es decir, el valor de una propiedad vara desde un estado inicial hasta otro estado o valor final, produciendo la sensacin visual de movimiento, o ms generalmente, de campgina
141
bio. Esas transiciones de estado tienen lugar a lo largo de un perodo de tiempo, y existen dos conceptos vinculados a toda animacin que son fundamentales: la lnea de tiempo (timeline) y la duracin del proceso de animacin (duration). La duracin es simplemente un valor escalar que indica al sistema cuando debe concluir el proceso despus de iniciado. La lnea de tiempo, es algo ms compleja y permite otras posibilidades tales como establecer un comportamiento para la transicin entre estados, en caso de que no deseemos que sta sea uniforme (por ejemplo, puede ir rpido al principio, ms lento en el medio y nuevamente rpido al final), combinar animaciones, y mucho ms. Al trabajar con animaciones, definimos todo el proceso a travs de la lnea de tiempo, indicando la duracin y caractersticas de cada uno de los estados intermedios. Estos estados intermedios reciben el nombre de KeyFrames.
Triggers
Una animacin es un proceso que tiene lugar siempre como respuesta a un evento. Y, en algunos casos, este evento puede ser expresado nicamente en Silverlight XAML mediante un desencadenador o trigger, representado por un atributo ms del elemento y programado como parte de su definicin de estilo. A diferencia de lo que sucede en WPF, solo existe un tipo de trigger soportado, el EventTrigger (en WPF disponemos de DataTriggers, MultiTriggers y otros elementos similares). Adems, como un elemento puede responder a ms de una accin, se dispone de una coleccin de Triggers (de ratn, de teclado, etc), de forma que si queremos que, como respuesta a alguna accin por parte del usuario o del sistema, se produzca la reaccin correspondiente, debemos asignar al atributo un manejador adecuado.
142
>>
El cdigo siguiente genera un rectngulo, y lo desplaza hasta el punto 300 del eje de las X, volviendo nuevamente a su posicin original. La duracin de cada desplazamiento se ha establecido en 5 segundos.
listado 6-13 <UserControl x:Class="A nimacioni2.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300"> <Canvas x:Name="LayoutRoot" Background="White"> <Rectangle x:Name="rect" Fill="BlueViolet" Canvas.Top="100" Canvas.Left="10" Width="100" Height="100"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard x:Name="A nimacion2"> <DoubleA nimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" A utoReverse="True" Duration="0:0:5" To="300"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Canvas> </UserControl>
Lo nico que hemos hecho es aadir un trigger a la coleccin de triggers del elemento Rectangle, indicar al trigger que se vincule con el evento Loaded (como dijimos antes, el nico posible si no queremos aadir cdigo paralelo para manejarlo), y establecer sus propiedades. Aqu hemos incluido el objeto DoubleA nimation dentro de un elemento Storyboard, y, ste, dentro de un BeginStoryboard, encargado de distribuir las animaciones que contiene a los objetos y propiedades adecuadas (por el momento no existe un objeto-contrapartida llamado EndStoryboard).
pgina
143
Vinculamos directamente el evento a su manejador en el cdigo XAML, e incluimos toda la animacin dentro los recursos de <Rectangle>. Ya hemos visto que es la forma en que Visual Studio crea automticamente un manejador de evento en el que programar el comienzo de la animacin:
144
pgina
>>
Si el lector prueba el ejemplo anterior, observar un efecto adicional que hemos aadido a propsito para ilustrar mejor los conceptos de Animation y StoryBoard: la ida y vuelta del rectngulo se repite dos veces: una por cada aparicin del atributo A utoReverse=True, ya que ste puede aparecer como parte de todo el Storyboard (contenga las animaciones que contenga), y tambin de cada una de las animaciones individuales.
nota
listado 6-16
Esto mismo puede hacerse con un botn en lugar de un rectngulo, o cualquier otro UIElement. El evento correspondiente variar en funcin de los que tenga definidos el elemento.
public void CreaYEjecutaA nimation(object sender, EventA rgs e) { // Creamos el rectngulo a animar Rectangle Rect1 = new Rectangle(); Rect1.Width = 180; Rect1.Height = 30; Rect1.SetValue(Canvas.LeftProperty, 50.0); Rect1.SetValue(Canvas.TopProperty, 50.0); // .. y sus propiedades Color myColor = Color.FromA rgb(255, 255, 125, 0); SolidColorBrush myBrush = new SolidColorBrush(); myBrush.Color = myColor; Rect1.Fill = myBrush; // A amos el rectngulo al contenedor. LayoutRoot.Children.A dd(Rect1);
pgina
145
listado 6-16
(Cont.) // establecemos la duracin Duration duration = new Duration(TimeSpan.FromSeconds(0.5)); DoubleA nimation A nimation1 = new DoubleA nimation(); A nimation1.Duration = duration; A nimation1.To = 200; // Y creamos la animacin Storyboard sb = new Storyboard(); sb.Duration = duration; sb.Children.A dd(A nimation1); Storyboard.SetTarget(A nimation1, Rect1); Storyboard.SetTargetProperty( A nimation1, new PropertyPath("(Canvas.Left)")); // A adir el Storyboard al recurso, para vincular animacin y rectngulo LayoutRoot.Resources.A dd("Key1", sb); sb.Begin(); //Lanzar la animacin
El efecto es similar al anterior e idntico funcionalmente. Solo falta aadir la llamada a este mtodo en el manejador de evento que nos interese (o en InitializeComponent() si deseamos que se ejecute al cargar la pgina).
146
pgina
>>
figura 6-8
La lnea de tiempo abarca un total de 4.5 segundos, y est dividida en 3 segmentos de duracin variable
El esquema del siguiente ejemplo queda ilustrado en la figura 6-8. Supongamos que en el caso del dibujo se trata de animar la propiedad Canvas.Left, al igual que hicimos antes, pero queremos que se realice en 3 fases: en la primera se recorren 100 pxeles/seg., en la segunda 200 en 3 segundos (va ms despacio) y en la tercera 100 pxeles en 0,5 seg. (la ms rpida de todas). Para ello, en el Explorador de proyectos, seleccionamos la opcin Abrir con Expression Blend en el men contextual del fichero Page.xaml, y una vez all, seleccionamos el control que deseamos animar (de nombre rect en el ejemplo). Despus, sobre el panel izquierdo, en la sub-ventana Objects and Timeline, creamos la animacin aqu con el nombre sbMovimiento, y para editarla, movemos la lnea vertical amarilla hasta el punto final de la duracin del primer segmento (hay un punto rojo en la parte superior de la pantalla, indicando que Timeline recording is on, que significa que se estn almacenando los cambios en las propiedades para generar despus el cdigo XAML correspondiente). Hecho esto, asignamos el nuevo valor que deseamos para la propiedad. Si modificamos ms de una propiedad, Blend crear un elemento StoryBoard por cada una.
figuras 9a y 9b Editor de lneas de tiempo en Expression Blend mostrando los 3 puntos segmentacin de la animacin. A la derecha, sub-ventana de edicin de posiciones de objetos mostrando el valor de la propiedad Left al final de la animacin
pgina
147
Este proceso lo repetimos para cada segmento. El resultado es un cdigo algo distinto, que utiliza un elemento llamado DoubleA nimationUsingKeyFrames, que contiene elementos SplineDoubleKeyFrame, cada uno de ellos indicando el tiempo transcurrido para cada KeyFrame y el valor de la propiedad a cambiar que debe obtenerse en ese momento. El cdigo de la animacin es el siguiente:
listado 6-17 <UserControl.Resources> <Storyboard x:Name="sbMovimiento"> <DoubleA nimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)"> <SplineDoubleKeyFrame KeyTime="00:00:01" Value="100"/> <SplineDoubleKeyFrame KeyTime="00:00:04" Value="300"/> <SplineDoubleKeyFrame KeyTime="00:00:04.5" Value="400"/> </DoubleA nimationUsingKeyFrames> </Storyboard> </UserControl.Resources>
Si el lector ejecuta este cdigo ver, que, si bien el desplazamiento se produce en tres tramos y en cada uno la velocidad es distinta (se recorre distinta distancia por unidad de tiempo), dentro de cada tramo la velocidad es constante, como no poda ser de otra forma, de acuerdo con la definicin. Se ha producido aqu un proceso de interpolacin lineal, para cada una de las tres fases. En la figura 6-10a vemos el grfico correspondiente para una interpolacin lineal, que se obtiene en Blend pulsando en cada uno de los smbolos indicados por las flechas verdes de la figura 6-9. La curva que describe la interpolacin (el clculo de valores intermedios entre dos dados previamente), es en realidad una recta. Eso nos est indicando que sta se ha realizado en saltos discretos y uniformes, sin cambios bruscos dentro de cada fase, y generando, de ese modo, velocidades constantes en el desplazamiento.
148
pgina
>>
figuras 6-10a y 6-10b Representacin grfica de la interpolacin lineal y curva propia del uso de KeyFrames
Interpolacin no-lineal Por tanto, la cuestin es: se pueden utilizar interpolaciones no lineales que permitan expresar conceptos como la aceleracin (entendiendo esto por el cambio brusco entre valores en el camino hacia el valor final)? La respuesta, naturalmente, es que s. Pero, en este caso, lo que haremos es modificar el trazado para convertirlo en una curva, y de una manera totalmente visual. El eje de las X representa el tiempo necesario para llegar a un valor dado, mientras que los valores intermedios hacia el valor final se representan en el eje de las Y. Por tanto, cada cambio en la curva, provocar resultados distintos. As pues, si volvemos al modo de edicin y en la ventana Easing, que nos describe la curva de interpolacin desplazamos los puntos amarillos hacia otro lugar de la cuadrcula, modificaremos el trazado, creando una curva de interpolacin que dar como resultado aceleraciones y/o deceleraciones (el grfico de la figura 6-10-b muestra el resultado del cambio). El nuevo patrn obtenido en nuestro ejemplo, es una curva con un punto de inflexin que pasa directamente por el centro. En el primer tramo, se produce una aceleracin que va descendiendo mientras se adoptan valores intermedios, volviendo a acelerarse en el tramo final. De hecho, como puede intuir el lector, existe una relacin directa entre los valores intermedios obtenidos y los valores de la funcin representada aqu.
pgina
149
En el cdigo XAML, no se han producido muchos cambios, en realidad. De hecho, lo nico que ha cambiado es que el segundo elemento SplineDoubleKeyFrame, contiene ahora un subelemento KeySpline, donde se definen esas fluctuaciones de acuerdo con la sintaxis siguiente:
listado 6-18 <SplineDoubleKeyFrame KeyTime="00:00:04" Value="300"> <SplineDoubleKeyFrame.KeySpline> <KeySpline ControlPoint1="0.1019,0.899" ControlPoint2="0.893999993801117,0.094"/> </SplineDoubleKeyFrame.KeySpline> </SplineDoubleKeyFrame>
Se indican 2 puntos de control a partir de los cuales el motor de Silverlight realizar la interpolacin de acuerdo con la curva definida. Pueden crearse tantas curvas distintas como permitan las combinaciones posibles donde situar los dos modificadores resaltados con un punto amarillo.
El modelo Parts-And-States
Trabajando con este modelo, muchos de los controles suministrados con esta versin encapsulan su apariencia en una plantilla administrada por Visual State Manager, que maneja sus 3 partes fundamentales:
pgina
150
>>
Parts (Partes): los elementos internos constituyentes del control. States & StateGroups (Estados y Grupos de estado): el cdigo XAML asociado con las transiciones entre estados para otorgar a cada uno su personalidad visual: qu es lo que sucede cuando el control pasa de tener al foco a no tenerlo, a estar pulsado, o deshabilitado, etc. Cada estado tiene su definicin, y Visual State Manager se encarga de la transicin entre estados. Transitions (Transiciones): cambios visuales, codificados normalmente como recursos, que se vincularn a un cambio de estado.
Partes
Una parte es un elemento con nombre dentro de un control. Es importante para referirnos a l con posterioridad, tanto por cdigo como desde Blend, y tambin resulta fundamental a la hora de la creacin de plantillas de controles personalizados.
151
Esta divisin en grupos de estado es muy importante en la codificacin de la plantilla, porque permite la agrupacin de acciones comunes aplicables despus, evitando as la multiplicacin combinatoria de estados que podra producirse de otra forma. Como veremos a continuacin, tras editar la plantilla de un botn, la ventana de interacciones (Interaction), nos muestra todos los estados posibles de un control y cules son las acciones vinculadas con las transiciones entre estados. Ah podemos modificarlas para crear nuestros propios efectos, o crear transiciones entre estados especiales definidos por el usuario.
Deconstruyendo a Button
Para observar cmo estn hechos los controles predeterminados, nada mejor que seleccionar uno en la ventana de diseo de Blend y, en su men contextual, escoger el tem Edit Control Parts -> Edit a Copy. Veremos que en la ventana superior izquierda del IDE de Blend aparecen definidos el conjunto de estados posibles del control y cules son las acciones a tomar en las transiciones entre un estado y otro, como aparece en la figura 6-11, tras haber hecho esto con un control Button.
figura 6-11
Al igual que suceda con las animaciones, cuando marcamos uno de los estados en la ventana, aparece una notificacin en la superficie de diseo para avisarnos de que se estn grabando las acciones en formato XAML (State Recording is on). Podemos hacer pruebas desactivando ese botn y volviendo a activarlo cuando queramos que se genere el cdigo XAML correspondiente.
pgina
152
>>
No obstante, en la escasa literatura al respecto (literatura digital, de momento), no se explica suficientemente un aspecto del funcionamiento de VSM como es la creacin de estados personalizados, dejando entrever, en ocasiones, que los citados antes son los nicos posibles (o que tienen sentido) y eso dista mucho de ser as.
figura 6-12
pgina
153
Obsrvese como se produce la rotacin cuando pasamos el cursor por encima del botn inferior. De eso se encarga VSM sin ningn cdigo en C# y optimizando todo el proceso. De la misma forma, al declarar el botn superior derecho como deshabilitado, el gestor de estado se encarga de aplicarle la opacidad disminuida creando el efecto oportuno.
figura 6-13
Situacin inicial de partida. Los dos elementos de la izquierda, estn ocultos al haberse definido con una coordenada negativa sobre el eje de las X
154
pgina
>>
Este diseo define por lo tanto, un estado lgico, no visual. El usuario puede o no haberse identificado. En este caso, el patrn Parts-and-States no tiene sentido, porque solo es aplicable a estados visuales. Lo que hacemos, pues, es sobre la Ventana de Estados, definir un nuevo StateGroup que tiene que ver con esta lgica, y que llamamos EstadoRegistro. Dentro de l aadiremos dos estados posibles: EstadoInicial y Registrado. No tocamos el estado inicial que coincidir con el del diseo. Pero en el estado Registrado, (despus de que el usuario haya pulsado el botn Entrar), definimos una transformacin por desplazamiento, e intercambiamos el valor de la propiedad X. As, los dos objetos Border que contienen los rtulos asumen valores opuestos a los que tenan (positivo por negativo, etc.) La consecuencia es que el rtulo del usuario se desplazar a la zona invisible y aparecer sbitamente el rtulo del saludo. Y lo mismo hacemos con el bloque de texto que muestra la informacin explicativa. Podemos comprobarlo, como siempre, pulsando en los dos estados y viendo los cambios. Ahora bien, como no se cambia esta vez el contexto visual pre-programado por el modelo, tenemos que intervenir en el cdigo fuente para llamar al cambio de estado, cuando se haya producido el cambio lgico. Bastar con programar el botn Entrar para que indique a Visual State Manager que el estado ha cambiado. A su vez, en el rtulo que corresponde al usuario registrado, utilizaremos el icono de usuario identificado para volver a la situacin inicial. El cdigo C# ser similar a esto:
listado 6-19 private void btnEntrar_Click(object sender, RoutedEventA rgs e) { VisualStateManager.GoToState(this, "Registrado", true); } private void ImgRegistrado_MouseLeftButtonUp(object sender, MouseButtonEventA rgs e) { VisualStateManager.GoToState(this, "EstadoInicial", true); }
Si adems, modificamos el tiempo de la transicin y asignamos 0,3 segundos en lugar del valor 0 predeterminado, obtendremos un cambio dinmico mucho ms agradable en la interfaz de usuario, cuando pasemos de un estado a otro (ver el cambio final en la figura 6-14). Y hemos aadido una etiqueta flotante para que el usuario sepa que el icono le lleva nuevamente al estado inicial. El cdigo XAML completo puede verlo en el listado 6-20.
pgina
155
figura 6-14
listado 6-20 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="EstadosYTransiciones.Page" Width="640" Height="480" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"> <Grid x:Name="LayoutRoot" Background="#FFD3CD6D" > <Grid.ColumnDefinitions> <ColumnDefinition Width="432"/> <ColumnDefinition Width="208"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="104.16"/> <RowDefinition Height="375.84"/> </Grid.RowDefinitions> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup x:Name="EstadoRegistro"> <vsm:VisualStateGroup.Transitions> <vsm:VisualTransition GeneratedDuration="00:00:00.3000000"/> </vsm:VisualStateGroup.Transitions> <vsm:VisualState x:Name="EstadoInicial"> <Storyboard/> </vsm:VisualState> <vsm:VisualState x:Name="Registrado"> <Storyboard>
156
pgina
>>
listado 6-20
(Cont.)
<DoubleA nimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="BordeRotulo2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3]. (TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> </DoubleA nimationUsingKeyFrames> <DoubleA nimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="BordeRotulo1" Storyboard.TargetProperty= "(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="-500"/> </DoubleA nimationUsingKeyFrames> <DoubleA nimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="textBlock" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3]. (TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> </DoubleA nimationUsingKeyFrames> <DoubleA nimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="textBlock1" Storyboard.TargetProperty="(UIElement.RenderTransform). (TransformGroup.Children)[3].(TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="-500"/> </DoubleA nimationUsingKeyFrames> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> </vsm:VisualStateManager.VisualStateGroups> <Border HorizontalA lignment="Stretch" Margin="12,12,12,12" BorderThickness="7,7,7,7" CornerRadius="18,18,18,18" BorderBrush="#FF000000" x:Name="BordeRotulo1" Background="#FFA 370B9" RenderTransformOrigin="0.5,0.5"> <Border.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Border.RenderTransform> <StackPanel Orientation="Horizontal" > <TextBlock Height="A uto" Width="123" TextWrapping="Wrap" Margin="21,21,21,21" HorizontalA lignment="Left"> <Run FontFamily="Verdana" FontSize="24" FontWeight="Bold" Text="Usuario:"/>
pgina
157
listado 6-20
(Cont.)
</TextBlock> <TextBox HorizontalA lignment="Left" VerticalA lignment="Center" Text="" TextWrapping="Wrap" BorderThickness="3,3,3,3" Width="135" Height="A uto" FontFamily="A rial" FontSize="24"/> <Button Height="35" HorizontalA lignment="Right" Margin="12,12,12,12" x:Name="btnEntrar" FontFamily="A rial" FontSize="24" Content="Entrar" Click="btnEntrar_Click" /> </StackPanel> </Border> <Image HorizontalA lignment="Stretch" Margin="40,12,40,-35.8400001525879" VerticalA lignment="Stretch" Grid.Column="1" Source="Graficos/find.png" Stretch="Fill"/> <Image Margin="40,63.8400001525879,40,184" Grid.Column="1" Grid.Row="1" Source="Graficos/search-web.png" Stretch="Fill"/> <Image Height="128" Margin="40,0,40,24" VerticalA lignment="Bottom" Grid.Column="1" Grid.Row="1" Source="Graficos/kdmconfig.png" Stretch="Fill"/> <Border HorizontalA lignment="Stretch" Margin="12,12,12,12" BorderThickness="7,7,7,7" CornerRadius="18,18,18,18" BorderBrush="#FF000000" x:Name="BordeRotulo2" Background="#FF9CC889" RenderTransformOrigin="0.5,0.5"> <Border.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform X="-450"/> </TransformGroup> </Border.RenderTransform> <StackPanel Orientation="Horizontal" > <TextBlock Height="A uto" Width="312" TextWrapping="Wrap" Margin="21,21,21,21" HorizontalA lignment="Left" VerticalA lignment="Center" FontFamily="Verdana" FontSize="27" Text="Bienvenido, Marino"/> <Image Height="A uto" HorizontalA lignment="Stretch" Margin="12,12,122,12" x:Name="ImgRegistrado" VerticalA lignment="Center" Width="A uto" RenderTransformOrigin="0.5,0.5" Source="Graficos/personal.png" Stretch="Uniform" MouseLeftButtonUp="ImgRegistrado_MouseLeftButtonUp"> <Image.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform X="-50"/> </TransformGroup>
158
pgina
>>
listado 6-20
(Cont.)
</Image.RenderTransform> </Image> <ToolTipService.ToolTip> <TextBlock>Pulse sobre el icono para salir</TextBlock> </ToolTipService.ToolTip> </StackPanel> </Border> <TextBlock Margin="32,35.8400001525879,40,48" Grid.Row="1" TextWrapping="Wrap" RenderTransformOrigin="0.5,0.5" x:Name="textBlock1"> <TextBlock.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </TextBlock.RenderTransform> <Run FontSize="20" FontWeight="Bold" Text="El veloz murcilago hind coma feliz cardillo y kiwi. "/> <LineBreak/> <Run FontSize="20" FontWeight="Bold" Text=" Y Ud. tambin comer feliz si entra en nuestro sitio y disfruta de todas sus ventajas!!"/> <LineBreak/> <Run FontSize="20" FontWeight="Bold" Text="Saludos"/> <LineBreak/> <Run FontSize="20" FontWeight="Bold" Text="El WebMaster"/> </TextBlock> <TextBlock Margin="32,35.8400001525879,40,48" TextWrapping="Wrap" OpacityMask="#FFCEDF18" RenderTransformOrigin="0.5,0.5" Grid.Row="1" FontSize="24" Foreground="#FF3E0404" Text="Gracias por registrarse. Como usuario registrado tendr ventanas especiales." x:Name="textBlock"> <TextBlock.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform X="-450"/> </TransformGroup> </TextBlock.RenderTransform> </TextBlock> </Grid> </UserControl>
pgina
159
160
>>
5. En el constructor de la clase que representa al control, asignamos la propiedad DefaultStyleKey al tipo de elemento que estamos creando:
this.DefaultStyleKey = typeof(Nombre_Del_Control);
Si es preciso, por ejemplo, por incluir algn control predeterminado, aadimos las DependencyProperties requeridas para almacenar valores del control. 6. Aadir TemplateBindings en las propiedades definidas en el ResourceDictionary para permitir la personalizacin de otros usuarios (esto puede hacerse como continuacin del paso 4, igualmente). 7. En caso de necesitar sustituir al contenido heredado, crear su intrprete de contenidos (ContentPresenter). Depender de las acciones a realizar por el control. 8. Aadir los eventos necesarios para gestionar la lgica interna (visual o de negocio). 9. (Opcionalmente) Aadir los estados visuales mediante Visual State Manager y retocar el punto 8 si es preciso. 10. Probar y publicar.
El control TextoDNI Vamos a construir un control muy sencillo, llamado TextoDNI, partiendo de cero, al que aadiremos la capacidad de validar la presencia de nmeros y calcular la letra del DNI automticamente al mostrarse en pantalla.
nota
Un control para produccin, llevara mucho ms cdigo que el que vamos a incluir aqu. Por propsitos didcticos, hemos mantenido el cdigo fuente lo mnimo imprescindible para que el control funcione y pueda ser probado.
Seguimos paso a paso las indicaciones apuntadas ms arriba, personalizndolas para este control. Paso 1) Queremos que sea capaz de validar al cargarse que los contenidos sean de tipo numrico y calcular la letra asociada a ese nmero suponiendo que se trata de un DNI y generando un mensaje estndar de error en caso contrario.
pgina
161
Paso 2) Crear una aplicacin Silverlight con sitio Web de pruebas, y aadir una librera de clases Silverlight (que llamamos CajaDNI) para albergar nuestro control. ste, deber heredar de Control o ContentControl (al final nos decantamos por ContentControl, por si el usuario decide modificar a su vez, toda la propiedad Content). Cambiamos el nombre de la clase a TextoDNI y escribimos un constructor vacio de momento. Compilamos, para poder hacer referencia a la DLL del proyecto desde el cdigo XAML. Paso 2-b) Antes de definir la interfaz, creamos un contexto vaco. Esto es, creamos la arquitectura y las referencias. Compilamos la librera CajaDNI, para generar la DLL. Hacemos referencia a ella desde el control Silverlight (Page.xaml). El cdigo aadido podra ser algo como esto:
xmlns:DNI="clr-namespace:CajaDNI;assembly=CajaDNI"
En la aplicacin Silverlight, aadimos el control, que no tiene representacin grfica, pero tendr que ser reconocido por el Editor con una lnea de este tipo: <DNI:TextoDNI />. Generamos la solucin. Paso 3) Definimos un estilo para el control, que, para propsitos de prueba, puede residir en el propio control, dentro de una etiqueta <Style>. Establecemos su TargetType. Deberamos de ver el aspecto del control en pantalla y el contenido si aadimos un elemento ContentPresenter. El cdigo podra ser algo as:
listado 6-21 <DNI:TextoDNI Content="123123123"> <DNI:TextoDNI.Style> <Style TargetType="DNI:TextoDNI"> Definiciones del estiloincluyendo una para Content </Style> </DNI:TextoDNI.Style> </DNI:TextoDNI Content="123123123">
Paso 4) Trasladamos el cdigo de la plantilla a un nuevo fichero Generic.xaml, que ubicamos en un subdirectorio de la librera de clases CajaDNI llamado Themes. Eliminamos el estilo de la interfaz de usuario, dejandolo como estaba al principio. Comprobamos que funciona exactamente igual con la plantilla de Generic.xaml (recurdese la propiedad Build Action asignada a Resource). El cdigo de Generic.xaml podra ser algo como esto:
pgina
162
>>
listado 6-22 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DNI="clr-namespace:CajaDNI;assembly=CajaDNI"> <Style TargetType="DNI:TextoDNI"> <Setter Property="Width" Value="180" /> <Setter Property="Height" Value="35" /> <Setter Property="Background" Value="Lavender" /> <Setter Property="FontSize" Value="14" /> <Setter Property="Margin" Value="10" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="DNI:TextoDNI"> <Grid x:Name="Contenedor"> <Rectangle Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Fill="{TemplateBinding Background}" StrokeThickness="3" Stroke="Purple" RadiusX="12" RadiusY="12" /> <ContentPresenter Content="{TemplateBinding Content}" HorizontalA lignment="Center" VerticalA lignment="Center"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
Paso 5-a) En la clase TextoDNI, establecemos su asignacin de plantilla mediante la propiedad DefaultStyleKey (ver sintaxis en punto 5 de los pasos a dar). Hemos usado TemplateBindings para las propiedades que queremos que sean modificables, por ejemplo, el fondo del rectngulo, la altura y la anchura. Como no nos estamos basando en ningn control anterior, el contenido estar vinculado al elemento ContentPresenter, cuya propiedad Content, tambin asignamos a un TemplateBinding. Paso 5-b) Aqu no es necesario, pero si el usuario probara la construccin de un control basndose en otro ya terminado, como un TextBox, para que el TemplateBinding sobre la propiedad Text del control TextBox sea reconocido por la clase TextoDNI, podemos definir en sta una propiedad Text como DependencyProperty, con la idea de mantener un mecanismo de almacenamiento del valor de la propiedad interna del TextBox en el control que lo encapsula. El cdigo para la definicin de esta propiedad en la clase TextoDNI sera el siguiente:
pgina
163
listado 6-23 private static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CajaTextoDNI), null); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }
A partir de este momento, se podra asignar el valor de la propiedad Text de TextoDNI desde el cdigo XAML igual que otra propiedad cualquiera que ofrezca el sistema de Intellisense. Paso 6) Para este ejemplo, ya hemos aadido antes los TemplateBindings de las propiedades personalizables. Paso 7) Tambin hemos definido ya un elemento ContentPresenter, as que no se necesita hacer nada. Paso 8) Programacin de la validacin y el clculo de la letra del DNI. Para ello, creamos el manejador del evento Loaded, que realizar la validacin numrica y el clculo nada ms cargarse el control, justo antes de mostrarlo en pantalla (el algoritmo de clculo es trivial y el lector puede comprobarlo en el cdigo del listado 6-24, que representa el estado final de la clase). Y eso es todo. Hemos preferido mantener el cdigo de la clase lo ms compacto y reducido posible para mostrar la funcionalidad y la estructura sin ningn adorno, ni ms comprobaciones que la que hacemos utilizando expresiones regulares, que acredita que el valor introducido solo contiene nmeros. El clculo de la letra del DNI, es muy sencillo, como puede verse. Paso 9) Compilar, probar, etc. Como prueba final, podemos escribir un contenido de Page.xaml para tener un par de controles TextoDNI independientes, con validacin y clculo. El cdigo de pruebas puede verlo en el listado 6-25. Con l obtendr una salida como la de la figura 6-14:
figura 6-14
164
pgina
>>
listado 6-24 using using using using System; System.Windows; System.Windows.Controls; System.Text.RegularExpressions;
namespace ControlesDNM { public class CajaTextoDNI : ContentControl { string Letras = "TRWA GMYFPDXBNJZSQVHLCKET"; Regex reNum = new Regex(@"^\d+$"); private static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CajaTextoDNI),null); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } public CajaTextoDNI() { this.DefaultStyleKey = typeof(CajaTextoDNI); this.Loaded += new RoutedEventHandler(CajaTextoDNI_Loaded); this.LostFocus += new RoutedEventHandler(CajaTextoDNI_LostFocus); } void CajaTextoDNI_LostFocus(object sender, RoutedEventA rgs e) { string Texto = (e.Source as TextBox).Text; bool esNumero = reNum.Match(Texto).Success; if (Texto != "" && esNumero) { (e.Source as TextBox).Text += " - " + Letras[Int32.Parse(Texto) % 23]; } else { (e.Source as TextBox).Text = "Error en el DNI"; } } void CajaTextoDNI_Loaded(object sender, RoutedEventA rgs e) { bool esNumero = reNum.Match(Text).Success; if (Text != "" && esNumero) { Text += " - " + Letras[Int32.Parse(Text) % 23]; } else { Text = "Error en el DNI"; } } } }
pgina
165
listado 6-25 <UserControl x:Class="ctlDNI_RC0.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DNI="clr-namespace:CajaDNI;assembly=CajaDNI" Width="300" Height="150"> <Grid x:Name="LayoutRoot" Background="Beige"> <StackPanel Orientation="Vertical" Grid.Row="0" VerticalA lignment="Center"> <DNI:TextoDNI Content="87654321" Width="150" /> <DNI:TextoDNI Content="12345678" Background="Gainsboro" /> </StackPanel> </Grid> </UserControl>
A partir de aqu son muchas las modificaciones que podramos hacer, tanto en la clase (control de otros comportamientos, ms propiedades de dependencia, etc.), como en la plantilla de Generic.xaml: por ejemplo, aadiendo estilos visuales para el control de los distintos estados del control, etc.
166
pgina
captulo
El tratamiento de datos
El modelo de ejecucin en sandbox que propone Silverlight supone que los mecanismos de acceso a datos no pueden seguir los patrones tpicos de las aplicaciones de escritorio, y, en buena parte, de las aplicaciones Web tradicionales. En su lugar, Silverlight plantea un modelo de acceso a datos basado en servicios Web, en los que podemos utilizar servicios tradicionales ASMX basados en SOAP, servicios WCF, servicios REst y ADO.NET Data Services (antes llamados proyecto Astoria). En el momento de escribir esto, ya existe una versin de ADO.NET Data Services disponible que forma parte del Service Pack 1 de Visual Studio 2008. Puede considerarse una extensin de Data Entity Framework, pero especialmente pensada para su uso en aplicaciones RIA. En cualquier caso incluyendo el acceso a objetos de negocio locales que pudiramos tener en nuestra aplicacin, es imprescindible tener claro el concepto de DataBinding en Silverlight, de manera que empezaremos por ver cmo funciona este mecanismo y continuaremos analizando las opciones de acceso a datos ms atractivas que presenta la plataforma. stos son los aspectos a abordar en este captulo: 1) Databinding en Silverlight 2. 2) Implementacin de Databinding de un solo sentido en una clase personalizada. 3) Acceso a datos desde un servicio WCF y presentacin de datos en un control DataGrid. 4) Acceso a servicios SOAP tradicionales. 5) Interaccin con servicios RESTful sobre HTTP: acceso a orgenes de datos RSS. 6) ADO NET Data Services. 7) Vinculacin de objetos de datos desde Expression Blend 2.0.
pgina
167
DataBinding en Silverlight 2
La separacin entre los datos mostrados y su origen se asemeja a la separacin entre diseo y funcionalidad que preconiza el modelo de trabajo de Silverlight. Dicha separacin no supone un problema en el cdigo XAML gracias al mecanismo de DataBinding. Ya hemos visto algunas de las formas de utilizacin del enlace a datos en los captulos anteriores, especialmente, a la hora de usar los recursos definidos a nivel de aplicacin. Vamos a ahondar algo ms en el tema (recordando que los mecanismos de enlace a datos en Silverlight 2, son un subconjunto de los existentes en Windows Presentation Foundation). Se trata de vincular un dato existente en una propiedad del CoreCLR con una propiedad de un UIElement (elemento de la interfaz de usuario) como Text, Background, o cualquier otra. Hacer corresponder un origen y un destino, de forma que los mecanismos del CoreCLR mantengan ambos en sincrona (al menos, inicialmente).
El sistema se comporta como una maquina conectora, que enlaza los orgenes de datos con sus destinos. Ahora bien, puede funcionar en 3 modos (BindingMode): OneTime: binding nico o inicial: que actualiza los datos una sola vez con la informacin del origen, cuando se crea el binding. OneWay: binding de un solo sentido: Actualizan el destino con los datos de origen cuando se crea el binding y cada vez que cambia el origen. TwoWay: binding en los dos sentidos: actualiza ambos, origen y destino, cuando se modifica el valor de cualquiera de los dos (sincrona). Tngase en cuenta que hay situaciones en las que un mismo proceso puede necesitar de los 3 tipos de enlace simultneamente, dependiendo de los campos. Por ejemplo, supuestos 3 datos como Hora de conexin, Datos_WebMaster y Stock_Disponible, podra darse que cada uno de ellos perteneciese a uno de los tipos citados (en ese orden).
pgina
168
El tratamiento de datos
>>
DataContext e ItemSource
Muchos elementos de la interfaz de usuario disponen de dos propiedades especialmente pensadas para el enlace a datos: DataContext e ItemSource. La primera, se utiliza para identificar el origen de datos de un enlace. Puede consistir en datos planos, pero suele asocirsele un objeto Binding, que dispone de medios para obtener los orgenes de datos. Los bloques de texto (en el ejemplo siguiente) y otros controles, presentan esa propiedad. Una ventaja adicional en el uso de DataContext, es que puede asignarse a un contenedor de otros objetos, tanto en lenguaje XAML, como por cdigo. Tpicamente, el enlace suele ser a una clase del CoreCLR. ItemSource, sin embargo, se diferencia de DataContext en que se usa para especificar una coleccin de objetos origen que debe usarse para la lectura del contenido. En realidad, es vlido cualquier objeto que implemente la interfaz IEnumerable. Ejemplo Vamos a crear un nuevo proyecto Silverlight 2 en Visual Studio, llamado LibrosNetalia, y aadiremos una clase de usuario llamada Libro, que representar de forma sencilla una capa de negocio. Ahora bien, queremos que los cambios en la capa de negocio sean reconocidos por la interfaz de usuario. Es decir, que cuando se cambie el valor de la propiedad en la clase que contiene los datos de origen, la interfaz lo refleje automticamente. La interfaz INotifyPropertyChanged Para ello, basta con hacer que nuestro objeto de negocio implemente la interfaz
INotifyPropertyChanged (expuesta por el espacio de nombres System.ComponentModel). Su contrato especifica que debe incluirse un evento del tipo PropertyChangedEventHandler, al que por convencin llamaremos PropertyChanged. Cada vez que se
cambia el valor de la propiedad, el mtodo encargado del cambio notificar a la interfaz de usuario esta circunstancia. En la prctica, de esa tarea se encarga el mtodo Set de la propiedad que lanza el evento PropertyChanged. Implementamos el caso de un nico campo (el Ttulo del libro), que sera el que vemos en el listado 7-1. El modelo seguido para la propiedad Titulo, sera similar para el resto de propiedades de las que la interfaz de usuario deba estar al tanto. Supongamos que en esta primera fase vamos a aadir 2 campos ms para mostrar al usuario: El autor (tipo string) y sus existencias en almacn (tipo int). No
pgina
169
listado 7-1 namespace LibrosNetalia { public class Libro : INotifyPropertyChanged { private string Titulo_Libro; public event PropertyChangedEventHandler PropertyChanged; public string Titulo { get { return Titulo_Libro; } set { Titulo_Libro = value; if ( PropertyChanged != null ) { PropertyChanged(this, new PropertyChangedEventA rgs("Titulo")); } } } } }
lo complicamos ms para mantener el cdigo legible y, por la misma razn, los datos a mostrar estarn empotrados dentro de la clase Page. El siguiente paso a dar es disear una interfaz de usuario sencilla capaz de mostrar los 3 datos de cada libro disponible en almacn. Nos basta con un Grid de un par de columnas y 4 filas que contenga los rtulos descriptivos de cada campo y el valor del campo. Y aadimos al final un botn para permitir el paso de un libro al siguiente. La parte ms interesante del cdigo XAML es la del mecanismo de binding. En el cdigo adjunto, las propiedades variables (los datos a mostrar de cada libro), van enlazadas mediante la sintaxis:
<Elemento Propiedad_a_enlazar= {Binding Nombre_del_Campo} />
Donde Nombre_del_Campo hace referencia a la propiedad pblica de la entidad de negocio que crearemos despus.
170
pgina
El tratamiento de datos
>>
listado 7-2 <UserControl x:Class="Cap7_Datos1.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="450" Height="230"> <Border Background="Navy" CornerRadius="20" BorderBrush="Navy" BorderThickness="5"> <Grid x:Name="LayoutRoot" Background="A liceBlue" Margin="5 5 5 5"> <Grid.RowDefinitions> <RowDefinition MaxHeight="45" /> <RowDefinition MaxHeight="45" /> <RowDefinition MaxHeight="20" /> <RowDefinition MaxHeight="45" /> <RowDefinition MaxHeight="65" /> </Grid.RowDefinitions> <TextBlock Text="Cuadernos Tcnicos de dotNetMana" FontSize="18" FontWeight="Bold" VerticalA lignment="Center" HorizontalA lignment="Center" Grid.Row="0" /> <StackPanel Orientation="Horizontal" Margin="33,0,0,0" Grid.Row="1"> <TextBlock x:Name="tbTitulo" Text="Ttulo: " VerticalA lignment="Center" HorizontalA lignment="Left" Grid.Row="1" /> <TextBlock x:Name="tbTituloLibro" Text="{Binding Titulo, Mode=OneWay }" VerticalA lignment="Center" FontSize="13" FontWeight="Bold" TextWrapping="Wrap" Grid.Row="1" /> </StackPanel> <StackPanel Orientation="Horizontal" Margin="33,0,0,0" Grid.Row="2"> <TextBlock x:Name="tbA utor" Text="A utor: " VerticalA lignment="Center" HorizontalA lignment="Left" /> <TextBlock x:Name="tbA utorLibro" Text="{Binding A utor, Mode=OneWay}" FontWeight="Bold" VerticalA lignment="Center" /> </StackPanel> <TextBlock x:Name="tbExistencias" Text="Existencias:" VerticalA lignment="Center" HorizontalA lignment="Left" Grid.Row="3" Margin="33,0,0,0" /> <TextBox x:Name="txtExistencias" Text="{Binding Existencias, Mode=TwoWay}" VerticalA lignment="Center" HorizontalA lignment="Center" Height="21" Width="190" Grid.Row="3" /> <Button x:Name="Change" Content="Siguiente Libro" Height="30" Width="120" HorizontalA lignment="Center" VerticalA lignment="Center" Grid.Row="4" /> </Grid> </Border> </UserControl>
pgina
171
A continuacin, en la clase de negocio (Libro.cs) aadimos dos propiedades: A utor y Existencias del producto, con sus llamadas a PropertyChanged, para notificar a la interfaz de usuario:
listado 7-3 public class Libro : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string Titulo_Libro; public string Titulo { get { return Titulo_Libro; } set { Titulo_Libro = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventA rgs("Titulo")); } } } private string A utor_Libro; public string A utor { get { return A utor_Libro; } set { A utor_Libro = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventA rgs("A utor")); } } } private int Existencias_Libro; public int Existencias { get { return Existencias_Libro; } set { Existencias_Libro = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventA rgs("Existencias")); } } }
172
pgina
El tratamiento de datos
>>
As conseguimos que se actualicen los datos a visualizar. Finalmente, la clase Page pone en marcha todo y almacena una lista genrica de objetos Libro (3, para simplificar). Cuando pulsamos el botn Siguiente libro, hacemos que la variable LibroA ctivo tome los datos que se quieren mostrar y en ese momento acta el mecanismo de binding. ste es el cdigo completo de la clase Page:
listado 7-4 public partial class Page : UserControl { List<Libro> ListaLibros; Libro LibroEnPantalla = new Libro(); public int LibroA ctivo = 0; public Page() { InitializeComponent(); Loaded += new RoutedEventHandler(Page_Loaded); } void Page_Loaded(object sender, RoutedEventA rgs e) { // Evento para el boton Change.Click += new RoutedEventHandler(Change_Click); // Valores iniciales del Libro ListaLibros = InicializaLista(); LibroEnPantalla = ListaLibros[0]; A signarOrigenDeDatos(); } private List<Libro> InicializaLista() { ListaLibros = new List<Libro>(2); // Inicializa manualmente los valores de 3 libros de la lista. ListaLibros.A dd(new Libro()); ListaLibros[0].A utor = "Pep Lluis Bao"; ListaLibros[0].Titulo = "Robot dispensador para MSDN video"; ListaLibros[0].Existencias = 10; LibroA ctivo++; ListaLibros.A dd(new Libro()); ListaLibros[1].A utor = "Luis Miguel Blanco"; ListaLibros[1].Titulo = "Diseo de Informes con SQL Reporting Services"; ListaLibros[1].Existencias = 30; LibroA ctivo++; ListaLibros.A dd(new Libro()); ListaLibros[2].A utor = "Marino Posadas";
pgina
173
listado 7-4 (Cont.) ListaLibros[2].Titulo = "Programacin segura con .NET Framework"; ListaLibros[2].Existencias = 20; LibroA ctivo = 0; return ListaLibros; } void Change_Click(object sender, RoutedEventA rgs e) { // A vanzamos al siguiente y, al final, volvemos al primero LibroA ctivo++; if (LibroA ctivo > 2) LibroA ctivo = 0; LibroEnPantalla = ListaLibros[LibroA ctivo]; A signarOrigenDeDatos(); } void A signarOrigenDeDatos() { LayoutRoot.DataContext = LibroEnPantalla; } }
De esta forma, se enlaza directamente el grid (LayoutRoot) con el libro activo. Cada vez que se pulsa el botn Siguiente libro, el contador avanza una posicin, se cachean los datos en la variable LibroA ctivo y se asigna el origen de datos. No se necesita nada ms. El resultado es el de la figura 7-1.
figura 7-1
174
pgina
El tratamiento de datos
>>
nota
Ejemplo
Si el usuario tuviera la necesidad de operar con Silverlight mediante la creacin de un servicio WCF tradicional, tenga en cuenta que estos tres aspectos necesitarn de un retoque, si bien funcionan perfectamente, una vez hecho esto.
Supongamos un escenario de acceso: en un servidor de bases de datos SQL Server, tenemos instalada una base de datos de ejemplo, llamada Pedidos08 (disponible en formato Access junto a los ejemplos descargables, en el sitio Web de dotNetMapgina
175
nia), con 5 tablas muy simples. Al crear un nuevo proyecto Silverlight, nos aseguraremos de seleccionar la opcin de una solucin que contiene una aplicacin Web. A continuacin, resolvemos la arquitectura de la aplicacin de servidor, que requerir de dos nuevos tems: la conexin con el servidor de datos y la creacin del servicio WCF usando la plantilla antes citada. El grfico adjunto muestra someramente esta arquitectura:
figura 7-2
Tendremos, pues, dos proyectos: el propio de Silverlight (con una referencia al servicio WCF que expone los datos), y el proyecto Web, que incluye el servicio WCF, la clase que acceder a los datos (usaremos LINQ-to-SQL) y la configuracin necesaria para el funcionamiento. Tenga presente el lector que una de las recomendaciones en la arquitectura de aplicaciones Silverlight es que la lgica de acceso a datos est siempre en el servidor. De ah que la creacin de las clases para acceso a datos y el servicio mismo, estarn ubicados en la aplicacin Web de pruebas. Los pasos a dar en la creacin del proyecto son los siguientes: 1) Creacin del mecanismo de acceso a datos con una clase de negocio. En este proceso utilizamos la plantilla LINQ to SQL Classes. 2) Creacin del servicio WCF que nos dar acceso a los datos: la plantilla Silverlight-Enabled WCF Service.
176
pgina
El tratamiento de datos
>>
3) Modificacin del servicio para acceder a los datos suministrados por la clase de negocio. 4) Creacin de una interfaz de usuario XAML que muestre los datos obtenidos. Comenzamos aadiendo un nuevo tem al sitio Web del tipo LINQ to SQL Classes y seleccionamos un nombre para la clase de acceso a datos (DatosPedidos.dbml, en el ejemplo). El IDE de Visual Studio nos crear una estructura de 3 ficheros relacionados, uno de los cuales es una clase que sirve de proxy para la comunicacin con el servidor. En concreto, este proceso generar la clase DatosPedidosDataContext (clase que hereda de DataContext), ya preparada para su uso, pero todava sin conexin con datos reales. Vinculado con el fichero .dbml, el IDE abre el Diseador de Objetos Relacionales (ver figura 7-3a), donde deberemos de establecer el modelo de datos a usar en el aplicacin. ste es el momento de escoger los orgenes de datos y las tablas a manejar en nuestra pgina (puede utilizar cualquiera de las existentes en SQL Server). Una vez seleccionado el servidor de datos (visible en el Explorador de servidores), podremos arrastrar las tablas que queramos a la ventana del editor y continuar el proceso. Aqu, elegimos solamente la tabla Clientes, de la que vamos a pedir un listado simple.
figura 7-3a y 7-3b Explorador de servidores mostrando orgenes de datos SQL Server y diseo de la clase Cliente en el Diseador de Objetos Relacionales
pgina
177
Dependiendo de lo seleccionado, el diseador nos mostrar una ventana como la de la figura 7-3b, y el generador de clases habr actualizado la clase de negocio con la informacin relativa a la tabla Clientes. En una aplicacin real, tendramos que disear aqu todo el modelo de datos, incluyendo relaciones entre tablas, etc. Hecho esto, se regenera la clase de negocio, que ahora contendr una sub-clase Cliente, con referencias a cada uno de los campos definidos en ella (ver fichero DatosPedidos.Designer.cs). El siguiente paso es la creacin del servicio. En la aplicacin Web, aadimos un tem de la clase Silverlight-enabled WCF Service. Llamaremos a nuestro servicio Servicio_Pedidos. A continuacin, modificamos el servicio para que se adapte a nuestra aplicacin, creando tantas funciones como operaciones queramos que exponga el servicio, y marcando cada una de ellas con el atributo [OperationContract]. Si empezamos con un listado de clientes, podemos utilizar la siguiente sintaxis LINQ para devolver ese listado:
listado 7-5 [OperationContract] public List<Cliente> ListadoClientesMadrid() { DatosPedidosDataContext dp = new DatosPedidosDataContext(); var ListaClientes = from c in dp.Clientes where c.Ciudad == "Madrid" select c; return ListaClientes.ToList(); }
Compilamos el proyecto Web para que Visual Studio actualice todas las referencias. Con esto, tendramos montada la maquinaria necesaria para suministrar datos a la aplicacin Silverlight, o sea, toda la parte de servidor. Ahora tenemos que vincular el servicio desde el cliente y preparar una sencilla interfaz de usuario para mostrar los datos (usaremos un control DataGrid). Lo primero, lo resolvemos con la ayuda del Asistente de descubrimiento de servicios, seleccionando las referencias de servicios sobre la aplicacin Silverlight y pulsando la opcin Aadir Referencia de Servicio. Obtendremos una caja de dilogo como la de la figura 7-4, donde tenemos un espacio para indicar la URL del servicio manualmente, el botn para navegar a esa URL, y la opcin de Descubrir Servicios en la propia solucin (botn Discover en la imagen), que es por lo que optamos aqu.
pgina
178
El tratamiento de datos
>>
El resultado es la creacin de un grupo de ficheros XML de configuracin, adems de uno que hace de proxy entre nuestro control Silverlight y el servicio propiamente dicho: Reference.cs. Esta clase se corresponde con los objetos de datos de la clase LINQto-SQL generada en la aplicacin Web, de forma que tenemos acceso a toda la informacin que se encuentra en aquella como si se tratara de una clase local ms. Y una cosa ms: hemos hecho que el mtodo ListadoClientesMadrid devuelva la lista de clientes utilizando la sintaxis return ListaClientes.ToList();. Con eso conseguimos que, en la parte XAML que vamos a construir a continuacin, el proceso de binding al Datagrid sea directo.
figura 7-4
La parte de cdigo en C# estar prcticamente completada, a efectos del ejemplo. Solo resta el cdigo de la llamada al servicio desde el control Silverlight cuando la pgina Web de prueba se cargue. Para ello, en el cdigo XAML, definimos un manejador del evento Loaded del DataGrid (o del UserControl), donde despus programaremos la llamada, (que ser de tipo asncrono). La interfaz de usuario XAML es muy simple:
pgina
179
Un TextBlock con el ttulo del listado Un DataGrid en el que indicamos que las columnas deben autogenerarse a partir de los datos suministrados en el proceso de binding (propiedad A utoGenerateColumns), y la capacidad de que el usuario cambie el tamao de las columnas (propiedad CanUserResizecolumns). Todo esto, despus de hacer referencia a la librera donde se encuentra el control DataGrid (System.Windows.Controls), o de arrastrarlo directamente a la ventana de cdigo. Este es el XAML resultante:
listado 7-6 <UserControl x:Class="Cap7_2WCF.Page" xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="495" Height="155"> <Grid x:Name="LayoutRoot" Background="Navy" Height="A uto" Width="A uto" > <Grid.RowDefinitions> <RowDefinition Height="20" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Text="Listado de Clientes de Madrid" Foreground="A liceBlue" HorizontalA lignment="Center" VerticalA lignment="Center" Grid.Row="0" /> <data:DataGrid x:Name="RejillaClientes" A lternatingRowBackground="Beige" A utoGenerateColumns="True" Width="A uto" Height="A uto" Grid.Row="1" Grid.Column="1" CanUserResizeColumns="True" Loaded="RejillaClientes_Loaded" BorderBrush="Navy" BorderThickness="3" /> </Grid> </UserControl>
Y, por ltimo, el cdigo de soporte del control Silverlight que hace la llamada (ver listado 7-7). Aqu vemos la llamada asncrona al servicio WCF. Una vez que la carga del control ha finalizado, se instancia un objeto de la clase ServicioPedidosClient (que se encuentra en el espacio de nombres RefDatosPedidos, definido en la ventana de descubrimiento del servicio Web).
pgina
180
El tratamiento de datos
>>
listado 7-7 private void RejillaClientes_Loaded(object sender, RoutedEventA rgs e) { RefDatosPedidos.ServicioPedidosClient ListadoClientes = new Cap7_2WCF.RefDatosPedidos.ServicioPedidosClient(); ListadoClientes.ListadoClientesMadridCompleted += new EventHandler <Cap7_2WCF.RefDatosPedidos.ListadoClientesMadridCompletedEventA rgs> (ListadoClientes_ListadoClientesMadridCompleted); ListadoClientes.ListadoClientesMadridA sync(); } void ListadoClientes_ListadoClientesMadridCompleted(object sender, Cap7_2WCF.RefDatosPedidos.ListadoClientesMadridCompletedEventA rgs e) { RejillaClientes.ItemsSource = e.Result; }
A continuacin, se declara el manejador de evento para el proceso de llamada asncrona, y se llama al servicio. Cuando el proceso concluye, el cdigo pasa por el mtodo ListadoClientes_ListadoClientesMadridCompleted, donde el resultado es recibido a travs del parmetro e, que contiene la lista de clientes en su propiedad Result. Esta vez, aprovechamos las caractersticas de la propiedad ItemsSource que ya comentamos ms arriba. Basta con asignarle los resultados, y tendremos un conjunto de datos similar al de la figura 7-5, que representa la salida de la aplicacin en Internet Explorer:
figura 7-9
pgina
181
Ntese que el proceso genera automticamente las columnas de datos, lo que significa que las va a ordenar alfabticamente si no le indicamos lo contrario. El proceso es bsicamente se. Todas las opciones adicionales en el tratamiento de datos o en la obtencin de resultados sern programadas en el servicio, reinterpretadas en la clase proxy que el IDE crea por nosotros, y utilizadas como sea necesario dentro de la interfaz de usuario. Adems, el DataGrid dispone de muchas otras posibilidades de personalizacin. Podemos permitir que el usuario reubique las columnas (como se hace en este caso), reordenar las filas seleccionando el nombre de la columna, permitir o no la edicin directa en las celdas, y controlar todo el proceso de edicin. Aparte, claro est, de todas las opciones de presentacin y visualizacin que ya hemos visto en Silverlight 2. Tambin es posible editar, y modificar las celdas de forma que incluyan otros objetos embebidos que hagan ms sencilla la edicin, por ejemplo, mostrando un ComboBox de las provincias disponibles al seleccionar una celda de la columna Provincia.
182
El tratamiento de datos
>>
listado 7-8 using System; using System.Text.RegularExpressions; using System.Web.Services; namespace Cap7_3SOA P.Web { /// <summary> /// Un solo mtodo que devuelve la letra del DNI a partir de su nmero /// </summary> [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] public class ServicioDNI : System.Web.Services.WebService { string Letras = "TRWA GMYFPDXBNJZSQVHLCKET"; Regex reNum = new Regex(@"^\d+$"); [WebMethod] public string LetraDNI(string DNI) { bool esNumero = reNum.Match(DNI).Success; if (DNI != "" && esNumero) { DNI += " - " + Letras[Int32.Parse(DNI) % 23]; } else { DNI = "Error en el nmero del DNI"; } return DNI; } } }
En el manejador (sDNI_LetraDNICompleted), recibiremos el resultado (como antes) en la propiedad Result del segundo argumento de evento (e). El cdigo tiene este aspecto, y funciona como cabe esperarse (nada especial para ver en pantalla, por lo que obviamos la salida):
listado 7-9 using using using using System; System.Windows; System.Windows.Controls; Cap7_3SOA P.RefServicioDNI;
pgina
183
listado 7-9 (Cont.) namespace Cap7_3SOA P { public partial class Page : UserControl { RefServicioDNI.ServicioDNISoapClient sDNI = new ServicioDNISoapClient(); public Page() { InitializeComponent(); } private void btnDNI_Click(object sender, RoutedEventA rgs e) { sDNI.LetraDNICompleted += new EventHandler <LetraDNICompletedEventA rgs>(sDNI_LetraDNICompleted); sDNI.LetraDNIA sync(txtDNI.Text); } void sDNI_LetraDNICompleted(object sender, LetraDNICompletedEventA rgs e) { txtDNI.Text = e.Result; } } }
Para una explicacin ms completa del modelo ver (en ingls): http://en.wikipedia.org/wiki/Representa-
tional_State_Transfer
184
pgina
El tratamiento de datos
>>
ceder al contenido del sitio de forma directa. Los dos candidatos que ofrece el CoreCLR de Silverlight para esa accin son WebClient y HttpWebRequest. El primero, soporta los verbos GET y POST, mientras que el segundo permite una aproximacin ms granular al trabajo con REst.
Polticas de seguridad
Pero, el uso de servicios reales, fuera del dominio de la aplicacin y sin presencia de un contrato, supone la existencia de unas polticas de seguridad establecidas en el servidor al que se realiza la peticin. La mejor solucin a este problema, se basa en la presencia de un fichero de servidor llamado ClientA ccessPolicy.xml3. Ah se establecen, al menos, qu tipo de peticiones se admiten, y qu tipo de clientes tiene garantizado el acceso. Un fichero tpico de esta clase ofrece el siguiente aspecto:
listado 7-10 <?xml version="1.0" encoding="utf-8" ?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="*"> <domain uri="*" /> <!-- A qu puede indicarse el filtro de dominios --> </allow-from> <grant-to> <resource path="/" include-subpaths="true" /> <!-- Y aqu, las ubicaciones permitidas para el sitio --> </grant-to> </policy> </cross-domain-access> </access-policy>
El cdigo XML anterior es expuesto por la fuente RSS que vamos a utilizar en nuestro ejemplo (el conocido sitio de blogs Geeks.ms) y cumple con estos requisitos. Se indica que se admiten peticiones desde cualquier origen (<allow-from>), y se permite el acceso a cualquier subdirectorio del sitio (<grant-to>), a partir de la raz. Cual-
185
quier sitio que incluya esa definicin, permite solicitar sus servicios desde Silverlight, y para comprobarlo, accederemos a una fuente RSS/ATOM que devuelve datos en formato XML, relativos a las entradas de sus blogs. Nos basta con instanciar una clase WebClient, para realizar la llamada. El resultado, lo trataremos mediante un objeto XMLReader, y accederemos a sus nodos con la ayuda de la clase SyndicationFeed, presente en System.ServiceModel.Syndication, y especialmente preparada para organizar la informacin de estos conjuntos de datos. El cdigo fuente de la clase quedara de esta forma:
listado 7-11 using using using using using System; System.Net; System.ServiceModel.Syndication; System.Windows.Controls; System.Xml;
namespace Cap7_3SOA P { public partial class RSS : UserControl { WebClient wcRSS = new WebClient(); public RSS() { InitializeComponent(); wcRSS.OpenReadCompleted += new OpenReadCompletedEventHandler(wcRSS_OpenReadCompleted); wcRSS.OpenReadA sync(new Uri("http://geeks.ms/blogs/mainfeed.aspx")); } void wcRSS_OpenReadCompleted(object sender, OpenReadCompletedEventA rgs e) { try { XmlReader xrdr = XmlReader.Create(e.Result); SyndicationFeed feed = SyndicationFeed.Load(xrdr); txtRSSConectado.Text = feed.Title.Text; foreach (SyndicationItem si in feed.Items) { txtRSS.Content += "Fecha: " + si.PublishDate + "\n"; txtRSS.Content += "? " + si.Title.Text + "\n"; } xrdr.Close(); } catch { txtRSS.Content= "Error en acceso al origen RSS"; } } } }
186
pgina
El tratamiento de datos
>>
Cuando la llamada al mtodo OpenReadA sync termina, lo que recibimos en OpenReadCompleted es un stream en formato XML. Tomamos el dato del feed principal para usarlo como ttulo, asignando el valor al TextBlock txtRSSConectado y situamos la salida correspondiente dentro de un ScrollViewer (txtRSS), donde, para cada entrada, vamos aadiendo la fecha y el ttulo de la publicacin. En diseo (trivial, por lo que obviamos el listado) ambos elementos de la interfaz de usuario estarn vacos.
figura 7-6
El lector puede comprobar mediante un punto de ruptura la estructura de datos devuelta, y disear sus propios lectores de informacin RSS con ayuda de las dos clases que hemos utilizado (XmlReader y SyndicationFeed).
187
jetivo es permitir la creacin de servicios de datos flexibles y que se integren en la Web de forma natural. Como consecuencia de ello, ADS utiliza sintaxis URI para referirse a los datos, y formatos sencillos y bien conocidos, tales como JSON y ATOM para representarlos. Esto supone que la arquitectura final se ofrece en un modelo de tipo REst, con el que los gestores de acceso a datos pueden interactuar usando los verbos estndar HTTP (GET, POST, PUT y DELETE), como apuntbamos antes. Al objeto de modelar esos datos expuestos, ADS utiliza un derivado del modelo entidad/relacin llamado Entity Data Model (EDM), que organiza los datos en entidades y establece relaciones entre ellos.
188
pgina
El tratamiento de datos
>>
figura 7-7
Llegados al punto de seleccin de las entidades de datos, marcaremos todas las tablas, vistas y procedimientos almacenados necesarios para el proceso (figura 7-7). Terminamos esa operacin, tras lo cual se genera una clase que representa a todas las entidades presentes en el modelo. Se abre la ventana del Diseador del Modelo de entidades, para hacer el ajuste fino que fuera necesario (figura 7-8). La clase generada hereda de ObjectContext, y de forma similar a los antiguos DataSets, contendr clases que mapearn todas las entidades presentes en el modelo, y ofrecern los servicios necesarios para las operaciones CRUD sobre ese modelo. Lo que resta en la parte del servidor, es generar el servicio que expondr estos datos al mundo exterior. Ah es donde interviene ADO.NET Data Services en lugar de los mecanismos anteriores. Los datos expuestos as, sern accesibles de forma directa desde el navegador utilizando una sintaxis especial tipo URI propuesta por esta tecnologa (sin necesidad de ejecutar la aplicacin).
pgina
189
figura 7-8
Aadimos, por tanto, un nuevo servicio ADO.NET Data Service del apartado Web de las plantillas disponibles, al que llamamos adsPedidos.svc. Este servicio consta de una clase que es el nico punto a retocar a mano en esta fase: la clase adsPedidos, que hereda de DataService y a la que indicamos cul es la entidad con la que queremos operar. Adems, en su mtodo InitializeService, deberemos expresar cules de las entidades disponibles exponemos en este momento, y los permisos de acceso. El cdigo resultante ser el del listado 7-12. Y eso es todo por parte del servidor. Lo novedoso de este modelo es que podemos comprobar si funciona en este mismo momento sin necesidad de la aplicacin cliente, ya que es posible interrogar al servicio utilizando una sintaxis de este tipo desde el navegador:
http://localhost:puerto/Servicio/Entidad?Parmetros
Podemos abrir un navegador y probar algo como http://localhost:1446/adsPedidos.svc/Clientes, obteniendo una respuesta del servicio en texto plano, que cada napgina
190
El tratamiento de datos
>>
listado 7-12 using System.Data.Services; namespace Cap7_A DS.Web { public class adsPedidos : DataService<Pedidos08Entities> { // Este mtodo se llama una sola vez para inicializar polticas de servicio public static void InitializeService(IDataServiceConfiguration config) { config.SetEntitySetA ccessRule("Clientes", EntitySetRights.A llRead); config.SetServiceOperationA ccessRule("*", ServiceOperationRights.A ll); } } }
vegador formatear como crea conveniente, pero que contendr los datos solicitados (podemos comprobarlo viendo el cdigo fuente generado como respuesta). Comprobado el funcionamiento, la parte de cliente resulta trivial, y es similar a las otras que hemos creado. Solo hay que recordar que deberemos seleccionar la llamada asncrona al servicio. Lo primero es hacer la referencia al servicio. Al igual que antes, ser descubierto fcilmente por el asistente. En este caso, lo denominamos ServicioClientes. La interfaz de usuario tambin es sencilla, porque solo nos interesan los datos y su acceso. Un ListBox puede bastar. Ahora bien, debemos de declarar los elementos que van a aparecer en el ListBox dentro de su plantilla, creando un modelo para los ListBoxItem e indicando los enlaces de datos (Binding) adecuados. Una interfaz similar a esta es suficiente para el caso:
listado 7-13 <UserControl xmlns:data= "clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="Cap7_4A DODS.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300"> <Grid x:Name="LayoutRoot" Background="White"> <ListBox x:Name="ListaCtes" Background="Beige" HorizontalA lignment="Stretch" VerticalA lignment="Stretch"
pgina
191
ItemsSource="{Binding Mode=OneWay}" Margin="8,8,8,10" > <ListBox.ItemTemplate> <DataTemplate> <StackPanel x:Name="Listado" Orientation="Horizontal" VerticalA lignment="Bottom" Margin="5" > <TextBlock x:Name="IdCliente" Text="{Binding Path=IdCliente}" Margin="5,0,0,0" VerticalA lignment="Bottom" HorizontalA lignment="Left" FontSize="12"/> <TextBlock x:Name="Empresa" Text="{Binding Path=Empresa}" Margin="5,0,0,0" VerticalA lignment="Bottom" HorizontalA lignment="Left" FontSize="12"/> <TextBlock x:Name="Nombre" Text="{Binding Path=Nombre}" Margin="5,0,0,0" VerticalA lignment="Bottom" HorizontalA lignment="Left" FontSize="12"/> <TextBlock x:Name="Ciudad" Text="{Binding Path=Ciudad}" Margin="5,0,0,0" VerticalA lignment="Bottom" HorizontalA lignment="Left" FontSize="12"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </UserControl>
Como vemos, cada tem del ListBox tiene definida una plantilla de datos con 4 TextBlocks, para los 4 campos de la tabla Clientes. Se establece el binding con el nombre de cada columna y se nombra el ListBox (ListaCtes). El ltimo paso, es hacer la llamada al servicio desde la clase Page. Primero, hacemos referencia al ServicioClientes, generado antes. En el constructor, declaramos el objeto de contexto y le indicamos que su destino es una URI que apunta al servicio. A continuacin, realizamos la llamada asncrona, declarando el mtodo que recoger los datos cuando sta haya concluido. En el mtodo receptor, recuperaremos el objeto de contexto, para que realice la consolidacin de la llamada, y asignamos al ListBox los datos obtenidos. El cdigo fuente completo de la clase Page es el siguiente:
pgina
192
El tratamiento de datos
>>
listado 7-14 using using using using System; System.Data.Services.Client; System.Windows.Controls; Cap7_A DS.ServicioClientes;
namespace Cap7_A DS { public partial class Page : UserControl { public Page() { InitializeComponent(); DataServiceContext ctxCtes = new DataServiceContext( new Uri("adsPedidos.svc", UriKind.Relative)); ctxCtes.BeginExecute<Clientes>(new Uri("/Clientes", UriKind.RelativeOrA bsolute), RellenarClientes, ctxCtes); } private void RellenarClientes(IA syncResult Resultado) { DataServiceContext ctxLlamada = Resultado.A syncState as DataServiceContext; ListaCtes.DataContext = ctxLlamada.EndExecute<Clientes>(Resultado); } } }
figura 7-9
193
nota
5 6
La posibilidad de leer informacin en formato XML no est implementada todava, y en esta versin, al menos la solapa correspondiente se encuentra deshabilitada, desde aplicaciones Silverlight. Si se quiere acceder a datos en formato XML se debe utilizar la tcnica usada para los servicios REst un objeto WebRequest, junto a un XmlReader o StreamReader, y si se necesita cargar recursos de los paquetes XAP en ese formato, el CoreCLR suministra la clase XmlXapResolver, con ese propsito5.
Supongamos que queremos realizar la vinculacin mediante Blend 2.0 en el ejemplo del visor de datos de libros. Como se trata de un conjunto manejable de informacin (9 tems6), decidimos ubicar los datos en una clase de negocio separada, de forma que al instanciarla, suministre todos los mtodos de acceso a datos. Podemos reutilizar la clase Libro para que albergue la informacin correspondiente a esos 3 campos y permita el acceso con propiedades pblicas. Esta clase, ser manejada por otra que har las veces de gestor (GestorLibros), que se encargar de devolver
Puede ver un ejemplo de esta tcnica en MSDN: http://msdn.microsoft.com/en-us/library/cc645034(VS.95).aspx Los publicados por .netalia, hasta el momento.
194
pgina
El tratamiento de datos
>>
una lista de libros con toda la informacin. Resumiendo: una clase con 3 campos que representa la entidad de negocio y una clase manejadora con un mtodo que devuelve la lista de objetos Libro. El aspecto del cdigo C# de esa clase sera el siguiente:
listado 7-15 public class GestorLibros : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; ObservableCollection<Libro> _Lista; public ObservableCollection<Libro> ListaDeLibros //Expone los datos { get { if (_Lista.Count < 1) ObtenerLibros(); return _Lista; } set { _Lista = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventA rgs("ListaDeLibros")); } } // Constructor personalizado public GestorLibros() { _Lista = new ObservableCollection<Libro>(); if (HtmlPage.IsEnabled == false) { ObtenerLibros(); } } public void ObtenerLibros() { _Lista = new ObservableCollection<Libro>(); // Inicializa manualmente los valores de 3 libros de la _Lista. _Lista.A dd(new Libro()); _Lista[0].A utor = "Pep Lluis Bao"; _Lista[0].Titulo = "Robot dispensador para MSDN video"; _Lista[0].Existencias = 10; _Lista.A dd(new Libro()); _Lista[1].A utor = "Luis Miguel Blanco"; _Lista[1].Titulo = "Diseo de Informes con SQL Reporting Services";
pgina
195
listado 7-15 (Cont.) _Lista[1].Existencias = 30; _Lista.A dd(new Libro()); _Lista[2].A utor = "Marino Posadas"; _Lista[2].Titulo = "Programacin segura con .NET Framework"; _Lista[2].Existencias = 20; _Lista.A dd(new Libro()); _Lista[3].A utor = "Pepe Hevia"; _Lista[3].Titulo = "Microsoft SharePoint Products & Technologies"; _Lista[3].Existencias = 200; _Lista.A dd(new Libro()); _Lista[4].A utor = "Braulio Daz y Reyes Garca"; _Lista[4].Titulo = "Optimizacin de A SP.NET 2.0"; _Lista[4].Existencias = 50; _Lista.A dd(new Libro()); _Lista[5].A utor = "Gustavo Vlez"; _Lista[5].Titulo = "Programacin con SharePoint 2007"; _Lista[5].Existencias = 0; _Lista.A dd(new Libro()); _Lista[6].A utor = "Miguel Katrib, et. al."; _Lista[6].Titulo = "Windows Presentation Foundation"; _Lista[6].Existencias = 70; _Lista.A dd(new Libro()); _Lista[7].A utor = "Grupo Weboo"; _Lista[7].Titulo = "Visual Studio 2008"; _Lista[7].Existencias = 120; _Lista.A dd(new Libro()); _Lista[8].A utor = "Marino Posadas"; _Lista[8].Titulo = "Programacin con Silverlight 2.0"; _Lista[8].Existencias = 5; } } public class Libro : INotifyPropertyChanged // (Ver implementacin en el apartado "DataBinding en Silverlight 2" al principio de // este captulo)
196
pgina
El tratamiento de datos
>>
Una vez codificada la clase de negocio, podemos volver a Blend para concluir la programacin y realizar en enlace a datos adecuado. En el cdigo anterior se ve cmo GestorLibros da acceso general a los mecanismos de construccin de datos, y Libro, representa cada libro individual. El enlace a datos desde Blend puede realizarse para ambas clases y la herramienta reconoce cualquier objeto de este tipo (o del runtime).
figura 7-10
Una vez ah, seleccionamos nuestra clase GestorLibros, disponible en el rbol de la aplicacin, y aceptamos. Veremos que el objeto pasa a formar parte de la lista
pgina
197
de elementos accesibles desde nuestra interfaz de usuario. Lo que sucede es que se han generado automticamente varias referencias en el cdigo XAML: la declaracin del control de usuario incluye ahora nuevos espacios de nombres y se ha aadido al control la referencia a nuestra clase de negocio. Los cambios aparecen en el fragmento de cdigo siguiente:
listado 7-16 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:SL_Desarrolladores1="clr-namespace:SL_Desarrolladores1" x:Class="SL_Desarrolladores1.Page" Width="440" Height="480" mc:Ignorable="d" > <UserControl.Resources> <SL_Desarrolladores1:GestorLibros x:Key="GestorLibrosDS" d:IsDataSource="True"/> </UserControl.Resources>
Los elementos expuestos por la clase ya estn disponibles para procesos de binding desde los objetos de la interfaz de usuario. De hecho, si desplegamos el objeto GestorLibros en la Ventana de datos, veremos que ofrece un array de Libros que se expone como una ObservableCollection.
nota
Una coleccin de este tipo es una novedad aportada por Silverlight para que los objetos que se utilicen en el enlace a datos sean capaces de notificar a la interfaz de usuario cuando ha sucedido un cambio en su contenido.
figura 7-11
198
pgina
El tratamiento de datos
>>
Ahora, podemos optar por una opcin bien sencilla: arrastrar la coleccin de elementos que nos interesa mostrar sobre un control de la interfaz de usuario, al estilo de lo que hacamos en algunos escenarios de Windows Forms. Si lo hacemos as, veremos que se nos ofrece un men contextual de enlace con dos categoras (eso depende siempre del tipo de datos a enlazar y del control que va a servir de destino del enlace):
figura 7-12
Podemos escoger la opcin predeterminada (ListaDeLibros to ListBox) o seleccionar otro control adecuado. El men contextual tiene dos espacios: el predeterminado (Bind to Existing Control) y otras posibles opciones que pueden tener sentido en este proceso (Select a control to). Debido a que no hemos indicado qu elemento de los expuestos por la clase Libro nos interesa mostrar, en la Ventana de diseo solo aparece el nombre del objeto dentro del control que lo contiene, pero ha detectado el nmero de objetos disponibles en la coleccin (los 9 tems), con lo que muestra una interfaz de usuario como la de la figura 13 (si ejecutamos la aplicacin la salida por pantalla sera idntica):
figura 7-13
Lo que tenemos que hacer a continuacin es crear una plantilla para que el control ListBox muestre correctamente la informacin que nos interesa de cada libro.
pgina
199
Para ello, en la opcin de men Object -> Edit Other Templates -> Edit Item Template -> Create Empty, creamos una nueva plantilla para estos datos. Nos aparecer una caja de dilogo titulada Crear plantilla de recurso de datos (Create DataTemplate Resource), donde podemos dar un nombre a la plantilla y especificar si queremos que ese recurso sea parte de toda la aplicacin (para ser utilizado por otros controles) o preferimos mantenerlo como un recurso restringido al control que estamos construyendo. Optamos por mantenerlo restringido a este control, como se ve en la figura 7-14:
figura 7-14
En ese momento, la Ventana Objects & Timeline cambia, y se nos presenta como si estuviramos editando un tem completamente nuevo. Nos genera un elemento ItemTemplate que contiene un Grid vacio y a partir de ah podemos empezar a definir los contenidos. Con ese ItemTemplate seleccionado, es momento de construir la interfaz de usuario de cada elemento enlazado al ListBox. Por ejemplo, podemos usar un StackPanel con direccin horizontal, e incluir en cada uno de ellos un TextBlock que represente a los 3 campos disponibles en la clase de negocio. Una vez codificada esta parte, se supone que el cdigo XAML completo de la aplicacin (eliminadas las imgenes), ser similar al siguiente:
pgina
200
El tratamiento de datos
>>
listado 7-17 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:SL_Desarrolladores1="clr-namespace:SL_Desarrolladores1" x:Class="SL_Desarrolladores1.Page" Width="440" Height="480" mc:Ignorable="d" > <UserControl.Resources> <SL_Desarrolladores1:GestorLibros x:Key="GestorLibrosDS" d:IsDataSource="True"/> <DataTemplate x:Key="Plant_Libros"> <Grid> <StackPanel x:Name="StackContenedor" Orientation="Horizontal" Width="400"> <TextBlock Height="A uto" Width="220" TextWrapping="Wrap" x:Name="Titulo" HorizontalA lignment="Left" VerticalA lignment="Bottom" Text="{Binding Mode=OneWay, Path=Titulo, Source={StaticResource LibroDS}}" FontFamily="Comic Sans MS" FontWeight="Bold" FontSize="14"/> <TextBlock Height="A uto" Width="129" TextWrapping="Wrap" Text="{Binding Mode=OneWay, Path=A utor, Source={StaticResource LibroDS}}" x:Name="A utor"/> <TextBlock Height="A uto" Width="30" TextWrapping="NoWrap" Text="{Binding Mode=OneWay, Path=Existencias, Source={StaticResource LibroDS}}" HorizontalA lignment="Right" TextA lignment="Right" FontWeight="Normal" x:Name="Existencias" Foreground="#FF1A 46D3"/> </StackPanel> </Grid> </DataTemplate> <SL_Desarrolladores1:Libro x:Key="LibroDS" d:IsDataSource="True"/> </UserControl.Resources> <Grid x:Name="LayoutRoot" > <Grid.ColumnDefinitions> <ColumnDefinition Width="496"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="83.179"/> <RowDefinition Height="*"/> <RowDefinition Height="0*"/> </Grid.RowDefinitions>
pgina
201
listado 7-17 (Cont.) <Border HorizontalA lignment="Stretch" Margin="32,4,104,4" x:Name="Borde" Grid.ColumnSpan="1" Background="#FF4C2F88" BorderBrush="#FF6A B6B9" BorderThickness="2,2,2,2" CornerRadius="20,20,20,20" VerticalA lignment="Stretch"/> <TextBlock HorizontalA lignment="Stretch" Margin="48,24,104,22.2999992370605" x:Name="Titulo" VerticalA lignment="Stretch" Grid.ColumnSpan="1" Grid.RowSpan="1" FontFamily="Lucida Sans Unicode" FontWeight="Bold" Foreground="#FFF0E2E2" Text="Visor de datos de libros" TextWrapping="Wrap" d:LayoutOverrides="Height, GridBox" TextA lignment="Center" FontSize="24" /> <ListBox Margin="16,15,72,23.8570003509521" VerticalA lignment="Stretch" Grid.Row="1" HorizontalA lignment="Stretch" ItemsSource="{Binding Mode=OneWay, Path=ListaDeLibros, Source={StaticResource GestorLibrosDS}}" ItemTemplate="{StaticResource Plant_Libros}"/> </Grid> </UserControl>
O sea, dentro de los recursos del control de usuario se hace una referencia al objeto de negocio y se crea una plantilla de datos (DataTemplate) que enlaza directamente con la clase que expone la informacin (GestorDatos). Los bloques de texto destinados a mostrar datos vinculan su propiedad Text con el recurso concreto (que aqu se llama LibroDS), y dentro de l, seleccionan (atributo Path), el campo a mostrar. En ese momento, podemos ejecutar la aplicacin o refrescar los datos. El editor visual de Blend nos ofrecer el aspecto final de la aplicacin, que ser similar al de la figura 7-15. Por tanto, Blend 2.0 permite a los diseadores trabajar con clases de negocio y tener una aproximacin real al aspecto que ofrecern los datos en pantalla, favoreciendo el trabajo conjunto con los desarrolladores al aportar su visin de la aplicacin con un punto de vista ms real que en solo diseo. Y en el caso de confluir ambas tareas en una persona, el manejo de Blend con clases de acceso a datos, puede darnos una idea de qu informacin merece ser resaltada o tratada en forma especial.
202
pgina
El tratamiento de datos
>>
figura 7-15
nota
Para ms opciones de manipulacin del DataGrid en Silverlight no cubiertas en esta obra vase el blog de Scott Morrison (co-autor del control) en http://blogs.msdn.com/scmorris y en castellano, el blog El aprendiz de brujo (http://geeks.ms/blogs/lmblanco) de Luis Miguel Blanco.
pgina
203
captulo
Marco tecnolgico
Podemos pensar en 3 aproximaciones distintas para la utilizacin de Silverlight en sitios Web: 1) Inclusin de islas Silverlight. Normalmente, reduce la utilizacin de Silverlight a elementos puntuales, como anuncios publicitarios o complementos visuales de pginas para enriquecer la experiencia de usuario. Se trabaja con elementos Silverlight individuales vinculados a etiquetas <div> en la(s) pgina(s) correspondientes. Se pueden utilizar tcnicas AJAX para que la carga se produzca de forma asncrona y dinmica. En general, no es la mejor aproximacin para la construccin de aplicaciones RIA.
pgina
205
2) La pgina entera es un control de usuario nico. El usuario crea su propio sistema de navegacin, dependiendo de los patrones utilizados, y se va cargando y descargando el control principal con las pginas individuales. Ideal para aplicaciones RIA estndar y/o de medio volumen. 3) Si se trata de aplicaciones complejas, se recomienda la utilizacin de marcos de desarrollo, como PRISM V.2 (conjunto de patrones para aplicaciones Silverlight 2, disponible en Patterns & Practices). PRISM se ha creado desde cero para WPF y Silverlight 2, no es una migracin. El modelo aparece representado en la figura 8-1:
figura 8-1
Modelo arquitectnico propuesto por PRISM v.2 para aplicaciones Silverlight 2.0
Para ms informacin sobre el estado de PRISM v2.0, descargas y documentacin, visitar el sitio de CodePlex http://www.codeplex.com/CompositeWPF, as como el blog de Blaine Wastell ( http://blogs.msdn.com/blaine/archive/2008/09/02/scope-for-prism-2-0.aspx).
pgina
206
>>
No obstante, e independientemente del marco elegido, hay un montn de buenas prcticas aplicables en la construccin de una aplicacin Silverlight. Algunas de ellas las comentamos a continuacin.
Capas de presentacin Se recomienda tener presentes los modelos de presentacin actuales: Modelo-Vista-Controlador (M-V-C), Modelo-Vista-Presentacin (M-V-P), y Modelo-VistaViewModel (M-V-VM). El patrn MVP se presta mejora para las aplicaciones RIA, donde, si la salida es muy compleja, se pueden implementar mltiples presentadores. Controles Ya hemos visto los controles que incorpora Silverlight de fbrica y tambin comentbamos las ltimas extensiones de controles y herramientas disponibles a travs de CodePlex. Todos ellos son modificables y adaptables por el usuario y pueden servir igual1
Muchas de estas ideas estn tomadas de los blogs de Csar de la Torre (http://blogs.msdn.com/cesardelatorre), Josh Holmes (http://joshholmes.com), Scott Guthrie (http://weblogs.asp.net/scottgu) y Michael Sync (http://michaelsync.net).
pgina
207
mente de base para la construccin de controles ms complejos. No obstante, no debemos olvidar la ya abundante oferta de terceros: ComponentOne, Infragistics, Xceed, Telerik, DevExpress y otros anuncian soluciones profesionales y hay muchos otros gratuitos como proyectos de cdigo comunitario tanto en CodePlex, como en sitios similares. Media y grficos Hay varias recomendaciones respecto a esto, y ser una decisin individual dependiendo de las necesidades de cada aplicacin. Pero el uso del streaming adaptativo, para la descarga de vdeos en el navegador es una de las prcticas recomendadas para solucionar problemas de ancho de banda. De igual forma, se fomenta la carga de imgenes y vdeos de forma asncrona desde fuera del fichero XAP. La parte grfica que no pueda ser resuelta de forma vectorial (o sea, si se requiere de imgenes que tienen que ser incluidas en el XAP por razones de presentacin inicial), la sugerencia es utilizar el formato PNG, dado que, adems de ser un estndar oficial W3C, su relacin entre tamao y calidad es de las mejores (y recuerde que el formato GIF no est soportado)2. Navegacin El consejo en este punto es claro: las aplicaciones RIA ofrecen una mejor experiencia de usuario cuando hay una sola interfaz central, basada ntegramente en Silverlight. Esto presenta potenciales problemas de control del flujo (interacciones del usuario navegando hacia atrs-adelante, etc.). Por ello se recomienda capturar y anular estos botones del navegador para limitar la navegacin a nuestro sistema Silverlight. Una opcin adicional es interceptar la URI y gestionar manualmente el histrico del navegador (por ejemplo, usando el objeto History de AJAX). El cdigo siguiente, basado en unas ideas de Josh Holmes, ilustra este punto. De igual forma, el proceso inicial siempre es importante de cara a la experiencia de usuario. Se debiera de mostrar una metfora de progreso segn se est produciendo la carga del control, y que esta sea lo suficientemente visible y fiable (evitemos el mal gusto de pasarse la mitad del tiempo en el 99%, haciendo pruebas de ancho de banda).
No obstante, Joe Stegman, dispone en su blog (http://blogs.msdn.com/jstegman) de interesantes aportaciones para la conversin automtica a otros formatos.
2
208
pgina
>>
listado 8-1 private void A pplication_Startup(object sender, StartupEventA rgs e) { string startPageParameter = "/PaginaInicial"; if (!e.InitParams.ContainsKey(paramPagInicial)) { this.RootVisual = new PaginaInicialPredeterminada(); } Else { switch (e.InitParams[paramPagInicial]) { case "PaginaInicialPredeterminada": this.RootVisual = new PaginaInicialPredeterminada(); break; case "PaginaInicialNoPredeterminada": this.RootVisual = new PaginaInicialNoPredeterminada(); break; default: throw new Exception("/PaginaInicial debe ser " + "'PaginaInicialPredeterminada' o " + "'PaginaInicialNoPredeterminada'."); } } }
Gestin de estados Si, por la razn que sea, se necesita guardar el estado de la aplicacin, distinga primero con claridad qu estado: el del cliente o el del servidor. Si es el del cliente, lo recomendable es el uso de Almacenamiento aislado (Isolated Storage), para mantener el estado entre sesiones, comprobando siempre la cuota de disco antes de grabar. Se puede sincronizar con el servidor cada cierto tiempo o al final de la sesin. Si se trata de guardarlo en el servidor, mejor que suceda solo cuando sea imprescindible, y usando un mecanismo seguro, como una base de datos. Validacin de datos de entrada Silverlight 2.0 no presenta un modelo interno de validacin, por lo que sta debe hacerse en el cliente o va servicios Web. Las recomendaciones son: Identificar claramente las fronteras entre capas y validar solamente aquellos datos que las crucen. Si la validacin se hace en cliente, el cdigo, mejor separado (incluso de las clases de negocio).
pgina
209
Recuerde los consejos generales de cargas parciales cuando el tratamiento es voluminoso, no muestre por pantalla las entradas no fiables y mantenga la validacin en servidor solamente para los datos crticos. Cachs El cache del navegador ser idneo para los datos que no cambian durante la sesin. El almacenamiento aislado no debe usarse para objetos BLOB (objetos binarios grandes, como grficos pesados o vdeos), y debemos comprobar la cuota antes de escribir, adems de llevar un control de errores. Accesibilidad Mientras que en la versin 1.0 apenas estaba presente, aqu la situacin ha cambiado de forma radical. Por un lado, cuanta ms presencia de informacin vectorial, mejor, ya que puede escalarse cuanto sea necesario sin prdida de calidad. Por otro lado, la versin 2.0 presenta las siguientes novedades: Soporte completo de teclado (foco y entradas, adems de control de Tabs). Inclusin de informacin de accesibilidad en los elementos XAML, a travs de las A utomationProperties (propiedades Name y Help) y del llamado A utomationPeer. Soporte de accesibilidad a controles personalizados. Soporte de lectores de pantalla y exposicin del rbol de automatizacin completo. Notificaciones para requisitos de alto contraste. Soporte de formatos de audio comprimido de calidad (ver captulo 1). Si necesitamos ver el rbol de accesibilidad para nuestras aplicaciones, recomendamos desde aqu la herramienta UI Spy3 (similar al XML-Spy, pero especial para Silverlight). Comunicaciones entre capas En lo tocante a llamadas a servicios Web de distintas clases, recuerde las consideraciones respecto a sincrona, hechas en el captulo anterior. Si an as necesita que algn proceso sea sncrono, utilice otros hilos de ejecucin, preferible-
http://msdn2.microsoft.com/en-us/library/ms727247.aspx
210
pgina
>>
mente basados en la clase BackgroundWorker (sino, el navegador se bloquea durante el proceso). Si se necesitan comunicaciones en ambos sentidos en tiempo real con el servidor, considere la utilizacin de servicios WCF FullDuplex, o el uso de sockets, que es mucho ms escalable. Seguridad Hablando en general, la seguridad en Silverlight es mayor que en otros tipos de aplicaciones debido a lo que comentamos al comienzo de esta obra sobre la ejecucin en SandBox. Recuerde tambin la explicacin del modelo de seguridad para la ejecucin de cdigo del captulo 1, dentro del apartado dedicado al CoreCLR. La Autenticacin y la Autorizacin pueden seguir los patrones estndar de las aplicaciones Web tradicionales, segn lo comentado en el captulo 7 sobre los ficheros de configuracin para acceso entre dominios. Por lo dems, en un escenario de Internet, lo mejor es usar autenticacin Forms, y autorizacin basada en roles, como en ASP.NET. Si estamos en una Intranet, la autenticacin Windows y los roles de Windows, pueden ser adecuados, aunque debe tenerse presente que no funciona para usuarios de otros sistemas operativos. Por ltimo, respecto al cifrado de las comunicaciones, considere como forma genrica el uso de SSL sobre HTTPS y recuerde que para otros propsitos el CoreCLR soporta un subconjunto de la oferta criptogrfica de .NET en los espacios de nombres System.Security.Cryptography y System.Messaging, especialmente si se tienen que guardar datos sensibles en la zona de almacenamiento aislado. Respecto al uso de cookies, las restricciones son por la vinculacin a un mismo nivel: de dominio, de protocolo y de puerto. Acceso a datos En el captulo anterior hemos visto ya algunas recomendaciones en funcin del escenario de acceso. No obstante, recordemos tres puntos en este apartado tan crtico: Reglas de negocio: como norma general, ya se ha indicado que la lgica de negocio debe residir en el servidor. Solo interesa mover al cliente aquellos aspectos imprescindibles para mejorar el rendimiento de casos especiales de uso, o por razones de reutilizacin. Y nunca dividir demasiado la lgica entre servidor y cliente. Lo que tenga que ir en el cliente, mejor situarlo en ensamblados separados para la carga dinmica y que nunca sean ensamblados crticos.
pgina
211
El uso del cach del cliente puede mejorar la situacin en algunas circunstancias. Los filtros de datos, siempre en el servidor, para reducir el trfico. Excepciones y depuracin Debemos disear una estrategia de tratamiento de excepciones: las de tipo sncrono, tratarlas con bloques try/catch, y las de tipo asncrono, mediante el evento OnError de Silverlight. Adems, recuerde no utilizar la lgica de excepciones para controlar el flujo del programa y determinar claramente cundo se notifica al servidor las excepciones producidas en el cliente. En cuanto a la depuracin, tenga presentes las capacidades de Visual Studio 2008, incluyendo la de depurar distintos hilos de ejecucin. Esto resulta especialmente til cuando se estn utilizando objetos del tipo BackgroundWorker y similares. Adems, Silverlight permite el uso y almacenamiento de trazas en ficheros, pero esto funciona por usuario. Podemos tras una planificacin transferir esos ficheros al servidor con determinada frecuencia para un tratamiento global, pero sin olvidar las restricciones que tiene el uso del almacenamiento aislado. En cuanto a la monitorizacin de uso de la aplicacin, recomendamos un vistazo a un Building Block para Silverlight, disponible en CodeProject, llamado CLOG: http://www.codeproject.com/KB/silverlight/SilverlightLogging.aspx.
212
>>
La experiencia de instalacin La idea es evitar el comportamiento predeterminado cuando Silverlight no se encuentra en la mquina, sustituyendo el espacio vaco del control por una imagen representativa de lo que se puede esperar tras su descarga y puesta en funcionamiento. Se distinguen, pues, dos tipos de experiencias de usuario: la predeterminada (cuando la versin del complemento instalado es suficiente para ver la pgina) y la de instalacin (cuando no es as). Para conseguir una experiencia de instalacin adecuada, debemos: Disear la experiencia predeterminada. En base a ella, disear la experiencia de instalacin. Enlazar ambas experiencias. Para el diseo de la experiencia predeterminada, seleccionamos una imagen que sea descriptiva de aquello que podr ver el usuario una vez instalado el complemento (puede servir con una captura de pantalla) y tener claramente separado lo que es el control Silverlight, de lo que le rodea. Puede verse en la siguiente imagen, que he incorporado a mi sitio (www.elavefenix.net) en el apartado de fotos:
figura 8-2
Conviene igualmente, planificar redistribuciones cuando hay una instancia en ejecucin y llevar un control de las versiones de los componentes. El atributo MinimumVersion del elemento <asp:Silverlight> nos permite un control ms fino de las versiones.
pgina
213
Por ltimo, no olvide comprobar la ejecucin en los navegadores ms populares. Esto incluye, al menos, tres: IE, Firefox y Safari, aunque puede que segn lo anunciado dentro de poco tambin haya soporte por parte de Operan y Google Chrome. No debiera de haber grandes diferencias, pero en este tipo de aplicaciones algn pequeo cambio puede estropear la interfaz de usuario. Por otro lado, es de esperar la aparicin de actualizaciones de los navegadores para solucionar la compatibilidad con el complemento. En principio, la versin final de Silverlight 2 es totalmente compatible con la 1.0.
214
pgina
>>
Ahora bien, dependiendo del escenario, puede interesarnos empotrar los recursos ms imprescindibles o aquellos que pensemos que pueden resultar de difcil acceso para el usuario final, dentro de la propia aplicacin (ya sea en la DLL o en el fichero XAP que vamos a distribuir), y no hay limitacin al tipo de fichero que se puede incluir en el paquete XAP: imgenes, vdeo, iconos, ficheros de texto, HTML, etc.
figura 8-3
Una vez hecho esto, desde el cdigo, utilizaremos la clase StreamResourceInfo, para el acceso al recurso. En este caso, al tratarse de una imagen, crearemos un Bitmap previo a la asignacin de ste a un control:
listado 8-3 using System.Windows.Resources; // para StreamResourceInfo using System.Windows.Media.Imaging; // para BitmapImage //y en el evento adecuado StreamResourceInfo sr = A pplication.GetResourceStream( new Uri("SilverlightA pplication1;component/Foto.png", UriKind.Relative)); BitmapImage bmp = new BitmapImage(); bmp.SetSource(sr.Stream); ControlImagen.Source = bmp
pgina
215
Si la imagen se encontrase guardada en una subcarpeta le indicaramos la situaal objeto StreamResourceInfo con una sintaxis del tipo component/Imgenes/Foto.png). En el caso de que prefiramos embeberlo en el propio fichero XAP de distribucin, sustituiremos la opcin de compilacin anterior por Content (en lugar de Resource, en la Ventana de propiedades). El cdigo fuente sera prcticamente el mismo, salvo en la cadena de creacin de la URI: cin
listado 8-4 StreamResourceInfo sr = A pplication.GetResourceStream( new Uri("Foto.png", UriKind.Relative)); BitmapImage bmp = new BitmapImage(); bmp.SetSource(sr.Stream);
E igual que antes, si estuviese en un directorio se lo indicaramos, pero, ahora sin la barra separadora inicial: Imgenes/Foto.png.
216
pgina
>>
Esto da una gran libertad, ya que muchas fuentes no son voluminosas (en este caso, ocupan unos 40 Kb entre ambas) y se puede conseguir una gran personalizacin de la pgina (figura 8-4).
figura 8-4
Fuentes embebidas
Hay que constatar que la versin final de Silverlight 2 ha supuesto una considerable mejora en la calidad visual de la interpretacin de las fuentes.
MIME Type application/xaml+xml application/x-silverlight-app Tabla 1: Tipos MIME introducidos por Silverlight
217
La forma de aadir las extensiones a un servidor, vara de servidor en servidor, pero existen enlaces de Internet explicativos de esta mecnica para los servidores ms populares (ver notas al pie5,6,7 y 8). En caso de que se desee conseguir un soporte completo de los nuevos tipos propuestos por las aplicaciones ClickOnce y WPF en un servidor, tambin sera necesario aadir los siguientes:
Extension
.manifest .application .xbap .deploy .xps
Con esto, tendramos configurado el servidor para las peticiones originadas en estas aplicaciones. No obstante, hay ocasiones en las que no disponemos de permisos de modificacin del servidor donde tenemos alojada la aplicacin. La ventaja, es que la extensin problemtica (en este caso, .xap), est basada en ZIP (como ocurre con los documentos Office 2007). Por tanto, basta con renombrar la extensin .xap a .zip y hacer que el parmetro de la etiqueta HTML que carga el control Silverlight (<object> o <asp:Silvelright>) apunte a la nueva ubicacin del fichero. Por ejemplo, si tenemos un fichero Silverlight llamado originalmente ContenidoSL.xap, basta con cambiar la extensin a ContenidoSL.zip, y podramos aadir en el cdigo fuente una secuencia equivalente a esta (listado 8-6). Donde, el mime-type que define la etiqueta <object>, es, en este caso, application/x-silverlight-2, o sea, la ltima versin sobre la que estamos trabajando.
5 6 7
Para Apache 2.0: http://httpd.apache.org/docs/2.0/mod/mod_mime.html#addtype Para Sun Java System Web Server: http://docs.sun.com/app/docs/doc/819-2630/abumi?a=view Para Internet Information Server 7: http://technet2.microsoft.com/windowsserver2008/en/library/2761b1cfcb53-40b2-80a1-f0c97030d7d61033.mspx?mfr=true
218
pgina
>>
listado 8-4 <div id="silverlightControlHost"> <object data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%"> <param name="source" value="ContenidoSL.zip"/> </object> </div>
Conclusin
El material cubierto en esta obra, no ha sido ms que un viaje inicial por la geografa del producto. Hemos tratado de cubrir todos los aspectos importantes de Silverlight 2.0, con la fortuna de haber podido revisarlo todo y hacer las correcciones y aadidos necesarios que introduca la versin final, respecto a las betas anteriores. Atenindonos al formato reducido de esta serie de Cuadernos Tcnicos de dotNetMana no haba espacio para mucho ms, pero espero que el lector encuentre en este texto un fundamento suficiente que le permita comenzar a practicar y despus profundizar en Silverlight 2. Para ms informacin, consulte el apndice Bibliografa e enformacin on-line.
Ver http://blogs.msdn.com/tims/archive/2008/03/18/configuring-a-web-server-to-host-silverlightcontent.aspx
pgina
219
aplndice
Libros
Shared Source CLI, David Stutz , Ted Neward y Geoff Shilling . O'Reilly Editorial, 2003 Silverlight 1.0 Unleashed, Adam Nathan, SAMS, 2007 Artculos Programming Silverlight with the CoreCLR, Andrew Pardoe, MSDN Magazine, Agosto 2008 Create Animations with XAML and Expression Blend, Lawrence Moroney, MSDN Magazine, Agosto 2008 Wicked Code: Craft Custom Controls for Silverlight 2, Jeff Prosise, MSDN Magazine, Agosto 2008 Service Driven Apps with Silverlight 2 and WCF, John Papa, MSDN Magazine, Septiembre 2008
221
222
pgina
El propsito de este libro es suministrar al lector los conocimientos necesarios para empezar a construir aplicaciones con la versin 2.0 de Silverlight, abordando para ello todos los procesos fundamentales: la eleccin del entorno de trabajo, el anlisis de la arquitectura y los modelos de desarrollo, la construccin de interfaces de usuario (desde Visual Studio 2008 y tambin desde Expression Blend 2.0 SP1), el acceso a la informacin del sistema y a servicios Web para la lectura/escritura de datos (en sus diferentes opciones) y los mecanismos de instalacin y puesta a punto de las aplicaciones finales. Los Cuadernos Tcnicos de dotNetMana son una serie de pequeos libros enfocados a temas concretos para programadores y arquitectos de software de la plataforma .NET. Cubren el hueco existente entre artculos muy especficos en una revista especializada como dotNetMana o los grandes libros sobre temas genricos.
Marino Posadas es Redactor Jefe de dotNetMana. Trabaja como Consultor y Program Manager en Alhambra-Eidos y es Microsoft Most Valuable Professional en Visual C#, adems de titulado MCSD, MCAD, MCT y MSFP. Antes de esta obra, ha colaborado con varios compaeros de trabajo en la elaboracin de otros cuatro ttulos, y esta es su tercera obra en solitario. Conferenciante en diversos eventos organizados por Alhambra-Eidos, Microsoft y varias universidades espaolas y extranjeras, se interes por .NET Framework desde las primeras versiones alfa y mantiene una Web de soporte de sus actividades en el sitio www.ElAveFenix.net
Patrocinador