19 - Control VisualStateManager para adaptar la interfaz a diferentes tamaños de ventana


Una de las características fundamentales en la implementación de aplicaciones universales para windows es que el mismo programa compilado debe ejecutarse en teléfonos, tablets, pc etc.

Como podemos imaginar los tamaños de pantallas son muy distintos entre por ejemplo un teléfono y una pc.

La primer tecnología para enfrentarnos al problema de diferentes tamaños de pantallas se llama "Adaptive Triggers".

Mediante los "Adaptative Triggers" podemos definir secciones en el archivo XAML para modificar el valor de propiedades de los objetos visuales dependiendo del ancho o alto de la ventana del dispositivo.

Por ejemplo podemos implementar una aplicación que si se ejecuta en una pantalla pequeña (teléfono) no se visualice el menú lateral y si ejecutamos la misma aplicación en una pc la misma arranque mostrando el menú lateral.

Problema 1

Confeccionar una aplicación que solo muestre un TextBlock con un mensaje. Cuando se cambia el ancho de la ventana hacer que el también cambie el tamaño de la fuente y el color de fondo de la pantalla. Implementar esto mediante "Adaptative Triggers".

Como primer paso creamos un nuevo proyecto llamado "Proyecto25" seleccionando desde el menú de opciones del Visual Studio: Archivo -> Nuevo -> Proyecto.

El archivo MainPage.xaml es:

<Page
    x:Class="Proyecto25.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Proyecto25"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid x:Name="grilla1" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="telefono">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="grilla1.Background" Value="red" />
                        <Setter Target="mensaje1.FontSize" Value="40" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="tablet">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1024" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="grilla1.Background" Value="green" />
                        <Setter Target="mensaje1.FontSize" Value="80" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="pc">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1280" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="grilla1.Background" Value="blue" />
                        <Setter Target="mensaje1.FontSize" Value="120" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <TextBlock x:Name="mensaje1" Text="Texto de prueba" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Page>

Si ejecutamos este programa y vamos cambiando el tamaño de la ventana veremos como cuando es muy estrecha aparece de color rojo, al ensancharla vemos que pasa a color verde y si seguimos llega a color azul (lo mismo pasa con el tamaño de la fuente):

VisualStateManager
VisualStateManager
VisualStateManager

Como vemos nuestra aplicación solo muestra en pantalla un TextBlock con un mensaje indicado en la propiedad Text, hemos definido su nombre en la propiedad Name:

        <TextBlock x:Name="mensaje1" Text="Texto de prueba" HorizontalAlignment="Center" VerticalAlignment="Center" />

El contenido visual del Grid es solo el TextBlock, el resto de código es necesario para implementar la captura de los distintos tamaño de la pantalla y los cambios a producir sobre las propiedades de los objetos visuales.

Para implementar el concepto de "Adaptative Triggers" debemos definir las etiquetas XAML:

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>

            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

Y dentro de VisualStateGroup los distintos VisualState indicando en VisualState.StateTriggers el tamaño mínimo de ancho del dispositivo y en VisualState.Setters las propiedades de los objetos a modificar con el valor indicado en la propiedad "Value":

                <VisualState x:Name="telefono">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="grilla1.Background" Value="red" />
                        <Setter Target="mensaje1.FontSize" Value="40" />
                    </VisualState.Setters>
                </VisualState>

Es decir que la asignación de estos valores sucede cuando el ancho en píxeles del dispositivo está entre los valores 0 y 1024 (que es el valor del segundo VisualState).

De forma similar definimos los otros dos VisualState para tamaños mayores de pantalla:

                <VisualState x:Name="tablet">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1024" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="grilla1.Background" Value="green" />
                        <Setter Target="mensaje1.FontSize" Value="80" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="pc">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1280" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="grilla1.Background" Value="blue" />
                        <Setter Target="mensaje1.FontSize" Value="120" />
                    </VisualState.Setters>
                </VisualState>

Estamos indicando si el tamaño del ancho de la pantalla es de 1280 píxeles o mayor se debe asignar a la propiedad Background del objeto grilla1 el valor "blue". El tamaño de fuente el objeto mensaje1 luego debe ser de 120.

Los "Adaptative Triggers" nos dan una gran flexibilidad a la hora de adaptar la pantalla según el ancho o alto de la misma.

Este proyecto lo puede descargar en un zip desde este enlace :Proyecto25.zip

Problema 2

Confeccionar una aplicación que solo muestre un menú lateral parcial para un celular y en forma expandida para pc. Implementar esto mediante "Adaptative Triggers".

Como primer paso creamos un nuevo proyecto llamado "Proyecto26" seleccionando desde el menú de opciones del Visual Studio: Archivo -> Nuevo -> Proyecto.

El archivo MainPage.xaml es:

<Page
    x:Class="Proyecto26.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Proyecto26"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="VisualStateGroup">
                <VisualState x:Name="telefono">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="splitView1.IsPaneOpen" Value="false" />
                        <Setter Target="mensaje.FontSize" Value="25" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="pc">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1280" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="splitView1.IsPaneOpen" Value="true" />
                        <Setter Target="mensaje.FontSize" Value="20" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <SplitView  x:Name="splitView1" 
                DisplayMode="CompactInline"
                IsPaneOpen="false"
                CompactPaneLength="50" 
                OpenPaneLength="200">
            <SplitView.Pane>
                <StackPanel Background="LightGray">
                    <Button x:Name="botonMenu" 
                        FontFamily="Segoe MDL2 Assets" 
                        Content="&#xE700;"
                        Width="50" 
                        Height="50" 
                        Background="Transparent"
                        Click="botonMenu_Click"
                        />
                    <StackPanel Orientation="Horizontal">
                        <Button x:Name="boton1" 
                            FontFamily="Segoe MDL2 Assets" 
                            Content="&#xE825;"
                            Width="50" 
                            Height="50" 
                            Background="Transparent" 
                            />
                        <TextBlock Text="Descripción 1" 
                               FontSize="18" 
                               VerticalAlignment="Center" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Button x:Name="boton2" 
                            FontFamily="Segoe MDL2 Assets" 
                            Content="&#xE11A;"
                            Width="50"
                            Height="50" 
                            Background="Transparent" 
                            />
                        <TextBlock Text="Descripción 2"
                               FontSize="18"
                               VerticalAlignment="Center"  />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Button x:Name="boton3" 
                            FontFamily="Segoe MDL2 Assets"
                            Content="&#xE115;"
                            Width="50" 
                            Height="50"
                            Background="Transparent"
                            />
                        <TextBlock Text="Descripción 3"
                               FontSize="18"
                               VerticalAlignment="Center" />
                    </StackPanel>
                </StackPanel>
            </SplitView.Pane>
            <SplitView.Content>
                <TextBlock x:Name="mensaje" Text="Contenido que se muestra"  />
            </SplitView.Content>
        </SplitView>
    </Grid>
</Page>

El menú lateral lo vimos en conceptos anteriores. Ahora tenemos que hacer que se muestre contraído cuando la interfaz visual es menor de 1280 píxeles, esto lo hacemos iniciando la propiedad IsPaneOpen del splitView1 con el valor false:

                <VisualState x:Name="telefono">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="splitView1.IsPaneOpen" Value="false" />
                        <Setter Target="mensaje.FontSize" Value="25" />
                    </VisualState.Setters>
                </VisualState>

Si el ancho de la ventana del dispositivo es de 1280 píxeles o más luego se ejecuta el bloque:

                <VisualState x:Name="pc">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1280" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="splitView1.IsPaneOpen" Value="true" />
                        <Setter Target="mensaje.FontSize" Value="20" />
                    </VisualState.Setters>
                </VisualState>

El código del archivo MainPage.xaml.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// La plantilla de elemento Página en blanco está documentada en http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace Proyecto26
{
    /// 
    /// Página vacía que se puede usar de forma independiente o a la que se puede navegar dentro de un objeto Frame.
    /// 
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void botonMenu_Click(object sender, RoutedEventArgs e)
        {
            if (splitView1.IsPaneOpen == true)
                splitView1.IsPaneOpen = false;
            else
                splitView1.IsPaneOpen = true;
        }
    }
}

Cuando ejecutamos la aplicación vemos que en un dispositivo con un ancho de menos de 1280 píxeles tenemos el siguiente resultado:

VisualStateManager

Cuando ejecutamos la aplicación un ancho de 1280 píxeles o más tenemos el siguiente resultado:

VisualStateManager

Este proyecto lo puede descargar en un zip desde este enlace :Proyecto26.zip

Retornar