Botón Xamarin.Forms

Download SampleDescargar el ejemplo

El Botón responde a un toque o clic que dirige una aplicación para llevar a cabo una tarea determinada.

El Button es el control interactivo más fundamental de todos los Xamarin.Forms. La Button normalmente muestra una breve cadena de texto que indica un comando, pero también puede mostrar una imagen de mapa de bits o una combinación de texto e imagen. El usuario presiona el Button con un dedo o hace clic en él con un mouse para iniciar ese comando.

La mayoría de los temas tratados a continuación corresponden a páginas de la muestra ButtonDemos.

Administración de las pulsaciones de botón

Button define un evento Clicked que se produce cuando el usuario pulsa Button con el dedo o con el puntero del ratón. El evento se produce cuando se suelta el dedo o el botón del mouse de la superficie de Button. El Button debe tener la propiedad IsEnabled establecida en true para que responda a los toques.

La página Clic de botón básico del ejemplo ButtonDemos muestra cómo crear una instancia de Button en XAML y administrar el evento Clicked. El archivo BasicButtonClickPage.xaml contiene un StackLayout con un Label y un Button:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ButtonDemos.BasicButtonClickPage"
             Title="Basic Button Click">
    <StackLayout>

        <Label x:Name="label"
               Text="Click the Button below"
               FontSize="Large"
               VerticalOptions="CenterAndExpand"
               HorizontalOptions="Center" />

        <Button Text="Click to Rotate Text!"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="Center"
                Clicked="OnButtonClicked" />

    </StackLayout>
</ContentPage>

Button t0iende a ocupar todo el espacio que se le permite. Por ejemplo, si no establece la propiedad HorizontalOptions de Button a algo distinto de Fill, el Button ocupará todo el ancho de su principal.

Por defecto, el Button es rectangular, pero puede darle esquinas redondeadas utilizando la propiedad CornerRadius, como se describe más adelante en la sección Apariencia del botón.

La propiedad Text especifica el texto que aparece en el elemento Button. El evento Clicked se establece en un controlador de eventos denominado OnButtonClicked. Este controlador se encuentra en el archivo code-behind, BasicButtonClickPage.xaml.cs:

public partial class BasicButtonClickPage : ContentPage
{
    public BasicButtonClickPage ()
    {
        InitializeComponent ();
    }

    async void OnButtonClicked(object sender, EventArgs args)
    {
        await label.RelRotateTo(360, 1000);
    }
}

Cuando se pulsa Button, se ejecuta el método OnButtonClicked. El argumento sender es el objeto Button responsable de este evento. Puedes usarlo para acceder al objeto Button o para distinguir entre varios objetos Button que comparten el mismo evento Clicked.

Este controlador Clicked en particular llama a una función de animación que rota el Label 360 grados en 1 000 milisegundos. Este es el programa funcionando en dispositivos iOS y Android, y como aplicación de la Plataforma Universal de Windows (UWP) en el escritorio de Windows 10:

Basic Button Click

Observe que el método OnButtonClicked incluye el modificador async porque await se utiliza dentro del controlador de eventos. Un controlador de eventos Clicked requiere el modificador async solo si el cuerpo del controlador utiliza await.

Cada plataforma interpreta el Button de una manera específica. En la sección Apariencia de los botones, verá cómo establecer los colores y hacer visible el borde Button para obtener una apariencia más personalizada. Button implementa la interfaz IFontElement, por lo que incluye FontFamily, FontSize, y propiedades FontAttributes.

Crear un botón en código

Es común crear una instancia de Button en XAML, pero también puede crear una Button en código. Esto puede ser conveniente cuando su aplicación necesita crear múltiples botones basados en datos que son enumerables con un bucle foreach.

La página Clic de botón de código muestra cómo crear una página funcionalmente equivalente a la página Clic de botón clásico, pero totalmente en C#:

public class CodeButtonClickPage : ContentPage
{
    public CodeButtonClickPage ()
    {
        Title = "Code Button Click";

        Label label = new Label
        {
            Text = "Click the Button below",
            FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
            VerticalOptions = LayoutOptions.CenterAndExpand,
            HorizontalOptions = LayoutOptions.Center
        };

        Button button = new Button
        {
            Text = "Click to Rotate Text!",
            VerticalOptions = LayoutOptions.CenterAndExpand,
            HorizontalOptions = LayoutOptions.Center
        };
        button.Clicked += async (sender, args) => await label.RelRotateTo(360, 1000);

        Content = new StackLayout
        {
            Children =
            {
                label,
                button
            }
        };
    }
}

Todo se hace en el generador de la clase. Dado que el controlador Clicked solo tiene una sentencia, puede adjuntarse al evento de forma muy sencilla:

button.Clicked += async (sender, args) => await label.RelRotateTo(360, 1000);

Por supuesto, también puede definir el controlador de eventos como un método separado (al igual que el método OnButtonClick en Clic de botón básico) y adjuntar ese método al evento:

button.Clicked += OnButtonClicked;

Deshabilitar el botón

A veces, una aplicación se encuentra en un estado concreto en el que un clic Button determinado no es una operación válida. En tales casos, se debe deshabilitar el objeto Button estableciendo su propiedad IsEnabled en false. El ejemplo clásico es un control Entry para un nombre de archivo acompañado de una apertura de archivo Button: El Button debe activarse solo si se ha escrito algún texto en el Entry. Puede utilizar un DataTrigger para esta tarea, como se muestra en el artículo Desencadenadores de datos.

Uso de la interfaz de comandos

Es posible que una aplicación responda a pulsaciones de Button sin controlar el evento Clicked. El Button implementa un mecanismo de notificación alternativo denominado interfaz comando o de comandos. Esta consta de dos propiedades:

Este enfoque es especialmente adecuado en relación con la vinculación de datos, y en particular cuando se implementa la arquitectura Model-View-ViewModel (MVVM). Estos temas se tratan en los artículos Enlace de datos, desde Enlaces de datos a MVVM, y MVVM.

En una aplicación MVVM, el viewmodel define propiedades de tipo ICommand que luego se conectan a los elementos Button XAML con enlaces de datos. Xamarin.Forms también define Command y Command<T> clases que implementan la interfaz ICommand y ayudan al viewmodel a definir propiedades de tipo ICommand.

Los comandos se describen con más detalle en el artículo La interfaz de comandos, pero la página Comandos básicos de botones de la muestra ButtonDemos muestra el enfoque básico.

La clase CommandDemoViewModel es un viewmodel muy simple que define una propiedad de tipo double denominada Number, y dos propiedades de tipo ICommand denominadas MultiplyBy2Command y DivideBy2Command:

class CommandDemoViewModel : INotifyPropertyChanged
{
    double number = 1;

    public event PropertyChangedEventHandler PropertyChanged;

    public CommandDemoViewModel()
    {
        MultiplyBy2Command = new Command(() => Number *= 2);

        DivideBy2Command = new Command(() => Number /= 2);
    }

    public double Number
    {
        set
        {
            if (number != value)
            {
                number = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Number"));
            }
        }
        get
        {
            return number;
        }
    }

    public ICommand MultiplyBy2Command { private set; get; }

    public ICommand DivideBy2Command { private set; get; }
}

Las dos propiedades ICommand se inicializan en el constructor de la clase con dos objetos de tipo Command. Los constructores Command incluyen una pequeña función (llamada argumento del constructor execute) que duplica o reduce a la mitad la propiedad Number.

El archivo BasicButtonCommand.xaml establece su BindingContext a una instancia de CommandDemoViewModel. El elemento Label y dos elementos Button contienen vínculos a las tres propiedades en CommandDemoViewModel:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ButtonDemos"
             x:Class="ButtonDemos.BasicButtonCommandPage"
             Title="Basic Button Command">

    <ContentPage.BindingContext>
        <local:CommandDemoViewModel />
    </ContentPage.BindingContext>

    <StackLayout>
        <Label Text="{Binding Number, StringFormat='Value is now {0}'}"
               FontSize="Large"
               VerticalOptions="CenterAndExpand"
               HorizontalOptions="Center" />

        <Button Text="Multiply by 2"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="Center"
                Command="{Binding MultiplyBy2Command}" />

        <Button Text="Divide by 2"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="Center"
                Command="{Binding DivideBy2Command}" />
    </StackLayout>
</ContentPage>

Al pulsar los dos elementos Button, se ejecutan los comandos y el número cambia de valor:

Basic Button Command

La ventaja de este enfoque sobre los controladores Clicked es que toda la lógica que implica la funcionalidad de esta página se encuentra en el modelo de vista en lugar del archivo de código subyacente, logrando una mejor separación de la interfaz de usuario de la lógica de negocios.

También es posible que los objetos Command controlen la habilitación y deshabilitación de los elementos Button. Por ejemplo, supongamos que deseas limitar el intervalo de valores numéricos entre 210 y 2-10. Puede agregar otra función al constructor (llamado el argumento canExecute) que devuelve true si el Button debe ser habilitado. Aquí está la modificación del constructor CommandDemoViewModel:

class CommandDemoViewModel : INotifyPropertyChanged
{
    ···
    public CommandDemoViewModel()
    {
        MultiplyBy2Command = new Command(
            execute: () =>
            {
                Number *= 2;
                ((Command)MultiplyBy2Command).ChangeCanExecute();
                ((Command)DivideBy2Command).ChangeCanExecute();
            },
            canExecute: () => Number < Math.Pow(2, 10));

        DivideBy2Command = new Command(
            execute: () =>
            {
                Number /= 2;
                ((Command)MultiplyBy2Command).ChangeCanExecute();
                ((Command)DivideBy2Command).ChangeCanExecute();
            },
            canExecute: () => Number > Math.Pow(2, -10));
    }
    ···
}

Las llamadas al método ChangeCanExecute de Command son necesarias para que el método Command pueda llamar al método canExecute y determinar si Button debe deshabilitarse o no. Con este cambio de código, cuando el número alcanza el límite, Button se deshabilita:

Basic Button Command - Modified

Es posible que dos o más elementos Button estén vinculados a la misma propiedad ICommand. Los elementos Button se pueden distinguir mediante la propiedad CommandParameter de Button. En este caso, querrás usar la clase genérica Command<T>. El objeto CommandParameter se pasa como un argumento a los métodos execute y canExecute. Esta técnica se muestra en detalle en la sección Comandos básicos del artículo Interfaz de comandos.

El ejemplo ButtonDemos también utiliza esta técnica en su clase MainPage. El archivo MainPage.xaml contiene una Button para cada página de la muestra:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ButtonDemos"
             x:Class="ButtonDemos.MainPage"
             Title="Button Demos">
    <ScrollView>
        <FlexLayout Direction="Column"
                    JustifyContent="SpaceEvenly"
                    AlignItems="Center">

            <Button Text="Basic Button Click"
                    Command="{Binding NavigateCommand}"
                    CommandParameter="{x:Type local:BasicButtonClickPage}" />

            <Button Text="Code Button Click"
                    Command="{Binding NavigateCommand}"
                    CommandParameter="{x:Type local:CodeButtonClickPage}" />

            <Button Text="Basic Button Command"
                    Command="{Binding NavigateCommand}"
                    CommandParameter="{x:Type local:BasicButtonCommandPage}" />

            <Button Text="Press and Release Button"
                    Command="{Binding NavigateCommand}"
                    CommandParameter="{x:Type local:PressAndReleaseButtonPage}" />

            <Button Text="Button Appearance"
                    Command="{Binding NavigateCommand}"
                    CommandParameter="{x:Type local:ButtonAppearancePage}" />

            <Button Text="Toggle Button Demo"
                    Command="{Binding NavigateCommand}"
                    CommandParameter="{x:Type local:ToggleButtonDemoPage}" />

            <Button Text="Image Button Demo"
                    Command="{Binding NavigateCommand}"
                    CommandParameter="{x:Type local:ImageButtonDemoPage}" />

        </FlexLayout>
    </ScrollView>
</ContentPage>

Cada Button tiene su propiedad Command vinculada a una propiedad llamada NavigateCommand, y el CommandParameter se establece en un Type objeto correspondiente a una de las clases de página en el proyecto.

Esa propiedad NavigateCommand es de tipo ICommand y está definida en el archivo code-behind:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        NavigateCommand = new Command<Type>(async (Type pageType) =>
        {
            Page page = (Page)Activator.CreateInstance(pageType);
            await Navigation.PushAsync(page);
        });

        BindingContext = this;
    }

    public ICommand NavigateCommand { private set; get; }
}

El constructor inicializa la propiedad NavigateCommand a un objeto Command<Type> porque Type es el tipo del objeto CommandParameter establecido en el archivo XAML. Esto significa que el método execute tiene un argumento de tipo Type que corresponde a este objeto CommandParameter. La función crea una instancia de la página y luego navega hasta ella.

Observe que el constructor concluye estableciendo BindingContext en sí mismo. Esto es necesario para que las propiedades del archivo XAML se vinculen a la propiedad NavigateCommand.

Presionar y soltar el botón

Además del evento Clicked, Button también define los eventos Pressed y Released. El evento Pressed se produce cuando un dedo presiona Button, o se presiona un botón del ratón con el puntero situado sobre el Button. El evento Released se produce cuando se suelta el dedo o el botón del mouse. Generalmente, un evento Clicked también se activa al mismo tiempo que el evento Released, pero si el dedo o el puntero del ratón se desliza fuera de la superficie del Button antes de ser liberado, el evento Clicked podría no ocurrir.

Los eventos Pressed y Released no se utilizan a menudo, pero pueden emplearse para fines especiales, como se demuestra en la página Botón de seleccionar y publicar. El archivo XAML contiene un Label y un Button con controladores adjuntos para los eventos Pressed y Released:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ButtonDemos.PressAndReleaseButtonPage"
             Title="Press and Release Button">
    <StackLayout>

        <Label x:Name="label"
               Text="Press and hold the Button below"
               FontSize="Large"
               VerticalOptions="CenterAndExpand"
               HorizontalOptions="Center" />

        <Button Text="Press to Rotate Text!"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="Center"
                Pressed="OnButtonPressed"
                Released="OnButtonReleased" />

    </StackLayout>
</ContentPage>

El archivo de código subyacente anima Label cuando se produce un evento Pressed, pero suspende la rotación cuando se produce un evento Released:

public partial class PressAndReleaseButtonPage : ContentPage
{
    bool animationInProgress = false;
    Stopwatch stopwatch = new Stopwatch();

    public PressAndReleaseButtonPage ()
    {
        InitializeComponent ();
    }

    void OnButtonPressed(object sender, EventArgs args)
    {
        stopwatch.Start();
        animationInProgress = true;

        Device.StartTimer(TimeSpan.FromMilliseconds(16), () =>
        {
            label.Rotation = 360 * (stopwatch.Elapsed.TotalSeconds % 1);

            return animationInProgress;
        });
    }

    void OnButtonReleased(object sender, EventArgs args)
    {
        animationInProgress = false;
        stopwatch.Stop();
    }
}

El resultado es que el Label solo gira mientras un dedo está en contacto con el Button, y se detiene cuando se levanta el dedo:

Press and Release Button

Este tipo de comportamiento tiene aplicaciones en los juegos: Mantener el dedo sobre un Button puede hacer que el objeto se mueva en una dirección determinada.

Apariencia del botón

Button hereda o define varias propiedades que afectan a su aspecto:

Nota:

La clase Button también tiene Margin y propiedades Padding que controlan el comportamiento de diseño de Button. Para obtener más información, vea Márgenes y relleno.

Los efectos de seis de estas propiedades (excluyendo FontFamily yFontAttributes) se muestran en la página Apariencia de los botones. Otra propiedad, Image, se trata en la sección Uso de mapas de bits con el botón.

