Empezando con Windows Phone 7.1 (y XIII) – Creando una nueva vista. Navegación entre páginas.

¡El horror, oh el horror!

En el anterior post nos quedamos con una forma de crear un nuevo lugar un tanto deleznable.

image

Así pues, creo que lo mejor es hacer que la nueva interfaz aproveche la barra de aplicación de Windows Phone.

Aplicando algunos cambios.

Para ello, he realizado algunos cambios en la aplicación. He creado una carpeta en la solución donde podré organizar más fácilmente las páginas y he movido la página principal a dicha carpeta.

image

Esto me ha obligado a realizar algunos cambios menores en el código.

En el manifiesto de la aplilcación

image

En el cual se indica la página de inicio para la aplicación, he cambiado la ruta.

 1: <Tasks>

 2:     <DefaultTask

 3:         Name="_default"

 4:         NavigationPage="/Pages/MainPage.xaml" />

 5: </Tasks>

Y, en la propia página, he cambiado la url relativa a las imágenes.

 1: <controls:Panorama.Background>

 2:     <ImageBrush

 3:         ImageSource="../Resources/images/Panorama%20Green.png"/>

 4: </controls:Panorama.Background>

Ahora vamos a realizar algunos cambios en la página principal. De momento eliminaremos el PanoramaItem que estaba destinado a contener los controles para crear un nuevo lugar y añadiremos la posibilidad de crear un nuevo lugar desde cualquier punto de la página principal. Para ello, vamos a crear una ApplicationBar en la página principal.

 1: <phone:PhoneApplicationPage.ApplicationBar>

 2:     <shell:ApplicationBar

 3:             Mode="Minimized"

 4:             IsMenuEnabled="False"

 5:             Opacity="0.7">

 6:

 7:         <shell:ApplicationBarIconButton

 8:             x:Name="NuevoLugarButton"

 9:             IconUri="/Resources/icons/Add%20New%20Place.png"

 10:             Text="Nuevo Lugar" />

 11:     </shell:ApplicationBar>

 12: </phone:PhoneApplicationPage.ApplicationBar>

Esto nos permite disponer de una barra de aplicaciones que no siempre estará visible, de esta forma aprovecharemos al máximo el espacio vertical.

Asignando Acciones a los botones de la ApplicationBar

Maldición. El primer problema que me he encontrado es. ¿Cómo puedo utilizar Comandos con los botones de la Application Bar para aprovecharme del patrón visto antes en MVVM? El control dispone de una propiedad Click que te permite asignar código en el “Code Behind” de la Vista, pero no de la VistaModelo como vimos en el anterior post (mediante Binding).

El otro problema es. ¿Cómo puedo asignar al texto del botón una entrada de mi fichero de recursos? Ni con Visual Studio ni con Blend me permite hacer uso de mi clase para manejar los recursos de la aplicación.

Proporcionando soporte multi-idioma y comandos en un ApplicationBarIconButton

¿Queréis la respuesta rápida?

No es posible sin cierta cantidad de esfuerzo ya que la barra de aplicación no es un componente Silverlight. Se trata de un objeto controlado por el Shell de Windows Phone, con lo que no aplican las mismas reglas que a cualquier otro control que incluyas en tu aplicación.

Confused smile

Resumiento la situación actual

En fin, algo que parecía bastante trivial se está convirtiendo en un pequeño problema:

  1. La barra de aplicación no es un control Silverlight, con lo que trabajar con la misma haciendo uso del patrón MVVM resulta complicado (recordad que la idea es evitar, en la medida de lo posible) tocar el Code Behind de la página.
  2. La navegación se realiza mediante la clase NavigationService que está accesible desde la propia págia como una propiedad.
  3. Desde la VistaModelo deberíamos de evitar instanciar nuevas clases que nos introduzcan dependencias (si tienes la palabra clave “new” en tu código lo mejor que puedes hacer es revisar el código). Un patrón como IoC viene aquí que ni al pelo.
  4. Crear una nueva vista y asociar correctamente su VistaModelo de forma flexible.

Siguientes Pasos

Si bien es perfectamente posible realizar todas las tareas descritas en el punto anterior (con mayor o menor esfuerzo). Este es un buen momento para hacer un alto en el camino y revisar algunas opciones que nos ayudarán con el Modelo Vista Vista Modelo y proseguir con el desarrollo apoyándonos en alguno de los siguientes Frameworks.

Anuncios

Empezando con Windows Phone (y XII) – Iconos, Botones y Metro Studio

¿Para qué?

Llegado a este punto, me queda asignar cierta lógica a la interfaz de alta de nuevos lugares. Actualmente (por poco tiempo, espero) se encuentra dentro del ModeloVista de la página principal, en breve se convertirá en una página aparte (momento en que aprovecharé para trastear un poco con la navegación).

Asignando Comandos a un Botón (al estilo MVVM)

La interfaz de alta de nuevos lugares es muy sencilla:

  1. Un bloque de texto que indica que este es el sitio de tu aplicación preparado para el alta.
  2. Un cuadro de texto encargado de recoger el nombre del nuevo lugar.
  3. Un botón para crear el nuevo lugar.

Para el botón, simplemente he enlazado el Comando al creado en la VistaModelo y, como parámetro, el texto de la caja de texto que se utiliza para crear el nombre del nuevo lugar. Así:

 1: <Button

 2:     Command="{Binding Path=CrearNuevoLugar}"

 3:     CommandParameter="{Binding ElementName=NuevoLugarTextBox, Path=Text}"

 4:

 5:     Margin="0,0,0,0"

 6:     x:Name="NuevoLugarBtn"

 7:     Grid.Row="2"

 8:     Content="{Binding LocalizedResources.TituloBotonNuevoLugar, Source={StaticResource LocalizationHelper}}"

 9:     ClickMode="Press"

 10:     Background="{StaticResource PhoneBackgroundBrush}"

 11:     BorderBrush="{StaticResource PhoneBorderBrush}"

 12:     Foreground="{StaticResource PhoneForegroundBrush}"/>

El “Binding” está realizado contra la propiedad “CrearNuevoLugar” que está expuesta en la vista.

 1: public class MainViewModel : IMainViewModel

 2: {

 3:     public MainViewModel()

 4:     {

 5:         _comandoCrearLugar = new DelegateCommand(AccionCrearNuevoLugar, PuedeCrearNuevoLugar);

 6:         _nombreNuevoLugar = Resources.MisSitiosFavoritosResources.TextoNuevoLugar;

 7:

 8:         ActualizarVistas();

 9:     }

 10:

 11:     public ICommand CrearNuevoLugar

 12:     {

 13:         get { return _comandoCrearLugar; }

 14:     }

 28: }

Por último, el comando delega la funcionalidad en dos métodos de la propia vista (AccionCrearNuevoLugar y PuedeCrearNuevoLugar). El primero se encarga de crear un nuevo lugar, el segundo se utiliza para determinar si dicha acción puede llevarse o no a cabo.

 1: private void AccionCrearNuevoLugar(object nuevo)

 2: {

 3:     if (nuevo == null)

 4:         throw new ArgumentException("nuevo");

 5:

 6:     var sNuevo = nuevo.ToString();

 7:     MiLugar nuevoLugar = Logica.NuevoLugar(sNuevo, string.Empty);

 8: }

 9:

 10: private bool PuedeCrearNuevoLugar(object nuevo)

 11: {

 12:     var result = false;

 13:     if (nuevo != null)

 14:     {

 15:         string sNuevo = nuevo.ToString();

 16:

 17:         result = !String.IsNullOrEmpty(sNuevo) && (String.Compare(sNuevo, Resources.MisSitiosFavoritosResources.TextoNuevoLugar) != 0);

 18:     }

 19:

 20:     return result;

 21: }

Ambos métodos recogen como parámetro el nombre del nuevo lugar. Uno de ellos se limita a crear una instancia del nuevo lugar, el otro comprueba el nombre (asegurándose de que no está vacío y que, además, no es la cadena de texto por defecto que se muestra en el control).

Lo bonito de esta aproximación es lo siguiente:

  1. Nada de código en un manejador de eventos que se dispara cuando se presiona el botón.
  2. De nuevo el Modelo-Vista no sabe absolutabmente nada de la vista y, con la ayuda de la clase DelgateCommand la lógica correspondiente al comando sigue dentro del Modelo-Vista.

Bueno, no mucho más que decir. Ahora a crear el botón.

Creando un botón (feo) y otro (menos feo) con Metro Studio

Ha llegado el momento de tener lista la interfaz para dar de alta un nuevo lugar (en este momento no está separada de la vista principal, pero en breve lo estará).

El primer intento consistió en añadir un simple botón con un texto en su interior.

image

Como podéis ver, se trata de un horrible botón que me permite dar de alta un nuevo lugar de forma bastante sencilla utilizando DataBinding.

En esta primera versión voy a reemplazar dicho botón por otro con un estilo visual más cercano a Metro. Para ello voy a utilizar una herramienta gratuita de Syncfusion llamada Metro Studio.

Metro Studio es una colección de iconos completamente gratuitos que siguen las normas de estilo y diseño de la interfaz Metro (la propia interfaz de la herramienta sigue dichas normas). Es muy sencilla de utilizar.

image

Simplemente busca iconos en el buscador o utilizando las categorías.

image

Selecciona tu icono (en mi caso, un original “Save”) y edítalo haciendo uso de una herramienta muy sencilla.

image

Finalmente puedes optar por almacenar el XAML generado por la herramienta.

image

O bien, obtener una imagen de la misma en tu formato preferido.

image

El resultado: algo igual de feo y que habrá que cambiar: Crear una nueva vista con su página correspondiente y, probablemente, una barra de herramientas con la botonera correspondiente.

image

 1: <controls:PanoramaItem Header="{Binding LocalizedResources.TituloNuevoSitio, Source={StaticResource LocalizationHelper}}" Orientation="Vertical" Foreground="{StaticResource PhoneBackgroundBrush}">

 2:     <Grid x:Name="LayoutForm" Background="Transparent">

 3:         <Grid.RowDefinitions>

 4:             <RowDefinition Height="Auto"/>

 5:             <RowDefinition Height="Auto" />

 6:             <RowDefinition Height="Auto"/>

 7:         </Grid.RowDefinitions>

 8:

 9:         <TextBlock Height="45" Margin="0,0,0,0" TextWrapping="Wrap" Text="{Binding LocalizedResources.TituloTextoNuevoLugar, Source={StaticResource LocalizationHelper}}" VerticalAlignment="Top" Width="400" Grid.Row="0" />

 10:         <TextBox Margin="0,0,0,0" x:Name="NuevoLugarTextBox" Text="{Binding Path=NombreNuevoLugar}" Style="{StaticResource LugarTextBox}" Padding="0,0,0,0" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.Row="1" />

 11:         <Button Margin="0,0,0,0" x:Name="NuevoLugarBtn" Command="{Binding Path=CrearNuevoLugar}" CommandParameter="{Binding ElementName=NuevoLugarTextBox, Path=Text}" Grid.Row="2" ClickMode="Press" Height="96" Width="96" HorizontalAlignment="Right">

 12:             <Button.Background>

 13:                 <ImageBrush ImageSource="Resources/images/SaveButton.png"/>

 14:             </Button.Background>

 15:         </Button>

 16:     </Grid>

 17: </controls:PanoramaItem>

Eso sí, tendrá que ser en el próximo post.

Winking smile