[Windows 10] Trabajando con múltiples ventanas

Introducción

Hay ocasiones en las que contar con más de una ventana en la misma App
añade unas posibilidades increíbles mejorando exponencialmente el uso y
funcionalidad de la misma. Para centrarnos en lo que estamos hablando,
pongamos un ejemplo. El uso de PowerPoint proyectando una presentación.
La audiencia ve la presentación a pantalla completa mientras en otra
pantalla el ponente puede ver más información con más acciones (página
actual, siguiente, número de páginas, etc).

De igual forma, se nos pueden ocurrir cientos de escenarios donde
permitir tener varias ventanas, aprovechar múltiples pantallas, etc. En
este artículo vamos a aprender como permitir abrir más de una ventana de
la misma App y los detalles involucrados en el proceso.

NOTA: En Windows 8.1 ya teníamos la posibilidad de realizar esta acción. Ejemplo de App que hacía uso de la misma, la App de correo.

Múltiples ventanas en App de correo (Windows 8.1)

Múltiples ventanas en App de correo (Windows 8.1)

Múltiples ventanas

Conceptos básicos

Antes de “arrancar” vamos a repasar algunos conceptos
básicos. Cuando abrimos múltiples ventanas en la misma App, cada ventana
se ejecuta en un hilo y contexto diferente. No por ello serán bloques
aislados, podemos permitir la comunicación utilizando Dispatcher.BeginInvoke.

NOTA: Por supuesto también podemos utilizar datos almacenados en el almacenamiento aislado de la App, Roaming, etc.

Comenzamos!

Crearemos un nuevo proyecto UAP:

Nueva App UAP

Nueva App UAP

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. Vamos a permitir crear una vista secundaria desde nuestra vista principal.

Comenzamos definiendo la vista principal:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Create Secondary View"/>
</StackPanel>

Muy simple, ¿cierto?. Tendremos un único botón que nos permitirá crear
la ventana secundaria. Para que el botón ejecute la acción necesitaremos
definir el comando necesario en la ViewModel:

private ICommand _createSecondaryViewCommand;
 
public ICommand CreateSecondaryViewCommand
{
     get { return _createSecondaryViewCommand = _createSecondaryViewCommand ?? new DelegateCommand(CreateSecondaryViewCommandExecute); }
}
 
private async void CreateSecondaryViewCommandExecute()
{
 
}

Bindeamos el comando con el botón:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Create Secondary View"
          Command="{Binding CreateSecondaryViewCommand}"/>
</StackPanel>

El resultado por ahora es:

Primera ventana

Primera ventana

Creando ventana secundaria

Para trabajar con ventanas utilizaremos para diferentes acciones
(switch, cerrar, etc) el identificador de la ventana. Podemos obtener el
identificador de una ventana utilizando el método GetApplicationViewIdForWindow disponible dentro de ApplicationView.

var _mainViewId = ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread());

La línea anterior nos permite obtener el identificador del CoreWindow específico correspondiente a nuestra vista y ventana principal.

Para abrir la nueva ventana lo primero que debemos hacer es… crearla:

var newView = CoreApplication.CreateNewView();

Utilizamos el método CreateNewView disponible en CoreApplication.
Esta línea crea un nuevo hilo y una nueva ventana en ese mismo hilo. La
ventana creada por supuesto no esta visible. La ventana visible es la
principal, creada en el arranque de la App. Es importante tener en
cuenta que, todos los eventos de activación se lanzarán en el hilo
principal, es decir, la ventana principal. Si se cierra la ventana
principal o su hilo de ejecución termina (sea cual sea el motivo) la App
se cerarrá.

Contenido de la ventana secundaria

Llegados a este punto, tenemos nuestra vista principal, pulsamos un
botón y creamos otro hilo donde creamos la ventana secundaria pero…¿que muestra?

Nuestro siguiente paso será indicar el contenido de la ventana secundaria:

