[Xamarin.Forms] C# Markup Extensions

Introducción

En Xamarin.Forms 4.6 han llegado las extensiones de marcado de C#. Se trata de un conjunto (opcional) de métodos de extensión y clases auxiliares para simplificar la creación de interfaces de usuario en Xamarin.Forms usando C#. En este artículo, vamos a crear la misma interfaz usando XAML, C# y las nuevas extensiones de marcado de C# para conocer las posibilidades de las extensiones añadidas.

Crear UI con XAML

Comenzamos creando una pantalla de Login usando XAML:

<Grid
    Padding="12">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Image
        Grid.Row="0"
        BackgroundColor="Gray"
        HorizontalOptions="Center"
        VerticalOptions="Center"
        HeightRequest="100"
        WidthRequest="100"/>
    <Entry
        Grid.Row="1"
        Text="{Binding Username, Mode=TwoWay}"
        Placeholder="Username"/>
    <Entry
        Grid.Row="2"
        Text="{Binding Password, Mode=TwoWay}"
        IsPassword="True"
        Placeholder="Password"/>
    <Label
        Grid.Row="3"
        Text="Forgot Password?"
        FontSize="Micro"
        TextDecorations="Underline"
        HorizontalOptions="End"/>
    <Grid
        Grid.Row="4">
        <Button
            BackgroundColor="White"
            CornerRadius="24"
            Text="LOGIN"
            HeightRequest="60"
            VerticalOptions="Center"
            Command="{Binding SignInCommand}"
            Margin="24, 12"/>
    </Grid>
</Grid>

Crear UI con C#

Vamos a crear lo mismo usando C#:

public class LoginCSharpView : ContentPage
{
    public LoginCSharpView()
    {
        BindingContext = new LoginViewModel();
        BackgroundColor = Color.LightGray;

        var grid = new Grid
        {
            Padding = 12
        };

        grid.RowDefinitions.Add(new RowDefinition());
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition());

        var logo = new Image
        {
            BackgroundColor = Color.Gray,
            HeightRequest = 100,
            WidthRequest = 100,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };

        grid.Children.Add(logo);
        Grid.SetRow(logo, 0);

        var usernameEntry = new Entry
        {
            Placeholder = "Username"
        };
        usernameEntry.SetBinding(Entry.TextProperty, "Username");
        grid.Children.Add(usernameEntry);
        Grid.SetRow(usernameEntry, 1);

        var passwordEntry = new Entry
        {
            IsPassword = true,
            Placeholder = "Password"
        };
        passwordEntry.SetBinding(Entry.TextProperty, "Password");
        grid.Children.Add(passwordEntry);
        Grid.SetRow(passwordEntry, 2);

        var forgotPassword = new Label
        {
            FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
            Text = "Forgot Password?",
            TextDecorations = TextDecorations.Underline,
            HorizontalOptions = LayoutOptions.End
        };
        grid.Children.Add(forgotPassword);
        Grid.SetRow(forgotPassword, 3);

        var signInGrid = new Grid();

        var signInButton = new Button
        {
            BackgroundColor = Color.White,
            CornerRadius = 24,
            HeightRequest = 60,
            Margin = new Thickness(24, 12),
            VerticalOptions = LayoutOptions.Center,
            Text = "LOGIN"
        };
        signInGrid.Children.Add(signInButton);

        grid.Children.Add(signInGrid);
        Grid.SetRow(signInGrid, 4);

        Content = grid;
    }
}

C# Markup Extensions

Pasamos a ver las novedades aportadas por las extensiones añadidas:

public class LoginCSharpMarkupView : ContentPage
{
    enum Row { Logo, Username, Password, Forgot, SignIn };

