[Xamarin.Forms] Bottom TabbedPage para Android

Xamarin.Forms 3.1

Con la llegada de Xamarin.Forms 3.1 llegan una gran cantidad de mejoras y correcciones (es genial ver que muchas vienen de la comunidad). Entre el conjunto de novedades tenemos la posibilidad de crear TabbedPage en la parte inferior en Android.

En este artículo, vamos a ver como crear una Bottom TabbedPage.

Bottom TabbedPage

Podemos crear una TabbedPage con posición en la parte inferior de forma muy sencilla en Android utilizando platform specific:

On<Xamarin.Forms.PlatformConfiguration.Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);

El resultado:

Bottom TabbedPage

Tienes un ejemplo disponible en GitHub:

Ver GitHub

Más información

[Xamarin.Forms] Utilizando LiteDB

Database-WFIntroducción

El trabajo con datos en dispositivos móviles se ha convertido ya en algo común y habitual en el desarrollo de aplicaciones. Existe una gran variedad de tipos de datos y formas de almacenamiento:

  • Archivos de texto. Texto plano o html cacheado en el espacio de almacenamiento aislado de la aplicación.
  • Imágenes. En el espacio de almacenamiento aislado de la aplicación o almacenadas en directorios conocidos del sistema.
  • Archivos serializados. Archivos XML o Json con objetos serializados.
  • Bases de datos. Cuando se requieren datos estructurados, obtener información más compleja con consultas avanzadas entre otro tipo de necesidades, la posibilidad de las bases de datos es la elección idónea.

En este artículo, vamos a conocer LiteDB y como utilizarlo con una aplicación Xamarin.Forms.

Recuerda

El mismo ejemplo que vemos en este artículo lo hemos visto previamente con:

Introducción a LiteDB

LiteDB es una base de da motor de base de datos Open Source  distribuido en una pequeña librería escrita en C# y compatible con .NET y .NET Standard. Inspirada en MongoDB almacena documentos BSON (Binary JSON).

Al ser compatible con .NET Standard 2.0, podemos utilizarla con Xamarin.iOS, Xamarin.Android y UWP con aplicaciones Xamarin.Forms.

Preparando el entorno

Comenzamos creando una aplicación Xamarin.Forms utilizando una librería .NET Standard:

Xamarin.Forms con librería .NET Standard

Tras crear la aplicación, añadimos las carpetas básicas para aplicar el patrón MVVM además del paquete NuGet de Autofac para la gestión del contenedor de dependencias.

Estructura del proyecto

Con el proyecto y estructura base creada, vamos a añadir LitrDB al proyecto. LiteDB esta disponible en NuGet. Vamos a añadir en cada proyecto de la solución la última versión disponible del paquete utilizando NuGet. El paquete a utilizar es LiteDB, implementación Open Source compatible con librerías .NET Standard.

LiteDB

Tras añadir la referencia vamos a crear una interfaz que defina como obtener la conexión con la base de datos y abstraer la funcionalidad específica de cada plataforma. Trabajando con LiteDB, el único trabajo específico a implementar en cada plataforma es determinar la ruta a la base de datos y establecer la conexión.

public interface IPathService
{
     string GetDatabasePath();
}

En Android, la implementación de IPathService nos permite establecer la conexión con la base de datos.

public class PathService : IPathService
{
     public string GetDatabasePath()
     {
          var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), AppSettings.DatabaseName);
          if (!File.Exists(path))
          {
               File.Create(path).Dispose();
          }
          return path;
     }
}

NOTA: Utilizamos el atributo assembly:Dependency para poder realizar la resolución de la implementación con DependencyService.

En iOS, la implementación de IPathService nos permite establecer la conexión con la base de datos. El archivo de la base de datos lo situamos dentro de la carpeta Library dentro del espacio de almacenamiento de la aplicación.

public class PathService : IPathService
{
     public string GetDatabasePath()
     {
          string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
          string libFolder = Path.Combine(docFolder, "..", "Library", "Databases");
          if (!Directory.Exists(libFolder))
          {
               Directory.CreateDirectory(libFolder);
          }
          return Path.Combine(libFolder, AppSettings.DatabaseName);
     }
}

Y por último en UWP:

public class PathService : IPathService
{
     public string GetDatabasePath()
     {
          return Path.Combine(ApplicationData.Current.LocalFolder.Path, AppSettings.DatabaseName);
     }
}

Todo listo para comenzar!

La definición de modelos

En nuestra aplicación, trabajaremos con elementos del listado ToDo, una única entidad sencilla.

public class TodoItem
{
     public int Id { get; set; }
     public string Name { get; set; }
     public string Notes { get; set; }
     public bool Done { get; set; }
}

La interfaz de usuario

En nuestra aplicación contaremos con dos vistas, un listado de tareas y una vista de detalles para crear, editar o eliminar una tarea específica.

Comenzamos definiendo la vista principal. Tendremos un listado de tareas:

<ListView ItemsSource="{Binding Items}"
     SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
     <ListView.ItemTemplate>
          <DataTemplate>
               <ViewCell>
                    <ViewCell.View>
                         <StackLayout Padding="20,0,20,0"
                              Orientation="Horizontal"
                              HorizontalOptions="FillAndExpand">
                              <Label Text="{Binding Name}"
                                   VerticalTextAlignment="Center"
                                   HorizontalOptions="StartAndExpand" />
                              <Image HorizontalOptions="End"
                                   IsVisible="{Binding Done}">
                                   <Image.Source>
                                        <OnPlatform x:TypeArguments="ImageSource">
                                             <On Platform="Android, iOS"
                                                  Value="check" />
                                             <On Platform="UWP"
                                                  Value="Assets/check.png" />
                                        </OnPlatform>
                                   </Image.Source>
                              </Image>
                         </StackLayout>
                    </ViewCell.View>
               </ViewCell>
          </DataTemplate>
     </ListView.ItemTemplate>
</ListView>

A parte de definir como se visualizará cada elemento de la lista definiendo el DataTemplate establecemos la fuente de información, propiedad ItemsSource enlazada a propiedad de la ViewModel que obtendrá los datos de la base de datos.

Además del listado, debemos añadir en nuestra interfaz una forma de poder insertar nuevas tareas. Para ello, una de las opciones más habituales e idóneas es utilizar una Toolbar.

<ContentPage.ToolbarItems>
     <ToolbarItem Name="Add"
          Command="{Binding AddCommand}">
          <ToolbarItem.Icon>
                <OnPlatform x:TypeArguments="FileImageSource">
                     <On Platform="Android, iOS"
                         Value="plus" />
                     <On Platform="UWP"
                         Value="Assets/plus.png" />
                </OnPlatform>
          </ToolbarItem.Icon>
     </ToolbarItem>
</ContentPage.ToolbarItems>

Añadimos un ToolbarItem que permitirá añadir elementos.

La clase Device es muy importante en Xamarin.Forms ya que nos permite acceder a una serie de propiedades y métodos con el objetivo de personalizar la aplicación según dispositivo y plataforma. Además de permitirnos detectar el tipo de dispositivo, podemos detectar la plataforma gracias a la enumeración Device.OS o personalizar elementos de la interfaz gracias al método Device.OnPlatform entre otras opciones. En nuestro ejemplo, personalizamos el icono de añadir en base a la plataforma.

Nuestra interfaz:

Vista principal
Vista principal

Enlazamos la View con la ViewModel estableciendo una instancia de la ViewModel a la propiedad BindingContext de la página.

BindingContext = App.Locator.TodoListViewModel;

En la ViewModel contaremos con una propiedad pública para definir el listado de tareas, además de la tarea seleccionada (utilizada para la navegación):

private ObservableCollection<TodoItem> _items;
private TodoItem _selectedItem;

public ObservableCollection<TodoItem> Items
{
     get { return _items; }
     set
     {
          _items = value;
          OnPropertyChanged();
     }
}
public TodoItem SelectedItem
{
     get { return _selectedItem; }
     set
     {
          _selectedItem = value;
          OnPropertyChanged();
     }
}

Añadimos elementos con un comando disponible en la ViewModel.

private ICommand _addCommand;

public ICommand AddCommand
{
     get { return _addCommand = _addCommand ?? new Command(AddCommandExecute); }
}

private void AddCommandExecute()
{
}

Al pulsar y lanzar el comando, navegaremos a la vista de detalles.

var todoItem = new TodoItem();
_navigationService.NavigateTo<TodoItemViewModel>(todoItem);

Si creamos un nuevo elemento pasaremos como parámetro una nueva entidad de TodoItem, en caso de seleccionar una existente, pasaremos el seleccionado disponible en la propiedad SelectedItem.

Definimos la interfaz de la vista de detalles:

<StackLayout VerticalOptions="StartAndExpand"
     Padding="20">
     <Label Text="Name" />
     <Entry Text="{Binding Item.Name}" />
     <Label Text="Notes" />
     <Entry Text="{Binding Item.Notes}" />
     <Label Text="Done" />
     <Switch x:Name="DoneEntry"
          IsToggled="{Binding Item.Done}" />
     <Button Text="Save"
          Command="{Binding SaveCommand}" />
     <Button Text="Delete"
          Command="{Binding DeleteCommand}" />
     <Button Text="Cancel"
          Command="{Binding CancelCommand}" />
</StackLayout>

Añadimos cajas de texto para poder editar toda la información de una tarea además de botones para poder guardar, borrar o cancelar y navegar atrás.

El resultado:

Detalle
Detalle

Para enlazar la información de un elemento seleccionado, debemos capturar la información enviada en la navegación. Creamos una propiedad pública para enlazar con la UI de la tarea:

private TodoItem _item;

public TodoItem Item
{
     get { return _item; }
     set
     {
          _item = value;
          OnPropertyChanged();
     }
}

¿Cómo capturamos el elemento seleccionado en la navegación?. Utilizamos el método OnAppearing para capturar el parámetro NavigationContext.

public override void OnAppearing(object navigationContext)
{
     if (navigationContext is TodoItem todoItem)
     {
          Item = todoItem;
     }

     base.OnAppearing(navigationContext);
}

En cuanto a cada botón, cada uno de ellos estará enlazado a un comando:

Trabajando con LiteDB

Para trabajar con la base de datos utilizaremos DependencyService para obtener la implementación de IPathService y obtener una conexión.

var db = new LiteDatabase(DependencyService.Get<IPathService>().GetDatabasePath());

Para almacenar nuestras tareas, comenzamos creando la colección necesaria en la base de datos.

_collection = db.GetCollection<TodoItem>();

Si la colección no existe en la base de datos, es creada.

Cada elemento guardado en LiteDB cuenta con una propiedad Id utilizado para identificarlo de manera única. Debeemos indicarle a LiteDB qué propiedad de nuestro objeto actuará como identificador. Podemos hacerlo usando atributos o un mapeador. Aquí utilizaremos Mappers.

var mapper = BsonMapper.Global;

mapper.Entity<TodoItem>()
.Id(x => x.Id);

Para obtener el listado de elementos de una colección utilizaremos el método FindAll:

var all = _collection.FindAll();

A la hora de insertar, verificamos si estamos ante un registro existente o no, para realizar el registro de un nuevo elemento o actualizar uno existente con los métodos Insert o Update respectivamente.

var existingTodoItem = _collection.FindById(item.Id);

if (existingTodoItem == null)
_collection.Insert(item);
else
_collection.Update(item);

Eliminar es una acción sencilla realizada con el método Delete.

_collection.Delete(i => i.Id.Equals(item.Id));

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

[Xamarin.Forms] Novedades utilizando Resource Dictionaries

Resource Dictionaries

En toda aplicación móvil la apariencia visual es vital. Cada vez es mayor el esfuerzo depositado a la hora de crear aplicaciones atractivas a la par que intuitivas y en muchos casos conseguir una imagen única que diferencia a la Aplicación del resto es prioritario. Por este motivo, debemos de contar con opciones sencillas de poder personalizar los distintos elementos que componen la interfaz.

Los estilos permitir definir múltiples propiedades visuales de elementos de la interfaz de forma reutilizable.

Los recursos XAML son objetos que podemos reutilizar más de una vez. Hablamos desde un sencillo color o tamaño de fuente, a el uso de estilos. Los diccionarios de recursos o ResourceDictionaries permiten definir una zona donde definir recursos.

Hasta ahora…

Podíamos definir diccionarios de recursos en archivos totalmente independientes. Hablamos de una clase derivada de ResourceDictionary, que consta de un par de archivos. Por un lado tenemos el archivo XAML donde se definen los recursos y por otro lado, la clase subyacente encarga de realizar InitializeComponent.

NOTA: Para crear un archivo de tipo ResourceDictionary, crea una ContentPage o ContentView y renombra la clase base de ContentPage o ContentView a ResourceDictionary.

Veamos un ejemplo.

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
     x:Class="ResourceDictionaries.MyResourceDictionary">

     <Style x:Key="LabelStyle"
          TargetType="Label">
          <Setter Property="TextColor"
               Value="Red" />
          <Setter Property="FontSize"
               Value="24" />
</Style>

</ResourceDictionary>

NOTA: Fíjate que utilizamos x:Class para establecer la clase subyacente necesaria.

Para utilizar el diccionario de recursos, creamos una instancia:

<ContentPage.Resources>
     <local:MyResourceDictionary />
</ContentPage.Resources>

La forma anterior de añadir el diccionario de recursos a pesar de ser válida es muy limitada ya que sólo nos permite utilizar los recursos del diccionario instanciado. Podríamos necesitar utilizar mñas de un diccionario de recursos o añadir otros recursos.

Para solucionar este problema, tenemos disponible la posibilidad de utilizar diccionarios de recursos combinados.

<ContentPage.Resources>
     <ResourceDictionary>
          <ResourceDictionary.MergedDictionaries>
               <local:MyResourceDictionary />
          </ResourceDictionary.MergedDictionaries>
     </ResourceDictionary>
</ContentPage.Resources>

Podemos combinar varias instancias de diccionarios de recursos en uno existente. Se puede realizar directamente desde XAML utilizando la propiedad MergedDictionaries.

Con la llegada de Xamarin.Forms 3.0

La combinación de ResourceDictionary se simplifica con la llegada de Xamarin.Forms 3.0. Por un lado, ya no es necesario utlizar las etiquetas MergedDictionaries ya que en su lugar, podemos agregar directamente otro ResourceDictionary utilizando la propiedad Source.

Además, Xamarin.Forms 3.0 crea automáticamente una instancia de ResourceDictionary por lo que ya no es necesario añadir estas etiquetas.

<ContentPage.Resources>
     <ResourceDictionary Source="MyResourceDictionary.xaml" />
</ContentPage.Resources>

Esta nueva sintaxis no crea una instancia del diccionario de recursos. Gracias a este cambio, ya no es necesario mantener la clase subyacente o code behind del diccionario de recursos (MyResourceDictionary.xaml.cs) por lo que podemos borrarlo además del atributo x:Class de la ráiz del diccionario.

Más simple y sencillo!

Puedes encontrar un ejemplo en GitHub:

Ver GitHub

Más información

[Material DotNet2018] Xamarin.Forms Everywhere

El evento

El pasado 29 de Mayo tenía lugar la DotNet2018. Un gran día repleto de sesiones técnicas cubriendo todo el espectro de tecnologías .NET, desde Xamarin, Azure, IoT, VR, etc.

Fue un día genial de grandes sesiones pero también de encontrarte con grandes amigos, desvirtualizar a nuevos amigos, networking, etc. Quisiera agradecer a todos los ponentes y asistentes por hacer posible el evento. Y por supuesto, mi enhorabuena a toda la organización por hacer que algo tan grande y complejo fluyese de forma tan natural y sencilla.

El material

He tenido en placer de poder participar en el evento con una sesión acerca de Xamarin.Formsy nuevas plataformas o posibilidades.

Comenzamos el evento revisando Xamarin.Forms en Linux:

Xamarin.Forms en Linux

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Continuamos viendo estado y opciones de Xamarin.Forms en WPF:

Xamarin.Forms WPF

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Y continuamos con nuevos backends de Xamarin.Forms, y en esta ocasión hablamos de Ooui de Frank Krueger.

Desde el navegador!

Repasamos estado, un poco de WebAssembly y continuamos. Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHubContinuamos viendo los primeros pasos del backend de Windows Forms y dimos paso a nuevas posibilidades.

Hablamos de wearables y Tizen.CircularUI, donde repasamos opciones principales, etc.

Posteriormente llegço el turno de IoT. Tras una breve introducción a Xamarin.IoT vimos como ya contamos con plantillas de proyectos e integración entre Xamarin.Forms (backend GTK#) con Xamarin.IoT.

Monitor de temperatura realizado con Xamarin.IoT y Xamarin.Forms

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Tras ver nuevas plataformas o dispositivos con IoT o Wearables, nos centramos en nuevas posibilidades en UI. Hicimos un repaso de:

  • VisualStateManager
  • FexLayout
  • SkiaSharp
  • Etc

En este punto, vimos como crear interfaces combinadas con elementos 3Dcon Wave Engineo como combinar ARCorecon Xamarin.Forms creando Custom Renderers.

ARCore

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Completamos el evento utilizando Xamarin.Forms para crear la UI de addins para Visual Studioen Windows y macOS.

El ejemplo:

Ver GitHub

Hasta el año próximo!

Y ya tenemos evento confirmado para el próximo año!. Apunta la fecha, 23 de Mayo.

DotNet 2019!

Allí nos vemos!

Más información

Primer vistazo a Xamarin Live Reload

Introducción

Xamarin.Forms es un toolkit que crea una abstracción sobre la interfaz de usuariode Android, iOS, Tize, WPF, macOS, Linux y Windows permitiendo desarrollarla una única vez con códigoC#o Extensible Application Markup Language(XAML).

A la hora de trabajar con la interfaz de usuario, tenemos grandes herramientas como IntelliSense en XAML, Previewer o Xamarin Live Player. Sin embargo, las dos últimas opciones no soportan todas las características que se pueden utilizar en una aplicación móvil lo que provoca errores al renderizar y previsualizar el contenido. Por este motivo, tenemos algunas grandes herramientas por parte de la comunidad Xamarin comoLive XAMLo Gorilla Player.

En este artículo, vamos a conocer Xamarin Live Reload, nueva herramienta oficial que nos permite ver cualquier cambio de XAML al vuelo.

Xamarin Live Reload

El objetivo principal de Xamarin Live Reload es permitir ver cualquier cambio relacionado con la interfaz de usuario de forma rápida y sencillo, soportando cambios al vuelo sin necesidad de compilar y desplegar. Cualquier cambio en XAML será reflejado de forma automática manteniendo los cambios.

La clave fundamental de esta nueva herramienta es que soporta cualquier librería, control de terceros además de Custom Renderers o efectos. A la hora de ver la previsualización, podemos utilizar emuladores o dispositivos físicos.

Instalación

Para poder instalar Live Reload necesitamos los siguientes requisitos:

Bastará con descargar e instalar la siguiente extensión:

Instalar Xamarin Live Reload

Instalamos…

Live Reload

Tras instalar la herramienta es hora de configurar nuestra App para utilizarla. Es tarea sencilla ya que solo tendremos que añadir el paquete NuGet Xamarin.LiveReloada nuestra librería .NET Standard 2.0.

Xamarin.LiveReload

NOTA:Es necesario utilizar .NET Standard para utilizar Live Reload en estos momentos.

Para completar la configuración y utilizar la herramienta en nuestra aplicación, en la clase App.xaml.csdebemos añadir:

 LiveReload.Init();

En el punto de entrada de la misma, es decir, en el constructor.

Todo preparado!

Utilizando la herramienta

Tras crear un proyecto Xamarin.Forms y lanzarlo en depuración en el emulador vemos lo siguiente:

Comenzamos a usar Live Reload

Visual Studio nos indica (parte superior) que Live Reload esta funcionando en un emulador de Android. A partir de este momento, cualquier cambio será aplicado:

Live Reload en acción!

A tener en cuenta…

  • Live Reload funciona con XAML. Cualquier cambio en C# requiere recompilación.
  • Se soportan las plataformas soportadas por Xamarin.Forms.
  • No se soportan (por ahora) estilos CSS.
  • Sólo funciona utilizando librerías .NET Standard.
  • Se puede utilizar en emuladores o dispositivos.
  • No hay límite en el número de dispositivos a utilizar.
  • Si no se cambia la configuración de Live Reload ni tampoco la máquina donde se compiló el código, no es necesario desplegar de nuevo. Basta con abror la aplicación previamente desplegada, conectar y continuar.

Más información

Crear Add-ins para Visual Studio para macOS utilizando Xamarin.Forms

Crear add-ins para Visual Studio macOS

Visual Studio para macOS esta basado en MonoDevelop. Antes de lanzarnos de lleno a crear un Add-in es importante conocer lo que se conoce como el modelo de extensión. La arquitectura de Visual Studio para macOS es extensible. La forma de extender se basa en rutas de extensión que permiten que terceros extiendan el comportamiento. Por ejemplo: para extender la zona de edición de código, se expone /MonoDevelop/SourceEditor2/ContextMenu/Editor permitiendo añadir nuevos comandos en el menu contextual al hacer clic derecho.

AddIn Maker

AddIn Maker es un proyecto Open Source creado por Mikayla Hutchinson que permite el desarrollo y la depuración de add-ins para Visual Studio macOS desde Visual Studio macOS.

Para añadirlo, nos dirimos al menu principal de Visual Studio y pulsamos sobre Addins…

Desde aquí podemos gestionar Addins en Visual Studio.

Crear proyecto de Add-in

Tras instalar AddIn Maker, tenemos accefso a un nuevo tipo de proyecto, Addin.

Seleccionamos la nueva plantilla, asignamos un nombre y continuamos.

Xamarin.Forms GTK

Llegamos a este punto, habitualmente, crearíamos nuestra interfaz de usuario utilizando GTK# o XWT. Cuando hablamos de XWT nos referimos a un tookit cross platform de UI para crear aplicaciones de escritorio con Mono y .NET.

Recientemente, hemos recibido los paquetes NuGet de la versión Preview de Xamarin.Forms 3.o. Entre las diferentes novedades encontramos soporte a Linux. El soporte de Linux se logra con un nuevo backend basado en GTK#.

Por lo que si usamos GTK#, ¿podríamos usar XAML y Xamarin.Forms para definir la UI de add-ins para Visual Studio?.

Es una posibilidad que personalmente he pensado en multitud de ocasiones e incluso lo he llegado a hablar con miembros de la comunidad Xamarin como con Matthew Robbins creador de MFRactor (tuvimos una genial conversación en el pasado MVP Summit).

Manos a la obra!

Comenzamos creando un proyecto de tipo IDE Extension:

IDE Extension

Este proyecto ya cuenta con la referencia a MonoDevelop.Addins, versión 0.4.4 al escribir este artículo. Continuamos añadiendo el paquete NuGet de Xamarin.Forms

Xamarin.Forms.Platform.GTK

Para trabajar con Xamarin.Forms, debemos realizar la inicialización. La mejor opción para realizar esta tarea es realizarla nada más se abra el IDE. Para ello, vamos a crear un comando:

public class InitXamarinFormsCommand : CommandHandler
{
     protected override void Run()
     {
          Forms.Init();
     }
}

Y modificaremos el archivo Manifiest.addin.xml para añadir nuestro comando como punto de extensión en el arranque del IDE.

<Extension path="/MonoDevelop/Ide/StartupHandlers">
      <Class class="LiveXAMLAddin.Commands.InitXamarinFormsCommand"/>
</Extension>

Llega la hora de extender. En nuestro caso, vamos a crear un sencillo Previewer de XAML para Xamarin.Forms.

NOTA: El objetivo de este ejemplo es demostrar como utilizar Xamarin.Forms para definir la UI de extensiones par Visual Studio. No estamos ante un nuevo Previewer de XAML. Aunque el funcionamiento es bastante rápido y correcto, no es el objetivo buscado.

Añadimos otro punto de extensión:

<Extension path = "/MonoDevelop/Ide/MainMenu/Edit">
     <CommandItem id="LiveXAMLAddin.Commands.PreviewXAMLCommand"/>
</Extension>

Fíjate que en esta ocasión, añadiremos un nuevo comando en el menu principal, opción editar.

Veamos la definición del comando:

public class PreviewXAMLCommand : CommandHandler
{
     protected override void Run()
     {
          Pad pad = null;

          var pads = IdeApp.Workbench.Pads;

          foreach (var p in IdeApp.Workbench.Pads)
          {
               if (string.Equals(p.Id, "LiveXAMLAddin.Pads.XAMLPreviewerPad", StringComparison.OrdinalIgnoreCase))
               {
                    pad = p;
               }
          }

          if (pad == null)
          {
               var content = new Pads.XAMLPreviewerPad();

               pad = IdeApp.Workbench.ShowPad(content, "LiveXAMLAddin.Pads.XAMLPreviewerPad", "XAML Previewer", "Right", null);

               if (pad == null)
                    return;
          }

          pad.Sticky = true;
          pad.AutoHide = false;
          pad.BringToFront();
     }

     protected override void Update(CommandInfo info)
     {
          info.Visible = true;

          if (IdeApp.Workbench.ActiveDocument != null && IdeApp.Workbench.ActiveDocument.FileName.ToString().Contains(".xaml"))
          {
               info.Enabled = true;
          }
          else
          {
               info.Enabled = false;
          }
     }
}

Reconoces a un Pad como un panel lateral anclable tanto a izquierda como derecha. En esta ocasión, creamos un nuevo Pad donde mostrar la previsualización de XAML, de título «XAML Previewer» y anclado a la derecha.

Para habilitar la opción de menu, vamos a determinar si el usuario esta editando un archivo XAML. Para ello, utilizamos IdeApp.Workbench.ActiveDocument. Tenemos posibilidad de acceder a toda la información del documento, como su formato o contenido.

Utilizando XAML para definir UI

¿Qué vamos a mostrar en el Pad?

Vamos a utilizar XAML!.  Creamos una nueva ContentPage:

<ContentPage 
     xmlns="http://xamarin.com/schemas/2014/forms" 
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
     x:Class="LiveXAMLAddin.Views.PreviewerView">
     <ContentPage.Content>
          <Grid>
               <ContentView
                    Content="{Binding Preview}" />
          </Grid>
     </ContentPage.Content>
</ContentPage>

Tenemos acceso a todas las opciones disponibles con XAML, entre ellas, sistema de enlace a datos, etc.  Para utilizar la página XAML como contenido del Pad vamos a embeber el contenido:

var page = new PreviewerView();
var pageContainer = page.CreateContainer();

Tenemos disponible el método de extensión CreateContainer de Xamarin.Forms en el backend GTK para obtener la ContentPage como contenido nativo.

Cafa vez que se cambie el texto de la página XAML en el editor de Visual Studio, vamos a actualizar la previsualización XAML:

IdeApp.Workbench.ActiveDocument.Editor.TextChanged += (sender , args) =>
{ 
     PreviewXaml(IdeApp.Workbench.ActiveDocument.Editor.Text);
};

Podemos utilizar MVVM (de hecho en este ejemplo se usa) en combinación con toda la potencia y las opciones de extensión que tenemos.

El resultado:

El resultado

Tienes el código fuente del ejemplo disponible GitHub:

Ver GitHub

¿Qué te parece?. Quizás en un futuro sea una opción a la hora de extender Visual Studio. Ahora que tenemos el IDE en más de un sistema operativo, es hora de pensar en contar con una opción para crear extensiones que compartan la mayor cantidad de código posible.

Más información

[Evento SVQXDG] Primer vistazo a novedades de Xamarin.Forms 3.0

Xamarin.Forms 3.0

Recientemente hemos recibido la versión Xamarin.Forms 3.0 Pre-Release. Esta nueva versión llega con soporte a .NET Standard, multi-targeting (también a PCL), mejoras en rendimiento, corrección de errores así como nuevas características. Entre las características nuevas encontramos:

  • Nuevas plataformas soportadas como Linux (GTK) o versiones anteriores de Windows (WPF).
  • Visual State Manager.
  • FlexLayout.
  • Uso de CSS para manejar estilos.
  • Localización Right to Left.

El evento

Ante tal cantidad de novedades, ¿algo mejor que verlas todas en un evento?. En este evento vamos a realizar un repaso de cada novedad a base de demos.

El evento se celebrará en la ETS de Ingeniería Informática. Dirección detallada:

E.T.S. Ingeniería Informática – Universidad de Sevilla, Aula A2.14
Av. Reina Mercedes s/n
Sevilla Se 41012

La fecha

Será el próximo Martes, 10 de Abril de 19:00h a 20:30h(GMT+1).

¿Te apuntas?

Más información

[Xamarin.Forms] Creando un Walkthrough usando Lottie

Introducción

A la hora de desarrollar aplicaciones móviles nos encontramos con ciertas necesidades a cubrir. Una bastante habitual es la de ofrecer en una experiencia sencilla y fluida una breve explicación de la propia aplicación, su funcionalidad o características más importantes.

Walkthrough

Para evitar que el usuario acabe «saltando»(Skip) el tutorial, es necesario atraer su atención. En este artículo, vamos a crear un Walkthrough o tutorial donde vamos a utilizar Lottie, animaciones de diferente tipo para conseguir atraer la atención de usuario.

Creando el Carousel

Comenzamos por la parte fundamental, el Carousel. El Carousel es un control que contiene una colección de contenido permitiendo navegar entre el mismo realizando un gesto de deslizamiento lateral.

Entre las diferentes opciones que tenemos disponibles (recuerda que existe paquete oficial para obtener un Carousel) tenemos CarouselView de Alexander Reyes. Es una opción interesante al permitir trabajar con:

  • Indicadores de posición.
  • Orientación: Vertical o Horizontal.
  • Animaciones
  • Control sobre estado, posición, si se muestran flechas para la navegación o no, etc.
CarouselView.FormsPlugin

Para trabajar con este Carousel, tras añadir el paquete NuGet correspondiente, añadimos el siguiente namespace en XAML:

xmlns:carousel="clr-namespace:CarouselView.FormsPlugin.Abstractions;assembly=CarouselView.FormsPlugin.Abstractions"

La definición básica del control es sencilla:

<carousel:CarouselViewControl />

Bien, en nuestro caso, cada paso del tutorial será una ContentView. Podemos gestionar la colección de diferentes formas, una sencilla:

_views = new View[]
{
     new BikingHardView(),
     new AcrobaticsView(),
     new SoExcitedView(),
     new BikingCoolView()
};

Y utilizando la propiedad ItemsSource(se puede utilizar un enlace a datos), establecemos la colección.

Carousel.ItemsSource = _views;

Tenemos lo básico. Continuamos!.

Lottie a escena!

Utilizando páginas estáticas, sin movimiento, podemos conseguir la atención del usuario con una buena combinación de uso de imágenes, fuentes y otros recursos visuales.

Con un poco de «acción»lo tenemos más sencillo. Tenemos la posibilidad de trabajar con Lottiedesde Xamarin para Android, iOS, así como en UWP y en Xamarin.Forms gracias a la gran comunidad Xamarin.

¿Qué es Lottie?

Airbnb ha desarrollado una herramienta Open Source con la que facilitar la creación de animaciones móviles. Podemos utilizar animaciones potentes creadas con After Effects y utilizarlas desde nuestras aplicaciones móviles de forma sencilla.

Para trabajar con Lottie en Xamarin.Forms, comenzamos añadiendo un paquete NuGet:

Lottie Xamarin.Forms

Recuerda añadirlo en cada proyecto de plataforma además de en la librería .NET Standard.

Para completar el proceso de preparación de Lottie en nuestro proyecto Xamarin.Forms, necesitamos añadir la siguiente línea tras la inicialización de Xamarin.Forms en cada proyecto nativo:

AnimationViewRenderer.Init();

Todo listo!.

Para mostrar la animación necesitamos un elemento visual que nos permita además realizar una gestión de la misma. Este elemento visual es AnimationView.

Para utilizar AnimationViewdesde XAML necesitamos utilizar el siguiente espacio de nombres:

xmlns:lottie="clr-namespace:Lottie.Forms;assembly=Lottie.Forms"

La interfaz:

<forms:AnimationView
     Animation="bikingishard.json"
     AutoPlay="true"
     Loop="True"
     Speed="1"
     HorizontalOptions="FillAndExpand"
     VerticalOptions="FillAndExpand" />

Hasta este punto, de forma sencilla, conseguimos un resultado bastante atractivo.

Obtener recursos

Un gran fuente de animaciones es LottieFiles. Puedes encontrar una gran variedad de animaciones listas para utilizar con opciones de búsqueda, etc.

LottieFiles

NOTA:Si usas animaciones de LottieFiles, recuerda otorgar crédito a sus creadores.

Añadimos más animaciones

Podemos continuar mejorando todo. En cada paso del tutorial vamos a añadir información relevante en texto y otros elementos visuales. Vamos a necesitar saber cuando el usuario pasa de un paso a otro.

De forma sencilla, podemos crear una interfazcomo la siguiente:

public interface IAnimatedView
{
     void StartAnimation();
}

De modo que, cada vez que el usuario cambie la ContentView seleccionada, si el paso hereda de nuestra interfaz, ejecutamos la implementación de la misma.

var currentView = _views[e.NewValue];

if (currentView is IAnimatedView animatedView)
{
     animatedView.StartAnimation();
}

¿Qué conseguimos con esto?

Vamos a añadir otra dependencia vía NuGet con la librería Xamanimation. Esta pequeña librería añade una gran variedad de animaciones predefinidas además de la posibilidad de crear Storyboards, lanzar animaciones desde XAML, etc.

NOTA:Xamanimation formará parte del bloque de animaciones del Toolkitde Xamarin.Forms.

Añadimos espacio de nombres en XAML:

xmlns:xamanimation="clr-namespace:Xamanimation;assembly=Xamanimation"

Y podemos definir una animación de la siguiente forma:

<xamanimation:StoryBoard
     x:Key="InfoPanelAnimation"
     Target="{x:Reference InfoPanel}">
     <xamanimation:FadeToAnimation
          Duration="50"
          Opacity="1" />
     <xamanimation:TranslateToAnimation
          TranslateY="0"
          Easing="CubicIn"
          Duration="100" />
</xamanimation:StoryBoard>

Esta animación, animará un elemento visual marcado con el nombre InfoPanel, primero la opacidad de su estado inicial, 0, es decir, no visible, a 1 además de realizar una trasladación en el eje Y.

Para lanzarla:

if (Resources["InfoPanelAnimation"] is StoryBoard animation)
{
     animation.Begin();
}

Sencillo, ¿verdad?.

De igual forma, aprovechamos algunas animaciones de la librería para animar el color de fondo al pasar de un paso a otro entre otros detalles.

El resultado:

El resultado

NOTA:Las animaciones de Lottie de este ejemplo corresponden a Yue XIde LottieFiles.

Tienes el código fuente del ejemplo disponible GitHub:

Ver GitHub

Más información

[Xamarin.Forms] Primer vistazo a VistualStateManager

Introducción

Una de las funcionalidades que estarán disponibles en próximas versiones de Xamarin.Forms y ya disponible en las Nightly es VisualStateManager.

NOTA: Puedes acceder a los paquetes Nightly de Xamarin.Forms en este enlace.

En este artículo, vamos a darle un primer vistazo a esta nueva característica.

VisualStateManager

VisualStateManager se utiliza para definir y gestionar diferentes estados entre elementos visuales de una página. En concreto, un VisualState es la colección de propiedades de un elemento visual que en conjunto definen el estado de un elemento visual.

Para entender como utilizar VisualStateManager de forma sencilla, vamos a crear un ejemplo habitual. A continuación, vamos a crear un sencillo formulario para asegurar que el usuario registra una contraseña de forma correcta, es decir, vamos a pedir introducir la misma hasta en dos ocasiones:

<StackLayout>
     <Label
          Text="Password" />
     <Entry
          x:Name="Password"
          IsPassword="True" />
     <Label
          Text="Repeat Password" />
     <Entry
          x:Name="RepeatPassword"
          IsPassword="True" />
</StackLayout>

Sencillo, ¿verdad?. Lo ideal sería trasmitir al usuario feedback visual directo ante la repetición de la contraseña en caso de error.

¿Cómo lo hacemos?

VisualStateGroups

Vamos a crear un estilo para cada donde pedimos la repetición de la contraseña:

<Style
     x:Key="RepeatPasswordStyle"
     TargetType="Entry">
     <Setter
          Property="VisualStateManager.VisualStateGroups">
          <VisualStateGroupList
               x:Name="CommonStates">
               <VisualStateGroup>
                    <VisualState
                         x:Name="Valid">
                         <VisualState.Setters>
                              <Setter
                                   Property="BackgroundColor"
                                   Value="White" />
                         </VisualState.Setters>
                    </VisualState>
                    <VisualState
                         x:Name="Invalid">
                         <VisualState.Setters>
                              <Setter
                                   Property="BackgroundColor"
                                   Value="Red" />
                         </VisualState.Setters>
                     </VisualState>
                </VisualStateGroup>
           </VisualStateGroupList>
      </Setter>
</Style>

Creamos dos VisualState o estados visuales. El primero de ellos lo utilizamos cuando la contraseña es correcta. En el segundo estado, que utilizaremos cuando la contraseña sea incorrecta (no coincide) vamos a modificar el color de fondo a rojo.

Aplicamos el estilo.

<Entry
     x:Name="RepeatPassword"
     IsPassword="True"
     Style="{StaticResource RepeatPasswordStyle}" />

Hasta este punto, nada tiene efecto alguno. Necesitamos alguna forma de poder gestionar cada estado definido.

Gestión de estados

Para poder gestionar el estado a utilizar, VisualStateManager cuenta con el método GoToState para permitir cambiar entre diferentes VisualStates.

¿Cómo validamos la contraseña y cambiamos entre estados?

Creamos un Behavior:

public class ConfirmPasswordBehavior : Behavior<Entry>
{
     public static readonly BindableProperty CompareToEntryProperty =
     BindableProperty.Create("CompareToEntry", typeof(Entry), typeof(ConfirmPasswordBehavior), null);

     public Entry CompareToEntry
     {
          get { return (Entry)GetValue (CompareToEntryProperty); }
          set { SetValue (CompareToEntryProperty, value); }
     }

     protected override void OnAttachedTo (Entry bindable)
     {
          bindable.TextChanged += HandleTextChanged;
          base.OnAttachedTo (bindable);
     }
     protected override void OnDetachingFrom (Entry bindable)
     {
          bindable.TextChanged -= HandleTextChanged;
          base.OnDetachingFrom (bindable);
     }

     void HandleTextChanged (object sender, TextChangedEventArgs e)
     {
          var password = CompareToEntry.Text;

          if (string.IsNullOrEmpty (password))
               return;

          var confirmPassword = e.NewTextValue;
          var isValid = password.Equals (confirmPassword);

          if (isValid) 
          {
               Xamarin.Forms.VisualStateManager.GoToState ((Entry)sender, "Valid");
          } 
          else 
          {
              Xamarin.Forms.VisualStateManager.GoToState ((Entry)sender, "Invalid");
          }
     }
}

Aplicamos el Behavior:

<Entry.Behaviors>
      <behaviors:ConfirmPasswordBehavior
           CompareToEntry="{Binding Source={x:Reference Password}}" />
</Entry.Behaviors>

El resultado:

VisualStateManager

Según vamos escribiendo salta el evento TextChanged del Behavior y en el mismo, validamos si la contraseña es igual a la anterior. En este momento, utilizamos el método GoToState de VisualStateManager para cambiar de un estado a otro según el caso.

Tienes el código fuente del Toolkit disponible GitHub:

Ver GitHubEstamos ante una funcionalidad muy solicitada que añade bastante potencial para la gestión de estados ante la interacción de la aplicación. ¿Qué te parece a ti?. Recuerda, cualquier duda o comentario puedes añadirla a los comentarios de la entrada.

Más información

[Xamarin.Forms] Primer vistazo a FlexLayout

Introducción

Una de las grandes nuevas características que tendremos en Xamarin.Forms, ya disponible en paquetes Nightly, es FlexLayout.

En este artículo, vamos a realizar una introducción a este nuevo Layout, sus opciones y características principales.

Layouts de Xamarin.Forms

En Xamarin.Forms contamos con diferentes Layouts que nos permiten organizar y posicionar los diferentes elementos visuales que componen la interfaz de usuario.

Las clases Layout y Layout<T> en Xamarin.Forms son tipos especiales de Views que actúan como contenedores de otras Views o Layouts.

Layouts en Xamarin.Forms

¿Qué es FlexLayout?

Al trabajar con CSS en desarrollo web, para adoptar una sintaxis limpia para crear diseños adaptativos llegó Flexbox.

Flexbox proporciona una forma eficiente de distribuir, alinear y gestionar el espacio entre elementos incluso cuando el tamaño de la ventana o de cada elemento es dinámico o desconocido.

Basados en esta opción, contamos con un nuevo Layout llamado FlexLayout con gran cantidad de opciones para la gestión de la distribución y tamaño de elementos.

¿Por dónde empezamos?

Probablente sea tu primera pregunta. Comenzamos creando un nuevo proyecto Xamarin.Forms.

Tras crear el proyecto, actualizamos el paquete a la versión Nightly de Xamarin.Forms:

Xamarin.Forms Nightly

NOTA: Puedes ver como acceder a versiones Nightly de Xamarin.Forms en este enlace.

Basta con añadir en la página:

<FlexLayout>
</FlexLayout>

Para comenzar a trabajar con FlexLayout.

FlexLayout, propiedades

El comportamiento del Layout es diferente en base a sus propiedades. A continuación, vamos a revisar las propiedades fundamentales.

Direction

La propiedad Direction controla la dirección de los elementos. Los valores posible son:

  • Row
  • Column
  • RowReverse
  • ColumnReverse

Simplificando, esta propiedad permite definir como se distribuyen los elementos que contiene. Puede ser de forma horizontal, vertical o de forma invertida en ambas direcciones. La dirección predeterminada es horizontal, de izquierda a derecha.

NOTA: En el «mundo flexible» no se usa «horizontal» o «vertical» si no eje principal (main-axis) y eje transversal (cross-axis).

Aunque no se establezca de forma explícita, FlexLayout tiene como valor predeterminado Row.

Wrap

Contamos con los siguientes valores:

  • Wrap
  • NoWrap
  • Reverse

Pero…¿para qué lo usamos?.

Para entender correctamente esta propiedad vamos a crear un sencillo ejemplo. Dentro de un FlexLayout añadimos elementos:

Comenzando el ejemplo

Son textos de 24px de ancho. Por defecto, como hemos visto anteriormente se organizarán en formato de Row de izquierda a derecha.

Bien, vamos a duplicar los elementos. ¿Qué va a ocurrir?, ¿se salen elementos de pantalla?, ¿se ajusta el tamaño de todo?, ¿se dividen elementos a una nueva línea?.

Duplicamos elementos

Nada como probar, para tener la respuesta. El contenedor se adapta para poder contenedor todos los elementos hijos. Este es el comportamiento por defecto. Es decir, la propiedad Wrap tiene como valor predeterminado NoWrap.

Podemos modificar el comportamiento de forma sencilla:

Wrap

JustifyContent

Si hasta ahora piensas que todo tiene buena pinta, esta propiedad te va a gustar. Cuenta con las siguientes opciones:

  • Start
  • Center
  • End
  • SpaceBetween
  • SpaceAround
  • SpaceEvenly

Esta propiedad te puede recordar a la alineación de texto. Define como se organizan los elementos en el eje principal. El valor por defecto es Start. Con este valor los elementos se agrupan al inicio del eje principal.

Start

Por el contrario, la opción End agrupa los elementos al final del eje principal:

End

Centrados:

Center

La opción SpaceBetween mantiene el mismo espacio entre cada elemento:

SpaceBetween

SpaceAround, es similar a la opción anterior, los elementos se distribuyen uniformemente manteniendo además el mismo espacio alrededor (en ambos lados). Nota que el primer elemento tendrá una unidad de espacio con respecto al borde del contenedor, pero dos unidades con respecto al siguiente elemento (el siguiente elemento cuenta con su propio espacio).

SpaceAround

Por último, contamos con SpaceEvenly donde los elementos se distribuyen manteniendo el mismo espacio entre elementos y los bordes.

SpaceEvenly

AlignItems

Esta propiedad es similar a la anterior por lo que es rápida de asimilar. Los posibles valores son:

  • Start
  • Center
  • End
  • Stretch

Define el comportamiento predeterminado de cómo se colocan los elementos a lo largo del eje transversal (cross-axis).

El valor predeterminado es Stretch que se encargará de «estirar» los elementos para que rellenen toda la altura del contenedor. El resto de opciones hacen justo lo que imaginas, agrupan los elementos al inicio, centro o final del contenedor.

AlignContent

¿Recuerdas lo que pasaba al añadir más elementos al probar la propiedad relacionada con Wrap?. Utilizando Wrap tenemos un contenedor flexible multilínea.

Esta propiedad permite controlar la alineación de los elementos haciendo Wrapping. Nos permite controlar el espacio adicional en el eje transversal.

NOTA: Esta propuedad no tiene efcto cuando solo hay una línea de elementos.

Los valores posibles son:

  • Start
  • Center
  • End
  • Stretch
  • SpaceBetween
  • SpaceAround
  • SpaceEvenly

Como en AlignItems el valor por defecto es Stretch. Con este valor, los elementos se «estiran» para adaptarse al espacio disponible a lo largo del eje transversal.

Hemos visto anteriormente el resto de propiedades, sin embargo, hay en esta ocasión ligeras diferencias.

El valor Start alinea los elementos del contenedot al inicio del eje transversal. Recuerda que el eje transversal tiene como orden predeterminado de arriba hacia abajo. Por lo tanto, el valor End los coloca en la parte inferior (abajo) y Center en el centro.

Propiedades de cada elemento

Además de poder cambiar el comportamiento del Layout gracias a sus propiedades principales, podemos adaptar diferentes opciones utilizando propiedades de cada elemento.

FlexLayout.Grow

La gran aportación de FlexLayout es permitir que sus elementos sean «flexibles». Esta propiedad nos permite tener aún más control en este sentido. El valor de la propiedad puede ser desde cero a cualquier valor numérico positivo. Vamos a ver en que consiste.

Por defecto, el valor de la propiedad es 0. Este valor hace que el elemento no crezca para ajustarse al espacio disponible. Podríamos decir que es como un «interruptor apagado». Si tenemos en un contenedor un elemento, y establecemos el valor a 1, el elemento pasa a ocupar todo el espacio.

Básicamente, esta propiedad define la capacidad de un elemento para crecer si es necesario. Acepta un valor sin unidades que sirve como una proporción. Indica qué cantidad de espacio disponible dentro del contenedor flexible debe ocupar el elemento.

Si todos los elementotienen un crecimiento flexible establecido en 1, el espacio restante en el contenedor se distribuirá por igual a todos los hijos. Si uno de los hijostiene un valor de 2, el espacio restante ocuparía el doble de espacio que los demás (o lo intentará, al menos).

FlexLayout.Basis

Esta propiedad define el tamaño predeterminado de un elemento antes que se distribuya el espacio restante disponible. Puede ser una proporción, por ejemplo un porcentaje (5%, 25%, etc.). También se pueden utilizar algunas palabras reversadas como Auto.

En combinación con FlexLayout.Grow permite un control bastante completo sobre el tamaño de cada elemento hijo del contenedor.

Conclusiones

No es extraño encontrar la necesidad de gestionar Wrapping de elementos, elementos con diferentes espacios de forma dinámica, etc. La llegada de un nuevo Layout basado en algo conocido como Flexbox y que aporte la posibilidad de gestionar Layouts flexibles de forma sencilla es importante.

FlexLayout

Tienes un ejemplo de FlexLayout disponible en GitHub:

Ver GitHub¿Qué te parece esta nueva opción?. Recuerda, cualquier duda o pregunta es bienvenida en los comentarios de la entrada.

Más información