Todas las vistas y vinculaciones de datos de la página Apariencia de los botones se definen en el archivo XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ButtonDemos"
             x:Class="ButtonDemos.ButtonAppearancePage"
             Title="Button Appearance">
    <StackLayout>
        <Button x:Name="button"
                Text="Button"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="Center"
                TextColor="{Binding Source={x:Reference textColorPicker},
                                    Path=SelectedItem.Color}"
                BackgroundColor="{Binding Source={x:Reference backgroundColorPicker},
                                          Path=SelectedItem.Color}"
                BorderColor="{Binding Source={x:Reference borderColorPicker},
                                      Path=SelectedItem.Color}" />

        <StackLayout BindingContext="{x:Reference button}"
                     Padding="10">

            <Slider x:Name="fontSizeSlider"
                    Maximum="48"
                    Minimum="1"
                    Value="{Binding FontSize}" />

            <Label Text="{Binding Source={x:Reference fontSizeSlider},
                                  Path=Value,
                                  StringFormat='FontSize = {0:F0}'}"
                   HorizontalTextAlignment="Center" />

            <Slider x:Name="borderWidthSlider"
                    Minimum="-1"
                    Maximum="12"
                    Value="{Binding BorderWidth}" />

            <Label Text="{Binding Source={x:Reference borderWidthSlider},
                                  Path=Value,
                                  StringFormat='BorderWidth = {0:F0}'}"
                   HorizontalTextAlignment="Center" />

            <Slider x:Name="cornerRadiusSlider"
                    Minimum="-1"
                    Maximum="24"
                    Value="{Binding CornerRadius}" />

            <Label Text="{Binding Source={x:Reference cornerRadiusSlider},
                                  Path=Value,
                                  StringFormat='CornerRadius = {0:F0}'}"
                   HorizontalTextAlignment="Center" />

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <Grid.Resources>
                    <Style TargetType="Label">
                        <Setter Property="VerticalOptions" Value="Center" />
                    </Style>
                </Grid.Resources>

                <Label Text="Text Color:"
                       Grid.Row="0" Grid.Column="0" />

                <Picker x:Name="textColorPicker"
                        ItemsSource="{Binding Source={x:Static local:NamedColor.All}}"
                        ItemDisplayBinding="{Binding FriendlyName}"
                        SelectedIndex="0"
                        Grid.Row="0" Grid.Column="1" />

                <Label Text="Background Color:"
                       Grid.Row="1" Grid.Column="0" />

                <Picker x:Name="backgroundColorPicker"
                        ItemsSource="{Binding Source={x:Static local:NamedColor.All}}"
                        ItemDisplayBinding="{Binding FriendlyName}"
                        SelectedIndex="0"
                        Grid.Row="1" Grid.Column="1" />

                <Label Text="Border Color:"
                       Grid.Row="2" Grid.Column="0" />

                <Picker x:Name="borderColorPicker"
                        ItemsSource="{Binding Source={x:Static local:NamedColor.All}}"
                        ItemDisplayBinding="{Binding FriendlyName}"
                        SelectedIndex="0"
                        Grid.Row="2" Grid.Column="1" />
            </Grid>
        </StackLayout>
    </StackLayout>
</ContentPage>

El Button en la parte superior de la página tiene sus tres propiedades Color vinculadas a elementos Picker en la parte inferior de la página. Los elementos Picker son colores de la clase NamedColor incluida en el proyecto. Tres elementos Slider contienen vínculos bidireccionales a los FontSize, BorderWidth, y propiedades CornerRadius de los Button.

Este programa le permite experimentar con combinaciones de todas estas propiedades:

Button Appearance

Para ver el borde Button, tendrá que establecer BorderColor a un valor distinto de Default, y el BorderWidth a un valor positivo.

En iOS, notará que los bordes de gran anchura se entrometen en el interior del Button e interfieren con la visualización del texto. Si decide utilizar un borde con un iOS Button, probablemente querrá comenzar y terminar la propiedad Text con espacios para mantener su visibilidad.

En UWP, la selección de un CornerRadius que exceda la mitad de la altura de la Button plantea una excepción.

Estados visuales del botón

Button tiene un PressedVisualState que se puede utilizar para iniciar un cambio visual a la Button cuando se pulsa por el usuario, siempre que esté habilitado.

En el ejemplo XAML siguiente se muestra cómo definir un estado visual para el estado Pressed:

<Button Text="Click me!"
        ...>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal">
                <VisualState.Setters>
                    <Setter Property="Scale"
                            Value="1" />
                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Pressed">
                <VisualState.Setters>
                    <Setter Property="Scale"
                            Value="0.8" />
                </VisualState.Setters>
            </VisualState>

        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Button>

PressedVisualState especifica que cuando se presiona Button, su propiedad Scale se cambiará del valor predeterminado de 1 a 0,8. NormalVisualState especifica que cuando Button está en un estado normal, su propiedad Scale se establecerá en 1. Por lo tanto, el efecto general es que, cuando se presiona Button, se vuelve a escalar para que sea ligeramente más pequeño y, cuando se libera Button, se vuelve a escalar a su tamaño predeterminado.

Para más información sobre los estados visuales, consulte el Xamarin.FormsAdministrador de estado visual.

Crear un botón de alternancia

Es posible sub clasificar Button para que funcione como un interruptor activar/desactivar: pulse el botón una vez para encenderlo y vuelva a pulsarlo para apagarlo.

La siguiente clase ToggleButton deriva de Button y define un nuevo evento llamado Toggled y una propiedad booleana llamada IsToggled. Estas son las mismas dos propiedades definidas por Xamarin.FormsSwitch:

class ToggleButton : Button
{
    public event EventHandler<ToggledEventArgs> Toggled;

    public static BindableProperty IsToggledProperty =
        BindableProperty.Create("IsToggled", typeof(bool), typeof(ToggleButton), false,
                                propertyChanged: OnIsToggledChanged);

    public ToggleButton()
    {
        Clicked += (sender, args) => IsToggled ^= true;
    }

    public bool IsToggled
    {
        set { SetValue(IsToggledProperty, value); }
        get { return (bool)GetValue(IsToggledProperty); }
    }

    protected override void OnParentSet()
    {
        base.OnParentSet();
        VisualStateManager.GoToState(this, "ToggledOff");
    }

    static void OnIsToggledChanged(BindableObject bindable, object oldValue, object newValue)
    {
        ToggleButton toggleButton = (ToggleButton)bindable;
        bool isToggled = (bool)newValue;

        // Fire event
        toggleButton.Toggled?.Invoke(toggleButton, new ToggledEventArgs(isToggled));

        // Set the visual state
        VisualStateManager.GoToState(toggleButton, isToggled ? "ToggledOn" : "ToggledOff");
    }
}

El constructor ToggleButton adjunta un controlador al evento Clicked para que pueda cambiar el valor de la propiedad IsToggled. El método OnIsToggledChanged activa el evento Toggled.

La última línea del método OnIsToggledChanged llama al método VisualStateManager.GoToState estático con las dos cadenas de texto «ToggledOn» y «ToggledOff». Puede leer más sobre este método y sobre cómo su aplicación puede responder a los estados visuales en el artículo El Xamarin.Forms administrador de estados visuales.

Debido a que ToggleButton hace la llamada a VisualStateManager.GoToState, la clase en sí no necesita incluir ninguna facilidad adicional para cambiar la apariencia del botón basándose en su estado IsToggled. Eso es responsabilidad del XAML que aloja el ToggleButton.

La página de Demostración del botón Alternar contiene dos instancias de ToggleButton, incluido el marcado Administrador de estado visual que establece el Text, BackgroundColor, y TextColor del botón en función del estado visual:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ButtonDemos"
             x:Class="ButtonDemos.ToggleButtonDemoPage"
             Title="Toggle Button Demo">

    <ContentPage.Resources>
        <Style TargetType="local:ToggleButton">
            <Setter Property="VerticalOptions" Value="CenterAndExpand" />
            <Setter Property="HorizontalOptions" Value="Center" />
        </Style>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <local:ToggleButton Toggled="OnItalicButtonToggled">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ToggleStates">
                    <VisualState Name="ToggledOff">
                        <VisualState.Setters>
                            <Setter Property="Text" Value="Italic Off" />
                            <Setter Property="BackgroundColor" Value="#C0C0C0" />
                            <Setter Property="TextColor" Value="Black" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState Name="ToggledOn">
                        <VisualState.Setters>
                            <Setter Property="Text" Value=" Italic On " />
                            <Setter Property="BackgroundColor" Value="#404040" />
                            <Setter Property="TextColor" Value="White" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </local:ToggleButton>

        <local:ToggleButton Toggled="OnBoldButtonToggled">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ToggleStates">
                    <VisualState Name="ToggledOff">
                        <VisualState.Setters>
                            <Setter Property="Text" Value="Bold Off" />
                            <Setter Property="BackgroundColor" Value="#C0C0C0" />
                            <Setter Property="TextColor" Value="Black" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState Name="ToggledOn">
                        <VisualState.Setters>
                            <Setter Property="Text" Value=" Bold On " />
                            <Setter Property="BackgroundColor" Value="#404040" />
                            <Setter Property="TextColor" Value="White" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </local:ToggleButton>

        <Label x:Name="label"
               Text="Just a little passage of some sample text that can be formatted in italic or boldface by toggling the two buttons."
               FontSize="Large"
               HorizontalTextAlignment="Center"
               VerticalOptions="CenterAndExpand" />

    </StackLayout>
</ContentPage>

Los controladores de eventos Toggled se encuentran en el archivo de código subyacente. Son responsables de establecer la propiedad FontAttributes de la Label basada en el estado de los botones:

public partial class ToggleButtonDemoPage : ContentPage
{
    public ToggleButtonDemoPage ()
    {
        InitializeComponent ();
    }

    void OnItalicButtonToggled(object sender, ToggledEventArgs args)
    {
        if (args.Value)
        {
            label.FontAttributes |= FontAttributes.Italic;
        }
        else
        {
            label.FontAttributes &= ~FontAttributes.Italic;
        }
    }

    void OnBoldButtonToggled(object sender, ToggledEventArgs args)
    {
        if (args.Value)
        {
            label.FontAttributes |= FontAttributes.Bold;
        }
        else
        {
            label.FontAttributes &= ~FontAttributes.Bold;
        }
    }
}

Aquí está el programa funcionando en iOS, Android y la UWP:

Toggle Button Demo

Uso de mapas de bits con botones.

La clase Button define una propiedad ImageSource que permite mostrar una imagen de mapa de bits en el Button, ya sea sola o en combinación con texto. También puedes especificar cómo se organizan el texto y la imagen.

La propiedad ImageSource es de tipo ImageSource, lo que significa que los mapas de bits se pueden cargar desde un archivo, un recurso incrustado, un URI o una secuencia.

Nota:

Aunque un Button puede cargar un GIF animado, solo mostrará el primer fotograma del GIF.

Cada plataforma compatible con Xamarin.Forms permite almacenar imágenes en varios tamaños para las diferentes resoluciones de píxeles de los distintos dispositivos en los que podría ejecutarse la aplicación. Estos mapas de bits múltiples se nombran o almacenan de forma que el sistema operativo pueda elegir el que mejor se adapte a la resolución de la pantalla de vídeo del dispositivo.

Para un mapa de bits en un Button, el mejor tamaño suele estar entre 32 y 64 unidades independientes del dispositivo, dependiendo de lo grande que quieras que sea. Las imágenes utilizadas en este ejemplo se basan en un tamaño de 48 unidades independientes del dispositivo.

En el proyecto iOS, la carpeta Recursos contiene tres tamaños de esta imagen:

  • Un mapa de bits cuadrado de 48 píxeles almacenado como /Resources/MonkeyFace.png
  • Un mapa de bits cuadrado de 96 píxeles almacenado como /Resource/MonkeyFace@2x.png
  • Un mapa de bits cuadrado de 144 píxeles almacenado como /Resource/MonkeyFace@3x.png

A los tres mapas de bits se les asignó una Acción de compilaciónBundleResource.

En el proyecto Android, todos los mapas de bits tienen el mismo nombre, pero se almacenan en diferentes sub carpetas de la carpeta de Recursos:

  • Un mapa de bits cuadrado de 72 píxeles almacenado como /Resources/drawable-hdpi/MonkeyFace.png
  • Un mapa de bits cuadrado de 96 píxeles almacenado como /Resources/drawable-xhdpi/MonkeyFace.png
  • Un mapa de bits cuadrado de 144 píxeles almacenado como /Resources/drawable-xxhdpi/MonkeyFace.png
  • Un mapa de bits cuadrado de 192 píxeles almacenado como /Resources/drawable-xxxhdpi/MonkeyFace.png

A estos se les asignó una Acción de compilación de AndroidResource.

En el proyecto UWP, los mapas de bits pueden almacenarse en cualquier parte del proyecto, pero generalmente se almacenan en una carpeta personalizada o en la carpeta de Activos existentes. El proyecto UWP contiene estos mapas de bits:

  • Un mapa de bits cuadrado de 48 píxeles almacenado como /Assets/MonkeyFace.scale-100.png
  • Un mapa de bits cuadrado de 96 píxeles almacenado como /Assets/MonkeyFace.scale-200.png
  • Un mapa de bits cuadrado de 192 píxeles almacenado como /Assets/MonkeyFace.scale-400.png

A todos ellos se les dio una Acción de compilación de Contenido.

Puedes especificar cómo se organizan las propiedades Text y ImageSource en Button mediante la propiedad ContentLayout de Button. Esta propiedad es de tipo ButtonContentLayout, que es una clase incrustada en Button. El constructor tiene dos argumentos:

  • Un miembro de la enumeración ImagePosition: Left, Top, Right o Bottom que indica cómo aparece el mapa de bits en relación con el texto.
  • Un valor double para el espaciado entre el mapa de bits y el texto.

Los valores predeterminados son Left y 10 unidades. Dos propiedades de solo lectura de ButtonContentLayout llamada Position y Spacing proporcionan los valores de dichas propiedades.

En código, puede crear una Button y establecer la propiedad ContentLayout así:

Button button = new Button
{
    Text = "button text",
    ImageSource = new FileImageSource
    {
        File = "image filename"
    },
    ContentLayout = new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Right, 20)
};

En XAML, es necesario especificar solo el miembro de la enumeración, o el espaciado, o ambos en cualquier orden separados por comas:

<Button Text="button text"
        ImageSource="image filename"
        ContentLayout="Right, 20" />

La página de Demostración del botón de imagen utiliza OnPlatform para especificar diferentes nombres de archivo para los archivos de mapa de bits de iOS, Android y UWP. Si desea utilizar el mismo nombre de archivo para cada plataforma y evitar el uso de OnPlatform, tendrá que almacenar los mapas de bits UWP en el directorio raíz del proyecto.

El primero Button en la página de Demostración del botón de imagen establece la Image propiedad pero no la propiedad Text:

<Button>
    <Button.ImageSource>
        <OnPlatform x:TypeArguments="ImageSource">
            <On Platform="iOS, Android" Value="MonkeyFace.png" />
            <On Platform="UWP" Value="Assets/MonkeyFace.png" />
        </OnPlatform>
    </Button.ImageSource>
</Button>

Si los mapas de bits UWP se almacenan en el directorio raíz del proyecto, este marcado puede simplificarse considerablemente:

<Button ImageSource="MonkeyFace.png" />

Para evitar un montón de marcado repetitivo en el archivo ImageButtonDemo.xaml, también Style se define un implícito para establecer la propiedad ImageSource. Style se aplica automáticamente a otros cinco elementos Button. Aquí está el archivo XAML completo:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ButtonDemos.ImageButtonDemoPage">

    <FlexLayout Direction="Column"
                JustifyContent="SpaceEvenly"
                AlignItems="Center">

        <FlexLayout.Resources>
            <Style TargetType="Button">
                <Setter Property="ImageSource">
                    <OnPlatform x:TypeArguments="ImageSource">
                        <On Platform="iOS, Android" Value="MonkeyFace.png" />
                        <On Platform="UWP" Value="Assets/MonkeyFace.png" />
                    </OnPlatform>
                </Setter>
            </Style>
        </FlexLayout.Resources>

        <Button>
            <Button.ImageSource>
                <OnPlatform x:TypeArguments="ImageSource">
                    <On Platform="iOS, Android" Value="MonkeyFace.png" />
                    <On Platform="UWP" Value="Assets/MonkeyFace.png" />
                </OnPlatform>
            </Button.ImageSource>
        </Button>

        <Button Text="Default" />

        <Button Text="Left - 10"
                ContentLayout="Left, 10" />

        <Button Text="Top - 10"
                ContentLayout="Top, 10" />

        <Button Text="Right - 20"
                ContentLayout="Right, 20" />

        <Button Text="Bottom - 20"
                ContentLayout="Bottom, 20" />
    </FlexLayout>
</ContentPage>

Los últimos cuatro elementos Button hacen uso de la propiedad ContentLayout para especificar una posición y un espaciado del texto y el mapa de bits:

Image Button Demo

Ya ha visto las distintas formas en que puede controlar los eventos Button y cambiar la apariencia Button.