    public LoginCSharpMarkupView()
    {
        var vm = new LoginViewModel();
        BindingContext = vm;
        BackgroundColor = Color.LightGray;

        Content = new Grid
        {
            Padding = 12,
            RowDefinitions = Rows.Define(
                (Row.Logo, GridLength.Star),
                (Row.Username, GridLength.Auto),
                (Row.Password, GridLength.Auto),
                (Row.Forgot, GridLength.Auto),
                (Row.SignIn, GridLength.Star)),
            Children =
            {
                new Image
                {
                    BackgroundColor = Color.Gray,
                    HeightRequest = 100,
                    WidthRequest = 100,
                    HorizontalOptions = LayoutOptions.Center,
                    VerticalOptions = LayoutOptions.Center
                }.Row(Row.Logo),
                new Entry
                {
                    Placeholder = "Username"
                }.Row(Row.Username).Bind(nameof(vm.Username)),
                new Entry
                {
                    IsPassword = true,
                    Placeholder = "Password"
                }.Row(Row.Password).Bind(nameof(vm.Password)),
                new Label
                {
                    FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
                    Text = "Forgot Password?",
                    TextDecorations = TextDecorations.Underline,
                    HorizontalOptions = LayoutOptions.End
                }.Row(Row.Forgot),
                new Button
                {
                    BackgroundColor = Color.White,
                    CornerRadius = 24,
                    HeightRequest = 60,
                    Margin = new Thickness(24, 12),
                    VerticalOptions = LayoutOptions.Center,
                    Text = "LOGIN"
                }.Row(Row.SignIn).Bind(nameof(vm.SignInCommand))
            }
        };
    }
}

Antes de profundizar en detalles, las extensiones de marcado C# añadidas en Xamarin.Forms 4.6 se encuentran en fase experimental:

Device.SetFlags(new string[]{ "Markup_Experimental" });

Las extensiones para crear la interfaz de usuario usando C# de forma  fluida se encuentran en el espacio de nombres Xamarin.Forms.Markup.

Enlace a datos

Contamos con el método de extension Bind que cuenta con varias sobrecargas para crear un enlace de datos entre la propiedad de una vista y una propiedad especificada.

var password = new Entry { IsPassword = true, Placeholder = "Password" }.Bind(nameof(vm.Password));

Layouts

Al usar un Grid, podemos usar una enumeración para definir filas y columnas, en lugar de usar directamente números. Es necesario utilizar el namespace:

using static Xamarin.Forms.Markup.GridRowsColumns;

Veamos el uso:

var grid = new Grid { 
     RowDefinitions = 
          Rows.Define( (Row.Logo, GridLength.Star), (Row.Username, GridLength.Auto), (Row.Password, GridLength.Auto), (Row.Forgot, GridLength.Auto), (Row.SignIn, GridLength.Star))
};

Además, se puede definir de forma sencilla filas y columnas usando la enumeración:

var password = new Entry { IsPassword = true, Placeholder = "Password" }.Row(Row.Password);

Estilos

Los estilos implícitos se pueden utilizar cargándolos en el diccionario de recursos de la aplicación:

public App()
{
     Resources = Styles.Implicit;
     ...
}

Mientras que los estilos explícitos se pueden usar utilizando el método de extensión Style.

var password = new Entry { IsPassword = true, Placeholder = "Password" }.Style (PasswordStyle);

Otros

Aunque no lo hemos visto en el ejemplo de Login, tenemos bastantes mas extensiones para trabajar con fuentes, efectos, gestos, etc.

Para trabajar con gestos podemos usar el método de extensión BindTapGesture:

var tapLabel = new Label { Text = "Tap Me" } .BindTapGesture (nameof(vm.TapCommand));

Podemos adjuntar efectos usando el método de extensión Effects:

var effectButton = new Button { Text = "Tap Me" }.Effects (new ButtonBordeless());

Para ver más ejemplos y detalles relacionados con las extensiones de marcado, puedes leer la documentación oficial.

Puedes encontrar el ejemplo en GitHub:

Ver GitHub

¿Qué te parecen las extensiones de marcado C#?, ¿creas la interfaz de usuario en Xamarin.Forms usando XAML o C#). Recuerda, puedes dejar cualquier duda o comentario en la entrada!.

Más información

Deja un comentario