[Windows 10] Control SplitView

Snack-BurgerEl 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:

Control SplitView

Control SplitView

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.

SplitView 02

IsPaneOpen es False

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:

SplitView 03

IsPaneOpen es True

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 utilizando MVVM

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

Nueva App UAP

Nueva App 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.
DisplayMode

DisplayMode

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();
     }
}

HamburguerPero… ¿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:

Segoe MDL2 Assets

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:

Nuestra App!

Nuestra App!

Al pulsar el botón Hamburguer, modificamos la propiedad IsPaneOpen de la ViewModel que modificará el estado del SplitView cerrándolo:

SplitView con el Panel colapsado

SplitView con el Panel colapsado

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:

Volver atrás

Volver atrás

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:

Junto a Adaptive Triggers y RelativePanel se puden crear interfaces adaptadas a cada tamaño y dispositivo

Junto a Adaptive Triggers y RelativePanel se puden crear interfaces adaptadas a cada tamaño y dispositivo

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

4 pensamientos en “[Windows 10] Control SplitView

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s