var newView = CoreApplication.CreateNewView();
int newViewId = 0;
 
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
     var frame = new Frame();
     frame.Navigate(typeof(SecondaryView), null);
     Window.Current.Content = frame;
     Window.Current.Activate();
 
     newViewId = ApplicationView.GetForCurrentView().Id;
});

Navegamos a una nueva vista e importante, llamamos a la activación (necesario en Windows 10).

NOTA: Resaltar que utilizamos Dispatcher.RunAsync para comunicarnos con la vista, recordar que se usa un hilo diferente.

Mostrando la vista secundaria

Ya tenemos creada nuestra vista secundaria y hasta tiene contenido, nos falta mostrarla. Utilizaremos el método TryShowAsStandaloneAsync disponible en la clase ApplicationViewSwitcher:

var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
                newViewId,
                ViewSizePreference.Default,
                ApplicationView.GetForCurrentView().Id,
                ViewSizePreference.Default);

Le pasamos el identificador de la nueva ventana y muestra la ventana secundaria junto a la ventana principal.

Definición de la vista secundaria

Hemos mostrado una vista secundaria pero nos falta ver su definición:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Switch to Main View"/>
     <Button
          Content="Switch to Main View and Hide"/>
</StackPanel>

Añadiremos dos botones. El primero de ellos nos pasará de la ventana
secundaria a la pantalla principal (cambiará la ventana activa)
manteniendo ambas ventanas mientras que el segundo botón realizará lo
mismo pero cerrando la ventana secundaria programáticamente.

Añadimos los comandos para los botones en la ViewModel:

private ICommand _switchViewCommand;
private ICommand _hideViewCommand;
 
public ICommand SwitchViewCommand
{
     get { return _switchViewCommand = _switchViewCommand ?? new DelegateCommand(SwitchViewCommandExecute); }
}
 
public ICommand HideViewCommand
{
     get { return _hideViewCommand = _hideViewCommand ?? new DelegateCommand(HideViewCommandExecute); }
}
 
private void SwitchViewCommandExecute()
{
 
}
 
private void HideViewCommandExecute()
{
 
}

Bindeamos los comandos correspondientes:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Switch to Main View"
          Command="{Binding SwitchViewCommand}"/>
     <Button
          Content="Switch to Main View and Hide"
          Command="{Binding HideViewCommand}"/>
</StackPanel>

El resultado tras abrir la ventana secundaria:

Ventana secundaria

Ventana secundaria

View Switching

Nos falta por ver un detalle muy importante, el intercambio y gestión entre ventanas.

para hacer el intercambio de una ventana a otra utilizaremos el método SwitchAsync disponible en la clase ApplicationViewSwitcher:

await ApplicationViewSwitcher.SwitchAsync(App.MainViewId);

A la llamada le pasamos como parámetro el identificador de la ventana
que deseamos que reemplace a la actual, en nuestro ejemplo, la ventana
principal.

De igual forma, además de poder hacer el intercambio de la ventana, podemos cerrar una secundaria:

await ApplicationViewSwitcher.SwitchAsync(App.MainViewId,
                ApplicationView.GetForCurrentView().Id,
                ApplicationViewSwitchingOptions.ConsolidateViews);

Aprovechamos otra sobrecarga del método SwitchAsync para
pasar además del identificador de la ventana a la que deseamos hacer el
switch, el identificador de la ventana que deseamos cerrar y el modo en
el que se realiza el intercambio entre ventanas. Para indicar el modo
utilizamos un parámetro de tipo ApplicationViewSwitchingOptions. Es una enumeración con los siguientes valores:

  • Default: Transición estándar entre ventanas.
  • SkipAnimation: Transición inmediata entre ventanas.
  • ConsolidateViews: Cierra la ventana, la quita del listado de usadas recientemente y vuelve a la ventana a la que se realiza el cambio.

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

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

Más información

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *