Introducción a las aplicaciones universales Windows en Windows 8.1 y Windows Phone 8.1
05 Agosto 2014


Por Pavel Yosifovich, MVP Microsoft

En los siguientes días vamos a presentarles textos de algunos miembros de nuestro programa MVP de desarrolladores Microsoft. Los MVP de Microsoft son líderes expertos y apasionados de la comunidad que comparten su conocimiento y experiencia del mundo real en construir soluciones con plataforma Microsoft.

Pavel Yosifovich, CTO en CodeValue; Can Bilgin, Arquitecto Senior de Soluciones en CompuSight Corp.; y Rob Miles, profesor de universidad, han tenido experiencia en la implementación de aplicaciones universales de Windows, por lo que pensamos que sería bastante útil mostrar las cosas que han aprendido y cómo han podido aprovechar mucho del mismo código para entregar experiencias en diferentes tipos de dispositivos.

Los otros dos invitados en esta serie de textos pueden ser encontrados aquí y aquí.

Una de las nuevas características presentadas durante la conferencia Build es el Windows Runtime que converge en Windows Phone 8.1, que abre el camino a las “aplicaciones universales de Windows”. En este texto me gustaría compartirles mi primera experiencia con las aplicaciones universales de Windows, su estructura y posible futuro.

El antes

Antes de la aparición de las aplicaciones universales, crear aplicaciones para la Tienda Windows 8.x y Windows Phone 8 era en su mayoría un proyecto de dos aplicaciones. Claro que algún código lógico podía ser compartido a través de Librerías de Clase Portable (PCL o Portable Class Libraries), pero la superficie común de las PCL era muy pequeña, lo que llevaba a muchas declaraciones tipo #if/#endif. Además, compartir XAML era muy complicado, y en mi experiencia un ejercicio inútil, por lo que tenía que mantenerse separado.

Otras cosas, similares en el exterior, como los Mosaicos Vivos y las notificaciones visuales, tenían que hacerse de manera diferente, porque era una API diferente, construida en bases diferentes. La Tienda Windows fue construida en la parte alta de Windows Runtime (en ocasiones llamado WinRT) – una API nativa, basada en COM, mientras que la API Windows Phone era por lo general basada en Silverlight – en su mayoría .NET puro. Sí, con la versión 98.0 de Windows Phone algunas API de WinRT llegaron al teléfono (en ocasiones conocido como “WinPRT”), común con WinRT, pero esa API común era relativamente pequeña.

El después

Con Windows Phone 8.1 y Windows 8.1 Update, las cosas han cambiado, o debería decir – convergen. La API WinRT se ha esparcido al teléfono, reemplazando las API basadas en Silverlight. Por ejemplo, la clase Windows.UI.Xaml.Controls.Button para WinRT ahora existe como un tipo Win(P)RT en WP 8.1. El “viejo” botón, System.Windows.Controls.Button aún sigue ahí para soportar las aplicaciones existentes (y es probable que sea un envoltorio alrededor del “nuevo” botón). Las nuevas aplicaciones aún pueden utilizar el modelo Silverlight, pero sospecho que eso será descartado en el futuro.

Otro efecto de esta convergencia es “PCL Universales”, que ahora se han expandido para incluir un área mucho más larga que la de las PCL no universales. Utilizo el término “Universal PCL” pero estas son PCL regulares dirigiéndose a Win8.1/WP8.1 que incluyen una superficie más común para compartir, incluyendo XAML. El término “no universal” aquí significa en realidad PCL que apuntan más que Windows 8.1 y Windows Phone 8.1.

Aplicaciones Windows universales

Las aplicaciones Windows universales están ahí para permitir a la misma aplicación ser escrita para la Tienda Windows 8.1 y Windows Phone 8.1 con pocos cambios de código. Tomen en cuenta que no es lo mismo que ligar una aplicación Windows y Phone en la Tienda – esto puede hacerse en el modelo no universal también; aquí me refiero a compartir código, ya sea que las aplicaciones estén ligadas en la Tienda o no. Casi todo puede ser compartido, y obvio, algunas cosas tendrán que cambiar, como el diseño de la UI, o el uso de características especiales de una u otra plataforma. Pero resulta que, la mayoría del código, e incluso el XAML puede ser compartido, y esa es una ventaja real que antes no teníamos.

Vamos a crear una aplicación universal Windows simple y examinemos sus componentes. Para tener soporte para esto, necesitan Visual Studio 2013 (cualquier SKU) con Update 2. Esto instalará las plantillas de proyecto adecuadas y otro soporte requerido para que esto funcione.

Desde Visual Studio, seleccionen Archivo -> Nuevo Proyecto y naveguen a la nueva subcarpeta de Apps de la Tienda:




Es posible comenzar con una aplicación Windows 8.1 o una Windows Phone 8.1 y agregar su aplicación “gemela” utilizando una opción de menú contextual en el nodo de proyecto en el navegador de solución.

Con la selección de aplicación universal, tenemos dos proyectos de aplicación, uno para Win 8.1 y otro para Windows Phone 8.1. La parte inusual es la tercera, el proyecto “Compartido”:



Vean que los proyectos son muy similares. El archivo de manifiesto de Silverlight WMAppManifest.xml ha sido transformado a Package.appxmanifest, en esencia es el mismo al de su contraparte de Windows 8.1.

Como podemos ver, el proyecto “Compartido” tiene App.xaml y App.xaml.cs compartidos por defecto. Esto significa que esos archivos no aparecen en los proyectos normales, pero son “jalados” por cada uno cuando son construidos.

El proyecto Compartido es un poco extraño pues no tiene una salida propia – no es un DLL, EXE, XAP o lo que sea. No es nada; sólo contiene material compartido que debe ser jalado por cada uno de los proyectos “reales”. Incluso no cuenta con un nodo Referencias.

Así que, ¿Qué pasa si queremos utilizar algunas librerías de clase (mejor que sea una PCL Universal)? La agregamos como referencia para cada proyecto real, y luego lo que contiene está disponible para ser utilizado en el proyecto Compartido. ¿Lo tienen?

Hagamos algo de experimentación. Arrastren el archivo MainPage.xaml del proyecto Windows Phone al nodo de proyectos Compartidos:



Vean que el archivo no desaparece del proyecto original. Ahora, al tratar de construir se producen 2 errores, porque hay dos archivos con el mismo nombre (identidad) para cada proyecto.

Borremos el archivo MainPage.xaml (junto con el código detrás) de los proyectos Win 8.1. y WP 8.1, y dejémoslo sólo en el proyecto Compartido:



Ahora los dos proyectos construidos están bien, e incluso corren bien. En este caso, el archivo MainPage.xaml era idéntico en los dos proyectos originales, así que no hay problema. Agreguemos un control de Botón simple a la cuadrícula que existe en ese (ahora sólo uno) MainPage.xaml:




El diseñador ahora tiene una opción de cambiar a una o dos vistas – Win 8.1 o Win Phone 8.1 para que se puedan revisar los resultados en tiempo de diseño:



Al correr cada aplicación se muestra el botón como esperábamos, al centro de la Cuadrícula.

Por cierto, vean que el XAML ha convergido, por lo que ahora Windows Phone y Windows 8 reconocen la misma sintaxis, como la manera de mapear un namespace XAML a un namespace CLR a través de la palabra clave “using” en lugar de la palabra clave “clr-namespace”, como se hacía de la manera “Silverlight”. Este es sólo un aspecto que hace posible compartir XAML.

Es lo mismo pero en ocasiones diferente

Algunos aspectos de la aplicación podrían ser diferentes entre Windows Phone y Windows. Para este propósito, las plantillas de proyecto definen los símbolos de WINDOWS_PHONE_APP (para WP) WINDOWS_APP (para Windows), para que el código C# pueda utilizar los marcadores estándar #if/#else/#endif para distinguir entre las plataformas. Esto es útil en particular para una PCL y cualquier otro código que no sea XAML.

Compartir XAML es más difícil. Digamos que quiero tener dos piezas de XAML colocadas de manera diferente en WP y Windows como en el siguiente ejemplo:




Una manera de conseguir esto es utilizar la misma página XAML, pero hacer los ajustes a través de la propiedad obligatoria “View Model”, para alineación, cuadrícula de fila/columna, etc. Aunque esto es posible, es una pesadilla de mantenimiento para vistas no triviales.

Un mejor acercamiento podría ser excluir los controles importantes como controles de usuario, en el proyecto compartido y tener una página XAML diferente para cada plataforma. En el ejemplo anterior, cree un control de usuario DeviceList en el proyecto compartido que hospeda una lista de dispositivos en la plataforma actual. El XAML del control es simple:




El código detrás provee la propiedad de dependencia de Dispositivos del tipo IReadOnlyList<DeviceInformation> que está mapeada desde la interfaz IVectorVie<>WinRT.

Las dos páginas XAML para las plataformas simplemente utilizan el control en el lugar correcto. Esto mantiene simple la página XAML y no requiere ningún soporte del modelo de vista o convertidores de valor. Aquí el ejemplo para Windows:



La “encuadernación” está colocada en el código detrás (podría estar en una PCL o en el código compartido ViewModel que está en práctica):



De esta manera, los controles de usuario pueden ser compartidos y manejar el peso pesado, mientras que los objetos de Página podrían no ser compartidos, pero manejar el diseño general y las partes comunes (tales como imágenes, estatus o lo que sea).

