Nace SVQXDG, grupo de desarrolladores Xamarin en Sevilla

Introducción

No hay duda, los smartphones han llegado, y ya forman parte de la
vida de todos nosotros. En muchos aspectos de nuestra vida accedemos a
información, realizamos algna tarea o sencillamente nos entretenemos con
uno de ellos.

Esto también nos afecta como desarrolladores. El desarrollo móvil se
ha convertido en una prioridad en una gran mayoria de ámbitos.

Actualmente contamos con varias plataformas dominantes:

  • iOS
  • Android
  • Windows Phone

Esto a nivel de desarrollo nos supone
barreras. Contamos con plataformas de desarrollo diferentes, lenguajes
diferentes, etc. suponiendo un nivel de aprendizaje y esfuerzo cada vez
mayor de cara a desarrolladores. Además, la experiencia nos ha
demostrado que los usuarios no aceptan aplicaciones no nativas. Buscan
aplicaciones rápidas, fluidas y con consumos moderados perfectamente
adaptadas a la plataforma, ofreciendo una experiencia completa.

¿Qué podemos hacer para mejorar este panorama?

Xamarin

Aquí es donde entran en juego las tecnologías multiplataforma. Entre
las opciones disponibles, una de las más destacadas y en auge es Xamarin.

Xamarin permite el desarrollo de aplicaciones móviles nativas multiplataforma utilizando C#.
Además, poder compartir una gran cantidad de código, outilizar Visual
Studio como IDE lo convierte en una excelente opción para muchos
desarrolladores.

SVQXDG

En Sevilla, existen empresas y desarrolladores con un enorme talento
utilizando Xamarin para sus desarrollos de Apps móviles. Con este crisol
nace SVQXDG, o lo que es lo mismo, grupo de desarrolladores Xamarin de Sevilla. Es un grupo de usuario donde se buscará tener un punto habitual de reunión para ayudar, compartir y aprender entre todos.


Se ha creado un grupo en Meetup donde cualquier desarrollador podrá participar en discuciones, se organizarán actividades de grupo como quedadas donde poder charlar relajadamente sobre novedades, herramientas y otros aspectos relacionados además de por supuesto en eventos de comunidad principalmente presenciales aunque también online.

¿Te apuntas?

Más información

[Windows 10] Extensions SDK

Introducción

Desde la llegada de las Apps Universales ya con el SDK de Windows
Phone 8.1 se denotaba el esfuerzo por facilitar a los desarrolladores la
posibilidad de crear Apps para varias plataformas compartiendo la mayor
cantidad de código posible. Sin embargo, no debemos olvidar que la App
tiene como destino múltiples plataformas con características muy
diferentes.

¿Cómo se gestionan las características específicas de cada plataforma?

Antes de Windows 10

Con la llegada de las Apps Universales tuvimos una solución con tres
proyectos, uno específico para Windows, otro para Windows Phone y un
tercer proyecto llamado Shared donde añadir la mayor cantidad de código compartido posible. El resultado de la compilación eran dos binarios, uno por plataforma.

A nivel de desarrollo, a pesar del esfuerzo visible en la congergencia
entre plataformas, no todas las APIs estaban 100% compartidas.

Ante casos de APIs no compartidas o específicas de una plataforma teníamos múltiples opciones:

  • Colocar el código específico en cada proyecto de cada plataforma.
  • Utilizar directivas de compilación en clases compartidas.

Mediante directivas de compilación podemos elegir que parte del
código ejecutar dependiendo de la plataforma donde se ejecute. Se debe
utilizar sólo para pequeños trozos de código. Hay que tener en cuenta
que aunque logramos nuestro objetivo, en algunos casos estamos
duplicando código.

Con la llegada de Windows 10

Con la llegada del SDK de Windows 10 Preview tenemos la posibilidad de crear Apps Universales con un único binario
que funcione en múltiples plataformas. Tenemos acceso a una gran
cantidad de APIs totalmente compartidas pero… ¿que ocurre cuando
necesitamos acceder a APIs específicas de una plataforma?

La respuesta a la pregunta son los Extensions SDK. Por cada familia de dispositivo contamos con una serie de extensiones que nos permiten acceder a APIs específicas.

NOTA: Por ahora, en la versión disponible de las
herramientas de desarrollo para Windows 10 Technical Preview contamos
con extensiones para Desktop y Mobile.

Añadiendo extensiones

Para añadir una extensión correspondiente a una familia de
dispositivo hacemos clic derecho sobre las referencias del proyecto y
seleccionamos la opción Add Reference…

Accediendo al apartado Extensions dentro de Universal App Platform:

Tenemos disponible la extensión para Desktop y Mobile.

Trabajando con extensiones

Al trabajar con extensiones siempre debemos realizar verificaciones
de si la API específica de una familia de dispositivos con la que
deseamos trabajar esta disponible o no. Si no realizamos verificaciones y
la llamada a la API de una extensión se ejecuta en un dispositivo que
no soporta dicha extensión, obtendremos una excepción.

Vimos previamente como se gestionaba en Windows Phone 8.1 mediante
directivas de compilación la pulsación de la tecla volver. A
continuación, vamos a ver exactamente lo mismo utilizando Extensions SDK
en Windows 10.

Tras añadir la extensión Mobile:

En el constructor de la App en el archivo App.xaml.cs añadimos:

 HardwareButtons.BackPressed += HardwareButtons_BackPressed;

Con el evento asociado:

private void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
    //TODO:
}

Tan sencillo como esto pero si ejecutamos en Windows la App…

Como comentamos previamente debemos añadir verificaciones para asegurar que el dispositivo soporta la extensión o no:

if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
     HardwareButtons.BackPressed += HardwareButtons_BackPressed;

Utilizamos el método IsTypePresent de la clase estática ApiInformation incluida dentro del namespace Windows.Foundation.Metadata.

NOTA: En la clase estática ApiInformation tenemos métodos para verificar no solo tipos, sino métodos, propiedades, contratos, etc.

La verificación se realiza con el nombre completo del tipo en una cadena.

NOTA: Sin duda no es quizás la manera más óptima
para realizar la comprobación. Recordad que estamos ante una versión
Preview de las herramientas.

Podéis descargar el  pequeño ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información

[Windows 10] Control SplitView

El control SplitView

Algunos desarrolladores lo conocerán como side menu, otros como navigation drawer y otros como hamburguer menu.
En todas las plataformas móviles se ha utilizado en muchas Apps como
una solución de navegación. Consiste en un Menu deslizante que aparece
de uno de los laterales al pulsar un botón situado habitualmente en un
lateral en la parte superior. Con la llegada de Windows 10 nos llega un
nuevo patrón de diseño y navegación en las Apps:

Las herramientas de desarrollo de Windows 10 Technical Preview nos
aportan nuevas herramientas, emuladores, APIs y controles. Entre el
conjunto del controles nuevos disponibles cabe destacar el control SplitView. Este control es nos permite crear un menu deslizante lateral.

Anatomía del control

Antes de entrar de lleno, en el uso del control vamos a realizar un
análisis de la anatomía del mismo. El control SplitView cuenta con una
propiedad Pane que permite establecer el contenido del panel lateral. Podemos mostrar el panel a izquierda o derecha mediante la propiedad PanePlacement y el ancho utilizando OpenPaneLength.

El panel lateral puede estar expandido o no. Este comportamiento se gestiona con la propiedad IsPaneOpen. En la imagen superior el menu esta contraido. Al pulsar sobre el botón Hamburguer:

El menu se expande. La propiedad IsPaneOpen se modificará a
True. Podemos además modificar aspectos visuales como el color de fondo,
el color de fondo del panel o el ancho que tendrá el menu contraido.

En este artículo vamos a utilizar el control SplitView como
base estructural para la navegación de nuestra aplicación utilizando
buenas prácticas como el patrón MVVM, inyección de dependencias o el uso
de servicios.

Utilizando el control SplitView

Para probar las posibilidades del control SplitView crearemos un nuevo proyecto UAP:

Nuestro objetivo en este ejemplo será utilizar como base de la App el
control SplitView. Contaremos con un menu lateral von múltiples
opciones que nos permitirán acceder a distintos apartados de la App.

Comenzamos añadiendo el control SplitView en nuestra vista principal:

<SplitView>
</SplitView>

Modificamos el control utilizando sus propiedades básicas para adaptarlo a nuestras necesidades:

<SplitView x:Name="Splitter"
     DisplayMode="CompactInline"  
     Background="{StaticResource BackgroundBrush}"      
     PaneBackground="{StaticResource BackgroundPaneBrush}"       
     PanePlacement="Left"      
     CompactPaneLength="60"
     OpenPaneLength="240"    
     IsPaneOpen="{Binding IsPaneOpen}">
</SplitView>

Hemos utilizado las siguientes propiedades:

  • PanePlacement: Posición del Panel lateral. Tenemos dos posibles opciones, izquierda (Left) o derecha (Right).
  • PaneBackground: Color de fondo del Panel lateral.
  • OpenPaneLength: Ancho en píxeles del Panel abierto.
  • CompactPaneLength: Ancho en píxeles del Panel cerrado.
  • Background: Color de fondo.
  • IsPanelOpen: Propiedad de tipo bool que nos permite tanto saber como establecer si el Panel esta abierto no.

Otra de las propiedades fundamentales del control es DisplayMode.
Esta propiedad nos permite indicar el comportamiento del menu lateral
tanto abierto como cerrado. Tenemos cuatro opciones disponibles:

  • Inline: El panel cerrado no aparece mientras que abierto pasa a ocupar el ancho establecido en la propiedad OpenPaneLength, dejando que el contenido ocupe el resto del espacio disponible.
  • Overlay: El comportamiento es similar a Inline con
    la diferencia(importante) del modo abierto. En modo abierto el contenido
    ocupa todo el espacio, el Panel se posiciona por encima del contenido.
  • Compact Inline: El Panel abierto tiene el ancho establecido en la propiedad OpenPaneLength, dejando al contenido el resto del espacio. Al cerrar el Panel, pasa a ocupar el ancho establecido en la propiedad CompactPaneLength.
  • Compact Overlay: En este caso el Panel se posiciona por encima del contenido en modo abierto.

En la ViewModel de la vista tendremos una propiedad bindeada a la propiedad IsPaneOpen para poder gestionar el estado del menu:

private bool _isPaneOpen;
 
public bool IsPaneOpen
{
     get
     {
          return _isPaneOpen;
     }
     set
     {
      _isPaneOpen = value;
      RaisePropertyChanged();
     }
}

Pero… ¿cómo modificamos el estado del Panel?.

Necesitamos definir un ToggleButton para gestionar el estado
del Panel. El botón contará con dos estados para mostrar una apariencia
diferencia al usuario cuando el Panel este abierto o cerrado. Al
pulsarlo ejecutará un comando en la ViewModel que modificará la propiedad IsPaneOpen:

<ToggleButton                
     x:Name="HamburguerButton"                
     Style="{StaticResource SymbolButton}"                       
     Command="{Binding HamburgerCommand}"          
     VerticalAlignment="Top"            
     Foreground="White"          
     Margin="0,5,0,0">   
     <ToggleButton.Content>   
          <Border Background="Transparent"     
               Width="40"       
               Height="40">          
               <FontIcon x:Name="Hamburger"        
                    FontFamily="Segoe MDL2 Assets"          
                    Glyph="" />
          </Border>     
     </ToggleButton.Content>
</ToggleButton>

El comando a ejecutar:

private ICommand _hamburgerCommand;
 
public ICommand HamburgerCommand
{
     get { return _hamburgerCommand = _hamburgerCommand ?? new DelegateCommand(HamburgerCommandExecute); }
}
 
private void HamburgerCommandExecute()
{
     IsPaneOpen = (IsPaneOpen == true) ? false : true;
}

El contenido del Panel lateral lo definiremos dentro de la propiedad Pane:

<SplitView.Pane>
     <RelativePanel>
          <ListView
             ItemsSource="{Binding MenuItems}"
             SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}"
             ItemTemplate="{StaticResource MenuItemDataTemplate}"
             SelectionMode="Single"
             IsItemClickEnabled="False"
             Margin="0, 50, 0, 0" />
     </RelativePanel>
</SplitView.Pane>

En  nuestro ejemplo mostraremos un menu con varias opciones. Para ello, utilizaremos una lista de elementos MenuItem:

public class MenuItem
{
     public string Icon { get; set; }
     public string Title { get; set; }
     public Type View { get; set; }
}

Cada MenuItem (clase definida dentro de la carpeta Models) contará con
el icono y título a mostrar además del tipo de la vista a la que
navegará. Al entrar en la vista cargaremos nuestro listado de opciones:

private void LoadMenu()
{
     MenuItems = new ObservableCollection<MenuItem>
     {
         new MenuItem
         {
              Icon = "",
              Title = "Home",
              View = typeof(HomeView)
         },
         new MenuItem
         {
              Icon = "",
              Title = "Standings",
              View = typeof(StandingsView)
         },
         new MenuItem
         {
              Icon = "",
              Title = "About",
              View = typeof(AboutView)
         }
     };
 
     SelectedMenuItem = MenuItems.FirstOrDefault();
}

Además de cargar el listado, seleccionamos el MenuItem por
defecto, que será el primero. De todo lo anterior la parte más destacada
es el icono. Con la llegada de las herramientas de Windows 10 Technical
Preview, nos llega un nuevo tipo de fuente a utilizar en nuestras Apps,
Segoe MDL2 Assets:

NOTA: Podemos utilizar la herramienta Mapa de
carácteres para ver todos los iconos disponibles en la nueva fuente.
Para utilizar el icono en vuestras Apps bastará con copiar el icono
deseado.

Una vez establecido el elemento de Menu seleccionado, realizamos la navegación a la vista correspondiente:

public MenuItem SelectedMenuItem
{
    get { return _selectedMenuItem; }
    set
    {
        _selectedMenuItem = value;
        RaisePropertyChanged();
 
        Navigate(_selectedMenuItem.View);
    }
}

El Panel lateral del SplitView se mantendrá visible siempre modificando la vista que se muestra al cambiar el elemento seleccionado del panel.

¿Cómo gestionamos esto?

El Frame es el encargado de contener y gestionar cada una de las páginas (Page).Tenemos un Frame
creado durante el arranque de la App. Sin embargo, queremos gestionar
la navegación de páginas desde la página que contiene el SplitView.
Debemos crear otro frame dentro del control SplitView:

<Frame x:Name="SplitViewFrame"
       Margin="0, 10" />

Para gestionar correctamente la navegación desde nuestras ViewModels
debemos tener acceso al Frame. Todas nuestras páginas heredan de una PageBase donde estableceremos el Frame del SplitView:

private Frame _splitViewFrame;
 
public Frame SplitViewFrame
{
     get { return _splitViewFrame; }
     set
     {
          _splitViewFrame = value;
 
      if(_vm == null)
           _vm = (ViewModelBase)this.DataContext;
 
      _vm.SetSplitFrame(_splitViewFrame);
     }
}

Al establecer el Frame del SplitView en el PageBase se establecerá el mismo en una propiedad de tipo Frame de la ViewModelBase, clase de la que heredan todas nuestras ViewModels:

public Frame SplitViewFrame
{
     get { return splitViewFrame; }
}

De esta forma desde la ViewModel, de forma muy sencilla podemos gestionar la navegación:

private void Navigate(Type view)
{
    var type = view.Name;
 
    switch (type)
    {
        case "HomeView":
            SplitViewFrame.Navigate(view, _driverStanding);
            break;
        case "StandingsView":
            SplitViewFrame.Navigate(view, _driverStanding);
            break;
        case "AboutView":
            SplitViewFrame.Navigate(view);
            break;
    }
}
El resultado es el siguiente:
Al pulsar el botón Hamburguer, modificamos la propiedad IsPaneOpen de la ViewModel que modificará el estado del SplitView cerrándolo:

Pulsándo sobre cualquier elemento del Panel lateral provocará el
cambio del elemento seleccionado, se llamará al método Navigate de la ViewModel provocando la navegación en el Frame contenido dentro del SplitView.

Volver atrás

En las Apps Windows Phone contamos con el botón físico para navegar
atrás. En el caso de Apps Windows Store, gestionamos el botón volver en
pantalla.

¿Cómo gestionamos el botón volver en Apps UAP?

Añadimos en nuestra nuestra vista principal,
donde definimos el SplitView un nuevo elemento visual que nos permita
gestionar la navegación atrás:

<RadioButton
     x:Name="BackButton" 
     Command="{Binding BackCommand}"  
     Background="{StaticResource SystemControlBackgroundAccentBrush}"           
     Width="240"               
     Margin="0, 50, 0, 0">                  
     <RadioButton.Tag>                      
          <TextBlock Text=""                   
                     FontFamily="Segoe MDL2 Assets"                               
                     VerticalAlignment="Center"               
                     HorizontalAlignment="Left"                
                     Margin="24, 0, 0 ,0" />                     
     </RadioButton.Tag>     
</RadioButton>

Utilizamos la nueva fuente Segoe MDL2 Assets para mostrar una flecha hacia atrás y vinculamos con un comando llamado BackCommand definido en la ViewModel:

private DelegateCommand _backCommand;
 
public ICommand NavigateCommand
{            
     get { return _navigateCommand = _navigateCommand ?? new DelegateCommand<MenuItem>(NavigateCommandExecute); }
}
 
private void BackCommandExecute()
{
     SplitViewFrame.GoBack();
     var selectedMenuItem = SplitViewFrame.CurrentSourcePageType;
     SelectedMenuItem = MenuItems.FirstOrDefault(mi => mi.View.Equals(selectedMenuItem));
}    
 
private bool BackCommandCanExecute()
{
     return SplitViewFrame.CanGoBack;
}

Se verifica si hay elementos en la colección de páginas y podemos
navegar hacia atrás para habilitar el botón. En caso de poner navegar,
se realiza la navegación atrás (GoBack) además de seleccionar el elemento correspondiente del menu lateral.

El resultado:

Este nuevo control utilizado junto a otras novedades como los
Adaptive Triggers o el nuevo Panel RelativePanel nos permitirá definir
interfaces de usuario que se adapten ante cualquier tamaño de pantalla y
dispositivo:

<VisualStateManager.VisualStateGroups>
     <VisualStateGroup>      
          <VisualState x:Name="wideState">                   
               <VisualState.StateTriggers>     
                    <AdaptiveTrigger MinWindowWidth="641" />
               </VisualState.StateTriggers>      
               <VisualState.Setters>         
                    <Setter Target="Splitter.DisplayMode" Value="CompactInline"/>
               </VisualState.Setters>
          </VisualState>              
          <VisualState x:Name="narrowState">            
               <VisualState.StateTriggers>    
                    <AdaptiveTrigger MinWindowWidth="0" />
               </VisualState.StateTriggers>
               <VisualState.Setters>
                    <Setter Target="Splitter.DisplayMode" Value="Overlay"/>
               </VisualState.Setters>
          </VisualState>
     </VisualStateGroup>    
</VisualStateManager.VisualStateGroups>

Podemos modificar el modo del SplitView segun el tamaño de la ventana.
Si reducimos el tamaño, podemos ocultar el menu lateral para aprovechar
mejor el espacio:

De igual forma, podemos modificar la posición de los elementos entre sí segun ciertas condiciones.

El resultado final lo podéis ver directamente:

Podéis descargar el ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información

[Tips and Tricks] Creando vistas específicas por dispositivo en Apps Windows 10

Introducción

Con la llegada del SDK de Windows 10 Preview tenemos la posibilidad de crear Apps Universales con un único binario
que funcione en múltiples plataformas. Es un paso importante pero que
conlleva realizar una acción que sera comun, diferenciar entre las
diferentes plataformas donde correrá dicho binario para poder adaptar la
interfaz de usuario. Con ese objetivo utilizamos entre otras opciones
los Adaptive Triggers de los que ya hemos hablado.

Sin embargo, en ocasiones la misma vista en disferentes dispositivos puede que sea totalmente diferente.

¿Que hacer ante estas situaciones?

Creando vistas específicas por dispositivo

Podemos utilizar Adaptive Triggers para adaptar la interfaz o en caso
de ser más simple tener dos vistas en dos ficheros diferentes, podemos
hacerlo.

Crearemos un nuevo proyecto UAP:

Por defecto, se nos añade una vista llamada MainPage:

<Page
    x:Class="DeviceFamily_View.MainPage"
    xmlns:local="using:DeviceFamily_View"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    </Grid>
</Page>

Añadimos un texto específico a mostrar en Windows junto a un botón:

<StackPanel         
     HorizontalAlignment="Center"  
     VerticalAlignment="Center">
     <TextBlock
          Text="Windows"
          FontSize="48" />
     <Button
          Content="Botón" />
</StackPanel>

Si ejecutamos la App veremos algo como lo siguiente:

Veremos exactamente lo mismo ejecutando la App en Windows Phone.

¿Y si deseamos tener la misma vista específica para Windows Phone?

Creamos una carpeta siguiente la siguiente nomenclatura:

  • DeviceFamily-[Family]

Donde Family es la familia del dispotivo para el que
deseamos sobrescribir la vista. En nuestro ejemplo, creamos una carpeta
llamada DeviceFamily-Mobile.

Una vez creada la carpeta, vamos a añadir una vista XAML en la misma.
Clic derecho sobre la misma y elegimos la opción “Añadir nuevo
elemento”:

Añadimos la sobreescritura de la vista MainPage. Es importante un
detalle del fichero añadido, no añadimos code behind. Esta vista
utilizará el mismo code behind que la que teníamos previamente.

Añadimos el contenido de la vista específica para Windows Phone:

<StackPanel         
     HorizontalAlignment="Center"  
     VerticalAlignment="Center">
     <TextBlock
          Text="Windows Phone"
          FontSize="48" />
     <Button
          Content="Botón"
          Click="Button_Click"/>
</StackPanel>

Esta vista es específica para Windows Phone. En este momento al ejecutar
en Windows se mostrará la vista creada con el proyecto, mientras que si
ejecutamos en Windows Phone se mostrará la vista de la carpeta DeviceFamily-Mobile. El code behind, como comentamos es compartido. Si añadimos por ejemplo el evento clic del botón:

private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
     Debug.WriteLine("Lógica compartida!");
}

NOTA: Normalmente para lanzar la acción de un
botón al ser pulsado utilizaremos un comando en la ViewModel asociada
con DataContext de la vista.

Tendremos la lógica compartida en un mismo fichero.

NOTA: Recordad que estamos ante una versión
Preview de las herramientas de desarrollo. La versión final puede variar
con respecto al comportamiento actual.

Podéis descargar el ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

De esta forma podemos sobreescribir vistas por completo para tener
vistas específicas por familia de dispositivos. Recordar que cualquier
tipo de duda o sugerencia la podéis dejar en los comentarios de la
entrada.

Más información

[Quedada CartujaDotNet] Preparando próximos eventos

logo-cartuja-net_4Quedada

En CartujaDotNet
vamos a realizar una quedada informal para charlar abiertamente sobre
tecnologías Microsoft, herramientas utilizadas, intercambiar
impresiones, etc. Además, se analizarán las próximas charlas ya
planteadas y los eventos confirmados entre otros temas de interés.

No hace falta confirmar asistencia, y por supuesto será gratuito.

¿Te apuntas?

A continuación tienes disponible la fecha, hora y lugar:

Quedada CartujaDotNet
Bar Quinoa

Más información

[Evento CartujaDotNet] Introducción al desarrollo de Apps Windows 10

El evento

El pasado 23 de Marzo, Microsoft liberaba dentro del programa de
Windows Insider una Preview de las herramientas de desarrollo de Windows 10. Con la llegada de las nuevas herramientas tenemos la posibilidad de crear Apps Universales con un único binario que funcione en múltiples plataformas además de contar con nuevas APIs, controles y herramientas.

Ante tanta novedad, desde CartujaDotNet se organiza un evento para analizar los cambios principales.

¿Te apuntas?

Fecha

El evento tendrá lugar el próximo Jueves, 23 de Abril de 19:00h a 21:00h. Tendremos una sesión de 2 horas de duración.

Lugar

Tendrá lugar en el Cloud Pointing de Sevilla situado en el Parque Empresarial Nuevo Torneo. Tenéis la información exacta del lugar a continuación:

c Biología, 12, Edificio Vilamar 2, 3ª Planta
Parque Empresarial Nuevo Torneo
41015 Sevilla

Agenda y Ponentes

Introducción al desarrollo de Apps Universales Windows 10 (Javier Suárez & Josué Yeray) (120 minutos)

En esta sesión contaremos con Javier Suárez y Josué
Yeray, Windows Platform Development MVPs, que nos contarán las
principales novedades en desarrollo de Apps Universales en Windows 10.
Veremos las nuevas plantillas y herramientas disponibles para hacer
Apps, las bases de UAP viendo el SDK de extensiones además de analizar
técnicas para crear interfaces adaptativas a cualquier dispositivo
utilizando nuevos controles como el panel RelativePanel, el SplitView o con las novedades en estados visuales, los Adaptive Triggers.

Más información

[Windows 10] Navegación en Apps Windows

Introducción

Casi cualquier aplicación que realices tendrá más de una página. Por lo que es vital saber como navegar entre ellas.

En este artículo, vamos a aprender como se realiza la navegación entre páginas en Apps Windows
primero desde un punto de vista teórica para terminar realizando un
ejemplo paso a paso cubriendo todos los aspectos relacionados con la
navegación utilizando el patrón MVVM.

¿Te apuntas?

Navegación entre páginas

El Frame, se crea en el arranque de la aplicación y es el encargado de contener y gestionar cada una de las páginas (Page). Contamos con un modelo de navegación integrado que usa controles Frame y Page y funciona de manera similar a un navegador web. El control Frame hospeda controles Page
y tiene un historial de navegación que se puede utilizar para ir hacia
atrás y hacia adelante por las páginas que ya visitaste. Tras obtener el
Frame correspondiente, utilizamos el método Navigate para realizar la navegación a otra página. Tenemos dos sobrescrituras del método Navigate:

  • Navigate(TypeName). Provoca que el Frame cargue el contenido especificado por el tipo pasado como parámetro.
  • Navigate(TypeName, Object).
    En este caso, además de indicar el tipo del contenido a cargar (primer
    parámetro), podemos pasar un parámetro a la página que se navega(segundo
    parámetro).

Navegar atrás

Utilizamos primero la propiedad CanGoBack que
nos devuelve un bool indicando si hay páginas en el historial de
navegación o no. Si existe historial de navegación utilizamos el método GoBack que navega al elemento más inmediato del historial de navegación.

NOTA: También contamos con la propiedad CanGoForward. Indica si en el historial de navegación existe una página delante de la actual.

Pasar información entre páginas

Hemos visto la teoría necesaria para navegar hacia delante y hacia
atrás. Sin embargo, en ocasiones, necesitamos pasar información entre
las páginas. Ya hemos visto que el método Navigate cuenta con una sobrescritura que nos permite pasar parámetros.

Navigate(TypeName, Object).
En este caso, además de indicar el tipo del contenido a cargar (primer
parámetro), podemos pasar un parámetro a la página que se navega(segundo
parámetro).

Al navegar hacia o desde una página se ejecutan los métodos:

Para recuperar el objeto pasado como parámetro vamos a utilizar el método OnNavigatedTo que se ejecutará al entrar en la segunda página. El argumento de tipo NavigationEventArgs  cuenta con una propiedad Parameter.

Manos a la obra!

Para probar todas las posibilidades de navegación crearemos un nuevo proyecto UAP:

Nuestro objetivo en el ejemplo sera crear múltiples páginas y navegar entre ellas.

Realmente podemos mantener una implementación MVVM
muy similar a como hacíamos hasta ahora en Apps Universales para Windows
y Windows Phone 8.1 salvando las diferencias a nivel de proyectos (tres
VS uno). Comenzamos por la base. Contamos con dos ficheros clave en
nuestro proyecto para implementar el patrón MVVM:

  • PageBase
  • ViewModelBase

PageBase

Como revelamos con su nombre, será una clase de la que heredaran
todas las páginas de nuestra aplicación. Y si, comentamos que serán
todas las páginas de la aplicación (ya sean del proyecto Windows o del
proyecto Windows Phone). Las páginas en ambas plataformas es exactamente
igual, un objeto de tipo Page. Esta clase cuenta con varios objetivos:

  • Establecer el Frame activo en todo momento para que el acceso al mismo sea sencillo.
  • Permitir el acceso de los eventos de navegación desde nuestras viewmodels.

Además podría interesarnos además:

  • Gestionar las transiciones entre páginas.
  • Gestionar estados (Loading, etc.)
  • Gestionar el estado segun la conexión de red.

Con la funcionalidad básica (facilitarnos el acceso a Frame y eventos de navegación) quedaría:

public class PageBase : Page
{
        private ViewModelBase _vm;
 
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
 
            _vm = (ViewModelBase)this.DataContext;
            _vm.SetAppFrame(this.Frame);
            _vm.OnNavigatedTo(e);
        }
 
        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            _vm.OnNavigatedFrom(e);
        }
}

Gestionamos los eventos básicos de navegación (al entrar y al salir de la página) de modo que, en el método OnNavigateTo obtenemos la viewmodel de la página y asignamos el Frame.

ViewModelBase

Continuamos con la segunda de nuestras clases base. En esta ocasión
trataremos la clase base de la que heredarán todos los viewmodels. Los
objetivos básicos de la clase son:

  • Notificar cambios (implementar INotifyPropertyChanged).
  • Acceso al objeto Frame que nos permitirá realizar la navegación.
  • Permitir el acceso a los eventos de navegación.
public abstract class ViewModelBase : INotifyPropertyChanged
{
        private Frame appFrame;
        private bool isBusy;
 
        public ViewModelBase()
        {
        }
 
        public Frame AppFrame
        {
            get { return appFrame; }
        }
 
        public bool IsBusy
        {
            get { return isBusy; }
            set
            {
                isBusy = value;
                RaisePropertyChanged();
            }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        public abstract Task OnNavigatedFrom(NavigationEventArgs args);
 
        public abstract Task OnNavigatedTo(NavigationEventArgs args);
 
        public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
        {
            var Handler = PropertyChanged;
            if (Handler != null)
                Handler(this, new PropertyChangedEventArgs(propertyName));
        }
 
        internal void SetAppFrame(Frame viewFrame)
        {
            appFrame = viewFrame;
        }
}

El acceso a los eventos de navegación lo lograremos implementando en la viewmodel los métodos abstractos OnNavigatedFrom y OnNavigatedTo. Esto no permite guardar y recuperar parámetros o estados.

Utilizando PageBase

En la carpeta Views añadiremos nuestras páginas. En nuestro ejemplo, tendremos dos páginas:

  • FirstView
  • SecondView

Cada página será un objeto de tipo PageBase, tanto en XAML:

<base:PageBase
    x:Class="Navigation.Views.FirstView"
    xmlns:local="using:Navigation"
    xmlns:base="using:Navigation.Views.Base"
    mc:Ignorable="d"
    DataContext="{Binding FirstViewModel, Source={StaticResource Locator}}">
    <Grid>
    </Grid>
</base:PageBase>

Como en el code-behind:

public sealed partial class FirstView : PageBase
{
     public FirstView()
     {
         this.InitializeComponent();
     }
}

Añadimos en la primera vista (FirstView) dos botones que permita navegar a la segunda vista sin pasar y pasándo parámetro:

<Grid
     Background="LightBlue">
     <StackPanel
          HorizontalAlignment="Center"
      VerticalAlignment="Center">
      <Button
        Content="Go to Second View"
        Command="{Binding GoToSecondCommand}" />
      <Button
        Content="Pass parameter"
        Command="{Binding ParameterCommand}"
        Margin="0, 10"/>
     </StackPanel>
</Grid>

El resultado visual:

Para permitir navegar a la segunda vista, necesitamos definir dos
comandos (uno navegará sin pasar parámetro y otro pasándolo) en la
viewmodel. Creamos una clase derivada de ViewModelBase:

public class FirstViewModel : ViewModelBase
{
 
}

Implementamos los métodos de navegación definidos en ViewModelBase:

public class FirstViewModel : ViewModelBase
{
    public override Task OnNavigatedFrom(NavigationEventArgs args)
    {
        return null;
    }
 
    public override Task OnNavigatedTo(NavigationEventArgs args)
    {
        return null;
    }
}

Y añadimos comandos que permitan navegar de la página principal a la segunda página:

public class FirstViewModel : ViewModelBase
{
        private ICommand _goToSecondCommand;
        private ICommand _parameterCommand;
 
    public override Task OnNavigatedFrom(NavigationEventArgs args)
    {
        return null;
    }
 
    public override Task OnNavigatedTo(NavigationEventArgs args)
    {
        return null;
    }
 
    public ICommand GoToSecondCommand
    {
        get { return _goToSecondCommand = _goToSecondCommand ?? new DelegateCommand(GoToSecondCommandExecute); }
    }
 
    public ICommand ParameterCommand
    {
        get { return _parameterCommand = _parameterCommand ?? new DelegateCommand(ParameterCommandExecute); }
    }
 
    private void GoToSecondCommandExecute()
    {
        AppFrame.Navigate(typeof(SecondView));
    }
 
    private void ParameterCommandExecute()
    {
        var rnd = new Random();
        AppFrame.Navigate(typeof(SecondView), rnd.Next(1, 100));
    }
}

Recordamos que el control Frame hospeda controles Page
y tiene un historial de navegación que se puede utilizar para ir hacia
atrás y hacia adelante por las páginas que ya visitaste. Tras obtener el
Frame correspondiente, utilizamos el método Navigate para realizar la navegación a otra página. Tenemos dos sobrescrituras del método Navigate:

  • Navigate(TypeName). Provoca que el Frame cargue el contenido especificado por el tipo pasado como parámetro.
  • Navigate(TypeName, Object).
    En este caso, además de indicar el tipo del contenido a cargar (primer
    parámetro), podemos pasar un parámetro a la página que se navega(segundo
    parámetro).

En nuestro ejemplo hemos utilizado la segunda sobrescritura del método pasando un parámetro.

Ahora ,necesitamos conectar nuestra vista con nuestro viewmodel.
Podemos hacerlo de múltiples formas, desde el constructor de la vista,
instanciandola en App, usando Ioc. En nuestro caso, utilizaremos Ioc.
Usaremos Unity.

Una vez añadida la referencia correspondiente en cada proyecto (Windows y Windows Phone) creamos una nueva clase en la carpeta Base dentro de la carpeta ViewModels llamada ViewModelLocator:

public class ViewModelLocator
{
     readonly IUnityContainer _container;
 
     public ViewModelLocator()
     {
            _container = new UnityContainer();
 
            _container.RegisterType<FirstViewModel>();
            _container.RegisterType<SecondViewModel>();
     }
 
     public FirstViewModel FirstViewModel
     {
            get { return _container.Resolve<FirstViewModel>(); }
     }
 
     public SecondViewModel SecondViewModel
     {
            get { return _container.Resolve<SecondViewModel>(); }
     }
}

Sencillamente registramos nuestras viewmodels y creamos un par de
propiedades públicas por cada viewmodel para poder resolverlas y acceder
a ellas desde las vistas. A continuación, registramos nuestro locator en App.xaml:

<locator:ViewModelLocator x:Key="Locator" />

Asignamos la viewmodel como DataContext de nuestra viewmodel:

DataContext="{Binding FirstViewModel, Source={StaticResource Locator}}"

Y todo preparado!. Nuestra segunda página sera similar a la primera (tipo PageBase):

<base:PageBase
    x:Class="Navigation.Views.SecondView"
    xmlns:local="using:Navigation.Views"
    xmlns:base="using:Navigation.Views.Base"
    mc:Ignorable="d"
    DataContext="{Binding SecondViewModel, Source={StaticResource Locator}}">
    <Grid
        Background="LightGreen">
        <StackPanel
            HorizontalAlignment="Center"                    
            VerticalAlignment="Center">
            <Button
                Content="Go Back"
                Command="{Binding BackCommand}" />
            <TextBlock
                Text="{Binding Parameter}"
                Visibility="{Binding Parameter, Converter={StaticResource IntToVisibilityConverter}}"
                FontSize="48" />
        </StackPanel>
    </Grid>
</base:PageBase>

Contará con su propia viewmodel:

public class SecondViewModel : ViewModelBase
{
    // Variables
    private int _parameter;
 
    // Commands
    private ICommand _backCommand;
 
    public override Task OnNavigatedFrom(NavigationEventArgs args)
    {
        return null;
    }
 
    public override Task OnNavigatedTo(NavigationEventArgs args)
    {
            if (args.Parameter != null)
        {
            Parameter = Convert.ToInt32(args.Parameter);
                }
 
        return null;
    }
 
    public int Parameter
    {
        get { return _parameter; }
        set
        {
            _parameter = value;
            RaisePropertyChanged();
        }
    }
 
    public ICommand BackCommand
    {
        get { return _backCommand = _backCommand ?? new DelegateCommand(BackCommandExecute); }
    }
 
    private void BackCommandExecute()
    {
        if (AppFrame.CanGoBack)
            AppFrame.GoBack();
    }
}

Utilizamos el método GoBack del
Frame que navega al elemento más inmediato del historial de navegación,
la página anterior. Por otro lado, en caso de recibir parámetro, lo
asignamos a una propiedad bindeada en la vista. En nuestro ejemplo
pasamos un valor aleatorio entero entre 1 y 100.

En caso de utilizar el segundo botón, pasaremos el parámetro, se capturará en el método OnNavigatedTo y se bindeará en la vista:

Gestión de la tecla física volver

En nuestra aplicación, ejecutándola en Windows Phone y estando en la
segunda página, podemos utilizar también la tecla física como hacíamos
en Windows Phone 8 para volver atrás. Si la pulsamos… Ops!, ¿que ha ocurrido?

No hemos vuelto a la página anterior. En aplicaciones Windows XAML, al pulsar la tecla física atrás provoca una navegación a la App anterior no a la página anterior.

NOTA: En aplicaciones Windows Phone Silverlight 8.1 la navegación atrás es dentro de la propia aplicación.

Podemos incluir código personalizado para anular este comportamiento y
causar una navegación atrás dentro de la propia aplicación.

Tras añadir la extensión Mobile:

Windows Mobile Extension SDK

En App.xaml.cs:

if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
     HardwareButtons.BackPressed += HardwareButtons_BackPressed;

Utilizamos el método IsTypePresent de la clase estática ApiInformation incluida dentro del namespace Windows.Foundation.Metadata.

void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
     Frame frame = Window.Current.Content as Frame;
  
     if (frame == null)
          return;
  
     if (frame.CanGoBack)
     {
          frame.GoBack();
          e.Handled = true;
     }
}

Analizamos que hemos hecho. Cada vez que se pulse la tecla física
volver, entramos en nuestro código personalizado. Accedemos al Frame de
la aplicación, y si no es nulo, navegamos atrás y anulamos el
comportamiento por defecto.

Podéis descargar el ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información

[Tips and Tricks] Universal Apps. Behavior para gestionar gestos swipe con comando

Introducción

En las aplicaciones móviles la pantalla táctil permite una
interacción profunda del usuario con la misma. Gracias a la pantalla
táctil el usuario puede interactuar utilizando gran variedad de gestos.
Tocar, un toque prolongado, deslizar, pinzamientos y asi cubriendo una
gran variedad de gestos con uno o varios dedos.

En aplicaciones Windows Phone 8.1 Silverlight asi como en
aplicaciones Universales WinRT tenemos a nuestra disposición un conjunto
de eventos de manipulación que nos permiten gestionar la actividad dada
en la pantalla táctil con uno o más dedos. Podemos determinar toques,
toques prolongados, deslizamientos, etc. Sin embargo, la gestión es
mediante eventos, ¿que ocurre si deseamos una gestión igual de sencilla
pero manteniendo nuestra arquitectura sólida basada en MVVM?

En este artículo vamos a ver como crear un Behavior que nos permita capturar un tipo de gestos e invocar un comando de nuestra ViewModel.

¿Te apuntas?

Crear el Behavior

Comenzamos creando un nuevo proyecto:

Nueva Aplicación Universal

Nueva Aplicación Universal

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo.

Nuestro objetivo sera muy sencillo. Nuestra aplicación de ejemplo tendra un Behavior
asociado al Grid principal que capturará los gestos de tipo Swipe
lanzando un comando de la ViewModel pasando como parámetro la dirección
del gesto.

NOTA: El Behavior se podrá asociar a cualquier elemento derivado de UIElement.

Los Behaviors nos permiten añadir lógica a nuestro XAML para realizar
acciones sencillas (pero increíblemente útiles). Entre el conjunto
variado de acciones que nos permiten ejecutar, podemos lanzar
animaciones, cambiar el estado de un VisualState, realizar la
navegación de páginas o invocar un comando. En líneas generales suelen
permitir realizar tareas habituales sin necesidad de escribir código en
el code-behind de la vista lo que hace posible que mantengamos una
arquitectura sólida sin código auxiliar repartido en distintas partes.

Añadir extensión Behaviors

Añadir extensión Behaviors

Con la llegada del SDK de Windows 8.1 recibimos también la incorporación del Behavior SDK. Disponible en una librería aparte del Core de .NET, podemos utilizarlo facilmente añadiendo la referencia a la misma.

NOTA: Con la llegada de Windows Phone 8.1 recibimos acceso a la misma extensión.

Si para nuestros objetivos, ninguno de los Behaviors que tenemos disponible nos cubre, podemos crear Behaviors nuevos totalmente personalizados.

A diferencia de la implementación que teníamos en WPF por ejemplo, no contamos con clases del tipo Behavior<T> de las que derivar, sino que contamos con interfaces, IBehavior. Disponible en el namespace Microsoft.Xaml.Interactivity.

public abstract class Behavior<T> : DependencyObject, IBehavior
       where T : DependencyObject
{
 
     public T AssociatedObject { get; set; }
 
     protected virtual void OnAttached()
     {
     }
 
     protected virtual void OnDetaching()
     {
     }
 
     public void Attach(DependencyObject associatedObject)
     {
            if (associatedObject == this.AssociatedObject ||
                DesignMode.DesignModeEnabled)
            {
                return;
            }
 
            this.AssociatedObject = (T)associatedObject;
            OnAttached();
     }
 
     public void Detach()
     {
            if (!DesignMode.DesignModeEnabled)
            {
                OnDetaching();
            }
     }
 
     DependencyObject IBehavior.AssociatedObject
     {
            get { return this.AssociatedObject; }
     }
}

La clase superior implementa la interfaz IBehavior de modo que facilmente podemos sobreescribir la propiedad AssociatedObject. Creamos un nuevo Behavior para los elementos que deriven de UIElement:

public class SwipeBehavior : Behavior<UIElement>
{
 
}

Implementamos la interfaz:

protected override void OnAttached()
{
     base.OnAttached();
 
     this.AssociatedObject.ManipulationMode =
                this.AssociatedObject.ManipulationMode |
                ManipulationModes.TranslateX |
                ManipulationModes.TranslateY;
 
     this.AssociatedObject.ManipulationCompleted += OnManipulationCompleted;
}
 
protected override void OnDetaching()
{
     base.OnDetaching();
 
     this.AssociatedObject.ManipulationCompleted -= OnManipulationCompleted;
}
 
private void OnManipulationCompleted(object sender,
            ManipulationCompletedRoutedEventArgs e)
{
 
}

SwipeBehavior hereda de la clase Behavior<T>.  En el evento sobreescrito OnAttached nos suscribimos al evento ManipulationCompleted gestionando los modos de trasladación en el eje X e Y (Swipes). En el método OnDetaching realizamos la dessuscripción del evento ManipulationCompleted.

public ICommand SwipeCommand
{
     get { return (ICommand)GetValue(SwipeCommandProperty); }
     set { SetValue(SwipeCommandProperty, value); }
}
 
public static readonly DependencyProperty SwipeCommandProperty =
     DependencyProperty.Register("SwipeCommand",
     typeof(ICommand),    
     typeof(SwipeBehavior),  
     new PropertyMetadata(null));
 
public static ICommand GetSwipeCommand(DependencyObject d)
{
     return (ICommand)d.GetValue(SwipeCommandProperty);
}

Creamos una DependencyProperty de tipo ICommand. De esta forma, permitimos bindear un comando a ejecutar pasándole como parámetro la dirección del gesto.

Gestión de gestos

Verificaremos valores recibidos de ManipulationCompletedRoutedEventArgs.
Concretamente haremos varias verificaciones tanto en el eje X como en
el Y para determinar la dirección del gesto. Creamos dos sencillas
constantes para simplificar los cálculos:

private const double Min = 0.5;
private const double Max = 100;

En el evento ManipulationCompleted:

bool right = e.Velocities.Linear.X.CompareTo(Min) >= 0 && e.Velocities.Linear.X.CompareTo(Max) <= 0;
bool left = e.Velocities.Linear.X.CompareTo(-Max) >= 0 && e.Velocities.Linear.X.CompareTo(-Min) <= 0;
bool up = e.Velocities.Linear.Y.CompareTo(-Max) >= 0 && e.Velocities.Linear.Y.CompareTo(-Min) <= 0;
bool down = e.Velocities.Linear.Y.CompareTo(Min) >= 0 && e.Velocities.Linear.Y.CompareTo(Max) <= 0;
 
var swipeCommand = GetSwipeCommand(this);
 
if (swipeCommand != null)
{
     if (right && !(up || down))
                    swipeCommand.Execute(SwipeDirection.Right);
     if (left && !(up || down))
                    swipeCommand.Execute(SwipeDirection.Left);
     if (up && !(right || left))
                    swipeCommand.Execute(SwipeDirection.Up);
     if (down && !(right || left))
                    swipeCommand.Execute(SwipeDirection.Down);
}

El argumento de tipo ManipulationCompletedRoutedEventArgs,
cuenta con toda la información relacionada con la manipulación
realizada (gesto). Entre el conjunto de propiedades disponibles contamos
con Velocities.Linear.X y Velocities.Linear.Y
que nos permiten determinar la dirección del gesto. Dependiendo de la
dirección del gesto, ejecutaremos el comando bindeado en la propiedad SwipeCommand pasándole como parámetro la dirección.

En nuestra interfaz, detectaremos el gesto de tipo Swipe y segun la
orientación del gesto, mostrando un texto de información indicándola.

En nuestra ViewModel, definiremos la propiedad que mostrará en pantalla la orientación del gesto:

private string _info;
 
public string Info
{
     get { return _info; }
     set
     {
          _info = value;
          RaisePropertyChanged();
     }
}

Al lanzar un gesto de tipo Swipe lanzaremos un comando:

private ICommand _swipeCommand;
 
public ICommand SwipeCommand
{
     get { return _swipeCommand = _swipeCommand ?? new DelegateCommand<SwipeDirection>(SwipeCommandExecute); }
}
 
private void SwipeCommandExecute(SwipeDirection direction)
{
     switch (direction)
     {
          case SwipeDirection.Up:
               Info = "Arriba";
               break;
          case SwipeDirection.Down:
               Info = "Abajo";
               break;
          case SwipeDirection.Left:
               Info = "Izquierda";
               break;
          case SwipeDirection.Right:
               Info = "Derecha";
               break;
     }
}

Recibimos como parámetro el tipo del Swipe de modo que podemos ejecutar en consecuencia.

En la interfaz de usuario, ante cualquier elemento visual derivado de UIElement:

<interactivity:Interaction.Behaviors>
     <behaviors:SwipeBehavior
          SwipeCommand="{Binding SwipeCommand}" />
</interactivity:Interaction.Behaviors>

Sencillo, ¿verdad?

GesturesBehavior

Podéis descargar el sencillo ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información

[Tips and Tricks] Utilizar la extensión Behaviors SDK en una App Universal Windows 10

Introducción

Con la llegada del SDK de Windows 10 Preview tenemos la posibilidad de crear Apps Universales con un único binario
que funcione en múltiples plataformas. En ocasiones desearemos utilizar
caracteristicas específicas de una plataforma. Para ello, utilizaremos
el SDK de extensiones. Haremos clic derecho sobre las referencias del
proyecto, Añadir referencia y accedemos al apartado Extensiones dentro del apartado Universal App Platform:

Anteriormente, en extensiones, teníamos el SDK de Behaviors, ¿dónde esta ahora?.

Behaviors… ¿dónde?, ¿cómo?

Los Behaviors (o comportamientos en Español) nos permiten añadir
lógica directamente en XAML para realizar acciones sin necesidad de
escribir código extra en el code behind.

En proyectos Windows y Windows Phone 8.1, al añadir referencias, teníamos la extensión Behaviors SDK (XAML) dentro del apartado extensiones de Windows 8.1:

El “truco”

Para poder utilizar la extensión Behaviors SDK en proyectos UAP, copiamos los ficheros de la ruta:

C:Program Files (x86)Microsoft SDKsWindowsv8.1ExtensionSDKs

A la ruta:

C:Program Files (x86)Microsoft SDKsUAPv0.8.0.0ExtensionSDKs

Tras reiniciar Visual Studio 2015 y acceder al apartado de extensiones:

Voila!

Más información

[Windows 10] Adaptive Triggers

Introducción

En la plataforma Windows contámos con una gran variedad de
dispositivos, PCs, tabletas, Phablets, teléfonos. Contamos con una gran
cantidad diversa de tamaños de pantalla, resoluciones y otros factores
que como desarrolladores debemos tener en cuenta. El usuario es
exigente, debe serlo. Es vital poder disfrutar de la mejor experiencia posible en cada uno de los dispositivos, y ese, es uno de nuestros retos.

Adaptando la interfaz

Adaptar la interfaz de usuario a distintos tamaños, orientaciones y
otros cambios no es algo nuevo. Con la llegada de las Apps Modern UI en
Windows 8.0 debíamos gestionar la vista Narrow. Con las Apps Universales
en Windows 8.1 desarrollábamos Apps con Windows Phone y Windows en
mente. Ante este tipo de situaciones, hasta ahora hemos utilizado
diferentes técnicas. Las más habituales son:

  • Vistas XAML separadas por plataforma.
  • Diseño flexible. Utilizar posiciones y tamaños relativos para permitir escalar facilmente.
  • Utilizar diferentes estados visuales (Visual States) para gestionar vistas en diferentes dispositivos, pantallas e incluso orientaciones.

Adaptive Triggers

Con la llegada del SDK de Windows 10 Preview tenemos la posibilidad de crear Apps Universales con un único binario
que funcione en múltiples plataformas. Es un paso importante pero que
conlleva realizar una acción que sera comun, diferenciar entre las
diferentes plataformas donde correrá dicho binario para poder adaptar la
interfaz de usuario. Con ese objetivo llegan los Adaptive Triggers. Recibimos potentes novedades en los Visual States:

  • Podemos modificar propiedades sin necesidad de animaciones.
    Anteriormente, la síntaxis era mucho más verbosa y necesitábamos
    utilizar StoryBoards para cambiar cualquier propiedad por simple que
    fuese.
  • Contamos con los StateTrigger. Podemos lanzar Triggers
    cuando se aplica un estado visual sin necesidad de código adyacente en
    el code-behind. El concepto es muy similar a los Triggers que tenemos
    disponible en WPF y Silverlight y el objetivo es el mismo, realizar un
    cambio cuando una condición cambia.

Actualmente contamos con un tipo de StateTrigger por defecto, el Adaptive Trigger que cuenta con los siguientes tipos:

  • MinWindowWidth
  • MinWindowHeight

Ambos nos permiten detectar cambios dinámicos en el tamaño
de la pantalla de modo que se adapte el tamaño de la pantalla entre
pantallas pequeñas y grandes. El funcionamiento es similar a las media
queries en CSS por ejemplo. Se crearán diferentes estados estableciendo
unos altos y anchos mínimos, de modo que, cuando la pantalla es más
grande que el tamaño asignado se activará el estado visual.

NOTA: Los valores se especifican en pixeles.

Para probar las posibilidades de los Adaptive Triggers crearemos un nuevo proyecto UAP:

Nuestro objetivo en el ejemplo serán:

  • Aprender a utilizar AdaptiveTrigger de la forma más sencilla
    posible. Utilizaremos un texto que modificaremos segun ciertas
    condiciones del ancho de la ventana.
  • Crearemos un StateTrigger personalizado.

Añadimos en nuestra infertaz un control para mostrar texto:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
     <TextBlock x:Name="Info"
                HorizontalAlignment="Center"
        VerticalAlignment="Center" />
</Grid>

Sera el control que modificaremos mediante el VisualState:

<!-- Using AdaptiveTrigger -->
<VisualState x:Name="Small">
     <VisualState.StateTriggers>
          <AdaptiveTrigger MinWindowWidth="0" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Pantalla pequeña" />
          <Setter Target="Info.FontSize" Value="32" />
          <Setter Target="Info.Foreground" Value="Red" />
     </VisualState.Setters>
</VisualState>
<VisualState x:Name="Big">
     <VisualState.StateTriggers>
          <AdaptiveTrigger MinWindowWidth="600" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Pantalla grande" />
          <Setter Target="Info.FontSize" Value="48" />
          <Setter Target="Info.Foreground" Value="Blue" />
     </VisualState.Setters>
</VisualState>

Analicemos el trozo de XAML de la parte superior. Hemos definido dos estados visuales donde utilizamos la propiedad MinWindowWidth del StateTrigger AdaptiveTrigger
para aplicar una serie de cambios en las propiedades del control
TextBlock Info. Podemos encadenar tantos Setters como sean necesarios y
no es necesario la gestión por medio de StoryBoards lo que nos permite
definirlo todo de una manera menos verbosa.

Si lanzamos nuestra App y el tamaño de la ventana es superior a 600 px veremos algo como lo siguiente:

Mientras que si reducimos el tamaño:

Muy potente, ¿verdad?. De esta forma podemos hacer distintos cambios
en la organización de elementos, estilos y otros detalles para ajustar
la interfaz de usuario segun el tamaño de la ventana.

Creando Adaptive Triggers personalizados

Hasta ahora hemos aprendido que:

  • Tenemos potentes novedades en los VisualStates.
  • Como gestionar los elementos de la interfaz de usuario para adaptar la interfaz de usuario a cada dispotivo.
  • El uso de Adaptative Triggers para controlar el ancho y/o alto de la pantalla y adaptar la UI.

Los Triggers disponibles son lo suficientemente potentes como para
permitirnos un trabajo correcto adaptando la interfaz de usuario pero…
¿podemos llegar más lejos?, ¿y si no es suficiente?

Si analizamos la clase AdaptiveTrigger utilizada hasta ahora:

Vemos que hereda de una clase base llamada StateTriggerBase disponible dentro del namespace Windows.UI.Xaml.

Podemos crear nuestros propios StateTriggers.

Crearíamos nuevas clases heredando de la clase mencionada. Vamos a
crear un nuevo StateTrigger que nos indique en que plataforma estamos
(Windows o Windows Phone).

Creamos una clase que herede de StateTriggerBase:

public class PlatformStateTrigger : StateTriggerBase
{
 
}

Al igual que en AdaptiveTrigger contamos con dos propiedades, MinWindowWidth y MinWindowHeight que nos permiten lanzar el Trigger dependiendo de condiciones utilizando las mismas.

En nuestro StateTrigger crearemos una propiedad de dependencia llamada Platform que nos permitirá pasar el nombre de la plataforma, de modo que si estamos en dicha plataforma lanzar la condición:

public string Platform
{
     get { return (string)GetValue(PlatformProperty); }
     set { SetValue(PlatformProperty, value); }
}
 
public static readonly DependencyProperty PlatformProperty =
     DependencyProperty.Register("Platform", typeof(string), typeof(PlatformStateTrigger),
     new PropertyMetadata(true, OnPlatformPropertyChanged));
 
private static void OnPlatformPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
 
}

La parte más importante del Trigger es determinar con exactitud la
plataforma en la que se ejecuta la App. Entre las opciones disponibles,
la más sencilla es utilizar ResourceContext:

var qualifiers = ResourceContext.GetForCurrentView().QualifierValues;
_deviceFamily = qualifiers.First(q => q.Key.Equals("DeviceFamily")).Value;

Accedemos al listado de QualifierValues, un IObservableMap con claves
y valor que contiene información como el idioma de la App, el
contraste, el tema utilizado o la familia del dispositivo. Utilizaremos
esta última para determinar si estamos en Desktop o Phone.

De modo que ante el cambio de nuestra propiedad:

private static void OnPlatformPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
     var obj = (PlatformStateTrigger)d;
     var platform = (string)e.NewValue;
 
     if(_deviceFamily.Equals("Desktop"))
    obj.SetTriggerValue(platform == "Windows");
     else
    obj.SetTriggerValue(platform == "WindowsPhone");
}

Nuestro objetivo final es llamar al método SetTriggerValue(bool)
que es el encargado de determinar si la condicion del estado se cumple o
no. Dependiendo del dispositivo lanzamos la condición correcta.

Por último, queda utilizar nuestro StateTrigger personalizado. En nuestra interfaz declaramos antes que nada el namespace donde lo hemos definido:

xmlns:customStateTriggers="using:AdaptiveTriggers.CustomStateTriggers"

Podemos crear VisualStates utilizando PlatformStateTrigger:

<VisualState x:Name="WindowsPhone">
     <VisualState.StateTriggers>
          <customStateTriggers:PlatformStateTrigger Platform="WindowsPhone" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Windows Phone" />
          <Setter Target="Info.FontSize" Value="32" />
          <Setter Target="Info.Foreground" Value="Red" />
     </VisualState.Setters>
</VisualState>
<VisualState x:Name="Windows">
     <VisualState.StateTriggers>
          <customStateTriggers:PlatformStateTrigger Platform="Windows" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Windows" />
          <Setter Target="Info.FontSize" Value="48" />
          <Setter Target="Info.Foreground" Value="Blue" />
     </VisualState.Setters>
</VisualState>

En el caso de estar en un dispositivo Windows Phone se lanzaría el
segundo estado visual, en el de Windows el primero, adaptando la
interfaz de usuario (en nuestro ejemplo un simple TextBlock).

De igual forma podemos crear Adaptive Triggers personalizados para
gestionar aspectos como el tipo de dispositivo, el tipo de orientación,
haciendo verificaciones de condiciones como datos, internet, etc.

Podéis descargar el ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información