¿Qué hay del Escritorio?

Desde mi perspectiva, la única cosa ausente en esta historia es el Escritorio. Microsoft se dio cuenta, hace tal vez un año o un poco más que el escritorio no se irá y es tan importante como siempre, si no es que más. Muchas aplicaciones corren en el escritorio (vs. las de la Tienda), muchas aplicaciones se apoyan en el mouse y el teclado para su utilización, y el tacto no es la respuesta para todo.

No veo ninguna razón por la que el modelo WinRT, incluyendo la UI y el XAML no vayan también hacia una aplicación de escritorio. WinRT es sólo COM al final de cuentas, y de hecho, muchos tipos pueden ya ser utilizados en aplicaciones de escritorio. Por alguna razón, la UI basada en XAML aún no puede pero en lo personal creo que debería. Los desarrolladores .NET tienen WPF, pero los desarrolladores nativos de escritorio tienen sólo MFC, que está obsoleto y primitivo en comparación ya sea con WPF o WinRT.

Por supuesto que hay otras alternativas C++UI que no son de Microsoft, como Qt, wxWidegts y muchas más, pero no son suficientes. Microsoft debería proveer su propio marco de trabajo, y ya está ahí – se trata de WinRT. Sólo necesita tener “permiso” de ejecutar aplicaciones de escritorio.

De hecho, creo que WinRT no sólo se trata de “aplicaciones”, en lugar de eso, es la nueva API Win32. Cada desarrollador que conoce alguna extensión con la API Win32 entiende estas palabras. Esta API es vieja, en su mayoría miles de funciones de estilo C, es difícil de manejar, y difícil de aprender debido a las API alternativas. WinRT está en su mayoría bien diseñada, orientada a objetos y sigue siendo nativa, así que tiene mucho potencial. Bien podría ser la nueva API Windows.

Veamos un rápido ejemplo de obtener esa misma lista de dispositivos y utilizar eso en una aplicación WPF.

El formato WinRT de metadatos es el mismo de .NET, así que agregar una referencia debería ser posible. Sin embargo, los proyectos de escritorio no están configurados por defecto para permitirlo. Necesitamos editar el archivo .csproj para permitir una fácil inclusión de la información de metadatos WinRT. Para hacer esto, descarguen el proyecto de escritorio, den clic derecho en el nodo del proyecto y elijan la opción de editar, que abrirá el archivo en el editor de texto XML. Agreguen lo siguiente como otro grupo de propiedad en cualquier parte del archivo:



Salven el archivo y vuelvan a cargar el proyecto. Abran la caja de diálogo Add Reference y vean el nodo “Windows” con el sub-nodo “Core” y el archivo WinMD Windows a la derecha:




Ahora que está ahí la referencia, podemos utilizar la API WinRT. Primero, aquí está un XAML simple para la ventana WPF:




Quiero mostrar columnas para las propiedades Name, Id e IsEnabled de un objeto DeviceInformation. Para obtener esa información, necesitamos algo de código detrás, como el siguiente, mientras manejamos el evento Loaded:



Vean la operación async. Si intentamos compilar esto, fallará, y nos dirá que el método GetAwaiter no está presente en esa IAsyncOperation<> que regresó. Esto es porque el compilador C# tiene un patrón para métodos que son de espera (el “patrón awaiter”). La clase regular Task<T> de .NET soporta esto por supuesto, pero este es un tipo WinRT, no un tipo .NET – por lo que no hay noción de un awaiter.

La solución es referenciar otro montaje, que provea ese soporte para trabajar más fácil con WinRT desde .NET. necesitamos agregar una referencia al montaje System.Runtime.WindowsRuntime.Dll ubicado en una carpeta como “C:Program Files (x86)ReferenceAssembilesMicrosoftFramework.NETCorev4.5”. Esto provee no sólo soporte de espera, también otras cosas útiles como el método de extensión AsTask que puede convertir IAsyncAction/Operation en una Tarea .NET (que tiene una API rica).

Al correr la aplicación tendremos algo como esto:



¿Qué sigue?

Claro que hay mucho camino por recorrer, pero creo que vamos en la dirección correcta. Lo que es aparente para mí es que WinRT es el presente y el futuro, pero la API Silverlight está aquí sólo por compatibilidad y mantenimiento. Así que, si han evitado o negado WinRT – no lo hagan. Las nuevas aplicaciones (incluso si no están planeadas para ser multiplataforma) deberán estar basadas en la API WinRT y no en la de Silverlight.

Windows Runtime es en verdad universal, en más de un sentido. Espero que un núcleo común de Windows Runtime esté presente en todos los factores de forma – teléfono, tableta, escritorio y Xbox.

Read More: