Xamarin.Forms Control deslizante
Usar un Slider para seleccionar desde un rango de valores continuos.
El Xamarin.FormsSlider
es una barra horizontal que el usuario puede manipular para seleccionar un valor double
desde un rango de valores continuos.
El Slider
define tres propiedades del tipo double
:
Minimum
es el valor mínimo del rango, con un valor predeterminado de 0.Maximum
es el máximo del rango, con un valor predeterminado de 1.Value
es el valor del slider, que puede oscilar entreMinimum
yMaximum
y tiene un valor predeterminado de 0.
Las tres propiedades están respaldadas por objetos BindableProperty
. La propiedad Value
tiene un modo de enlace predeterminado de BindingMode.TwoWay
, lo que significa que es adecuado como origen de enlace en una aplicación que usa la arquitectura Model-View-ViewModel (MVVM).
Advertencia
Internamente, Slider
garantiza que Maximum
sea menor que Minimum
. Si Minimum
o Maximum
se establecen alguna vez para que Maximum
no sea menor que Minimum
, se genera una excepción. Consulte la siguiente sección Precauciones para obtener más información sobre cómo establecer las propiedades Minimum
y Maximum
.
El Slider
reprime la propiedad Value
para que esté entre Minimum
y Maximum
, ambos inclusive. Si la propiedad Minimum
se establece en un valor mayor que la propiedad Value
, Slider
establece la propiedad Value
en Minimum
. Del mismo modo, si Maximum
se establece en un valor menor que Value
, Slider
establece la propiedad Value
en Maximum
.
Slider
define un evento de ValueChanged
que se desencadena cuando cambia el Value
, ya sea mediante la manipulación del usuario de la Slider
o cuando el programa establece la propiedad Value
directamente. También se desencadena un evento ValueChanged
cuando la propiedad Value
se coacciona como se describe en el párrafo anterior.
El objeto ValueChangedEventArgs
que acompaña al evento ValueChanged
tiene dos propiedades, de tipo double
: OldValue
y NewValue
. En el momento en que se desencadena el evento, el valor NewValue
es el mismo que la propiedad Value
del objetoSlider
.
Slider
también define eventos DragStarted
y DragCompleted
, que se desencadenan al principio y al final de la acción de arrastrar. A diferencia del evento ValueChanged
, los eventos DragStarted
y DragCompleted
solo se desencadenan a través de la manipulación por parte del usuario del Slider
. Cuando se desencadena el evento DragStarted
, se ejecuta DragStartedCommand
de tipo ICommand
. Del mismo modo, cuando se desencadena el evento DragCompleted
, se ejecuta DragCompletedCommand
de tipo ICommand
.
Advertencia
No uses opciones de diseño horizontal sin restricciones de Center
, Start
o End
con Slider
. Tanto en Android como en UWP, el Slider
se contrae a una barra de longitud cero y, en iOS, la barra es muy corta. Mantén la configuración predeterminada HorizontalOptions
de Fill
y no uses un ancho de Auto
al colocar Slider
en un diseño Grid
.
El Slider
también define varias propiedades que afectan a su apariencia:
MinimumTrackColor
es el color de la barra en el lado izquierdo del pulgar.MaximumTrackColor
es el color de la barra en el lado derecho del pulgar.ThumbColor
es el color del pulgar.ThumbImageSource
es la imagen que se va a usar para el pulgar, de tipoImageSource
.
Nota:
Las propiedades ThumbColor
y ThumbImageSource
se excluyen mutuamente. Si se establecen ambas propiedades, la propiedad ThumbImageSource
tendrá precedencia.
Código y marcado del Slider básico
La muestra comienza con tres páginas que son funcionalmente idénticas, pero se implementan de maneras diferentes. La primera página usa solo código de C#, el segundo usa XAML con un controlador de eventos en el código y el tercero puede evitar el controlador de eventos mediante el enlace de datos en el archivo XAML.
Creación de un Slider en el código
En la página Código deslizante básico se muestra cómo crear un Slider
y dos objetos Label
en el código:
public class BasicSliderCodePage : ContentPage
{
public BasicSliderCodePage()
{
Label rotationLabel = new Label
{
Text = "ROTATING TEXT",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
Label displayLabel = new Label
{
Text = "(uninitialized)",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
Slider slider = new Slider
{
Maximum = 360
};
slider.ValueChanged += (sender, args) =>
{
rotationLabel.Rotation = slider.Value;
displayLabel.Text = String.Format("The Slider value is {0}", args.NewValue);
};
Title = "Basic Slider Code";
Padding = new Thickness(10, 0);
Content = new StackLayout
{
Children =
{
rotationLabel,
slider,
displayLabel
}
};
}
}
El Slider
se inicializa para tener una propiedad Maximum
de 360. El controlador ValueChanged
del Slider
usa la propiedad Value
del objeto slider
para establecer la propiedad Rotation
del primero Label
y usa el método String.Format
con la propiedad NewValue
de los argumentos del evento para establecer la propiedad Text
del segundo Label
. Estos dos enfoques para obtener el valor actual de Slider
son intercambiables.
Este es el programa que se ejecuta en dispositivos iOS y Android:
El segundo Label
muestra el texto "(sin inicializar)" hasta que Slider
se manipula, lo que hace que se desencadene el primer evento ValueChanged
. Observe que el número de posiciones decimales que se muestran es diferente para cada plataforma. Estas diferencias están relacionadas con las implementaciones de la plataforma del Slider
y se describen más adelante en este artículo en la sección Diferencias de implementación de la plataforma.
Creación de un Slider en XAML
La página XAML del slider básico es funcionalmente la misma que código del slider básico, pero se implementa principalmente en XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SliderDemos.BasicSliderXamlPage"
Title="Basic Slider XAML"
Padding="10, 0">
<StackLayout>
<Label x:Name="rotatingLabel"
Text="ROTATING TEXT"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Slider Maximum="360"
ValueChanged="OnSliderValueChanged" />
<Label x:Name="displayLabel"
Text="(uninitialized)"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
El archivo de código subyacente contiene el controlador para el evento ValueChanged
:
public partial class BasicSliderXamlPage : ContentPage
{
public BasicSliderXamlPage()
{
InitializeComponent();
}
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
double value = args.NewValue;
rotatingLabel.Rotation = value;
displayLabel.Text = String.Format("The Slider value is {0}", value);
}
}
También es posible que el controlador de eventos obtenga el Slider
que desencadena el evento a través del argumento sender
. La propiedad Value
contiene el valor actual:
double value = ((Slider)sender).Value;
Si el objeto Slider
recibió un nombre en el archivo XAML con un atributo x:Name
(por ejemplo, "slider"), el controlador de eventos podría hacer referencia directamente a ese objeto:
double value = slider.Value;
Enlace de datos del Slider
La página Enlaces de slider básico muestra cómo escribir un programa casi equivalente que elimina el controlador de eventos de Value
mediante enlace de datos:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SliderDemos.BasicSliderBindingsPage"
Title="Basic Slider Bindings"
Padding="10, 0">
<StackLayout>
<Label Text="ROTATING TEXT"
Rotation="{Binding Source={x:Reference slider},
Path=Value}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Slider x:Name="slider"
Maximum="360" />
<Label x:Name="displayLabel"
Text="{Binding Source={x:Reference slider},
Path=Value,
StringFormat='The Slider value is {0:F0}'}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
La propiedad Rotation
del primer Label
está enlazada a la propiedad Value
de Slider
, tal como es la propiedad Text
del segundo Label
con una especificación StringFormat
. La página Enlaces de slider básico funciona de manera un poco diferente a las dos páginas anteriores: cuando aparece la página por primera vez, la segunda Label
muestra la cadena de texto con el valor. Esta es una ventaja de usar el enlace de datos. Para mostrar texto sin enlace de datos, tendrías que inicializar específicamente la propiedad Text
de Label
o simular una activación del evento ValueChanged
mediante una llamada al controlador de eventos desde el constructor de clase.
Precauciones
El valor de la propiedad Minimum
debe ser siempre inferior que el valor de la propiedad Maximum
. El siguiente fragmento de código hace que Slider
genere una excepción:
// Throws an exception!
Slider slider = new Slider
{
Minimum = 10,
Maximum = 20
};
El compilador de C# genera código que establece estas dos propiedades en secuencia y, cuando la propiedad Minimum
se establece en 10, es mayor que el valor predeterminado Maximum
de 1. Puedes evitar la excepción en este caso estableciendo primero la propiedad Maximum
:
Slider slider = new Slider
{
Maximum = 20,
Minimum = 10
};
Establecer Maximum
en 20 no es un problema porque es mayor que el valor predeterminado Minimum
de 0. Cuando Minimum
se establece, el valor es menor que el valor Maximum
de 20.
El mismo problema existe en XAML. Establece las propiedades en un orden que garantice que Maximum
siempre sea mayor que Minimum
:
<Slider Maximum="20"
Minimum="10" ... />
Luego, puedes establecer los valores Minimum
y Maximum
en números negativos, pero solo en un orden donde Minimum
siempre sea menor que Maximum
:
<Slider Minimum="-20"
Maximum="-10" ... />
La propiedad Value
siempre es mayor o igual que el valor Minimum
y menor o igual que Maximum
. Si Value
se establece en un valor fuera de ese intervalo, el valor se reprimirá para que se encuentre dentro del intervalo, pero no se genera ninguna excepción. Por ejemplo, este código no generará una excepción:
Slider slider = new Slider
{
Value = 10
};
En su lugar, la propiedad Value
se convierte en el valor Maximum
de 1.
Este es un fragmento de código que se muestra anteriormente:
Slider slider = new Slider
{
Maximum = 20,
Minimum = 10
};
Cuando Minimum
se establece en 10, Value
también se establece en 10.
Si un controlador de eventos ValueChanged
se ha adjuntado en el momento en que la propiedadValue
está coaccionada a algo distinto de su valor predeterminado de 0, se desencadena un evento ValueChanged
. Este es un fragmento de código XAML:
<Slider ValueChanged="OnSliderValueChanged"
Maximum="20"
Minimum="10" />
Cuando Minimum
se establece en 10, Value
también se establece en 10 y el evento ValueChanged
se desencadena. Esto puede ocurrir antes de que se haya construido el resto de la página y el controlador podría intentar hacer referencia a otros elementos de la página que aún no se han creado. Es posible que desees agregar código al controlador ValueChanged
que comprueba si hay valores null
de otros elementos en la página. O bien, puedes establecer el controlador de eventos ValueChanged
después de inicializar los valores Slider
.
Diferencias de implementación de la plataforma
Las capturas de pantalla mostradas anteriormente muestran el valor del Slider
con un número diferente de puntos decimales. Esto se relaciona con cómo se implementa el Slider
en las plataformas Android y UWP.
La implementación de Android
La implementación de Android de Slider
se basa en Android SeekBar
y siempre establece la propiedad Max
en 1000. Esto significa que el Slider
en Android tiene solo 1001 valores discretos. Si establece el Slider
para que tenga un Minimum
de 0 y un Maximum
de 5000, a medida que se manipula la Slider
, la propiedad Value
tiene valores de 0, 5, 10, 15, etc.
La implementación de UWP
La implementación de UWP de Slider
se basa en el control Slider
de UWP. La StepFrequency
propiedad de UWP Slider
se establece en la diferencia de las propiedades Maximum
y Minimum
divididas por 10, pero no mayores que 1.
Por ejemplo, para el rango predeterminado de 0 a 1, la propiedad StepFrequency
se establece en 0,1. A medida que se manipula el Slider
, la propiedad Value
está restringida a 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 y 1.0. Cuando la diferencia entre las propiedades Maximum
y Minimum
es 10 o superior, StepFrequency
se establece en 1 y la propiedad Value
tiene valores enteros.
La solución StepSlider
Se analiza un StepSlider
más versátil en el capítulo 27 de . Representadores personalizados del libro Creación de aplicaciones móviles con Xamarin.Forms. El StepSlider
es similar a Slider
, pero agrega una propiedad Steps
para especificar el número de valores entre Minimum
y Maximum
.
Sliders para la selección de colores
Las dos páginas finales del ejemplo usan tres instancias Slider
para la selección de colores. La primera página controla todas las interacciones del archivo de código subyacente, mientras que la segunda página muestra cómo usar el enlace de datos con un ViewModel.
Control de Sliders en el archivo de código subyacente
La página Sliders de color RGB crea una instancia de un BoxView
para mostrar un color, tres instancias de Slider
para seleccionar los componentes rojo, verde y azul del color y tres elementos Label
para mostrar esos valores de color:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SliderDemos.RgbColorSlidersPage"
Title="RGB Color Sliders">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Slider">
<Setter Property="Maximum" Value="255" />
</Style>
<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="10">
<BoxView x:Name="boxView"
Color="Black"
VerticalOptions="FillAndExpand" />
<Slider x:Name="redSlider"
ValueChanged="OnSliderValueChanged" />
<Label x:Name="redLabel" />
<Slider x:Name="greenSlider"
ValueChanged="OnSliderValueChanged" />
<Label x:Name="greenLabel" />
<Slider x:Name="blueSlider"
ValueChanged="OnSliderValueChanged" />
<Label x:Name="blueLabel" />
</StackLayout>
</ContentPage>
Un Style
proporciona a los tres elementos Slider
un intervalo de 0 a 255. Los elementos Slider
comparten el mismo controlador de ValueChanged
, que se implementa en el archivo de código subyacente:
public partial class RgbColorSlidersPage : ContentPage
{
public RgbColorSlidersPage()
{
InitializeComponent();
}
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
if (sender == redSlider)
{
redLabel.Text = String.Format("Red = {0:X2}", (int)args.NewValue);
}
else if (sender == greenSlider)
{
greenLabel.Text = String.Format("Green = {0:X2}", (int)args.NewValue);
}
else if (sender == blueSlider)
{
blueLabel.Text = String.Format("Blue = {0:X2}", (int)args.NewValue);
}
boxView.Color = Color.FromRgb((int)redSlider.Value,
(int)greenSlider.Value,
(int)blueSlider.Value);
}
}
La primera sección establece la propiedad Text
de una de las instancias de Label
en una cadena de texto corta que indica el valor del Slider
en hexadecimal. A continuación, se accede a las tres instancias de Slider
para crear un valor de Color
a partir de los componentes RGB:
Enlazar el control deslizante a un ViewModel
La página Sliders de color HSL muestra cómo usar un ViewModel para realizar los cálculos utilizados para crear un valor de Color
a partir de valores de tono, saturación y luminosidad. Al igual que todos los ViewModels, la clase HSLColorViewModel
implementa la interfaz INotifyPropertyChanged
y desencadena un evento PropertyChanged
cada vez que una de las propiedades cambia:
public class HslColorViewModel : INotifyPropertyChanged
{
Color color;
public event PropertyChangedEventHandler PropertyChanged;
public double Hue
{
set
{
if (color.Hue != value)
{
Color = Color.FromHsla(value, color.Saturation, color.Luminosity);
}
}
get
{
return color.Hue;
}
}
public double Saturation
{
set
{
if (color.Saturation != value)
{
Color = Color.FromHsla(color.Hue, value, color.Luminosity);
}
}
get
{
return color.Saturation;
}
}
public double Luminosity
{
set
{
if (color.Luminosity != value)
{
Color = Color.FromHsla(color.Hue, color.Saturation, value);
}
}
get
{
return color.Luminosity;
}
}
public Color Color
{
set
{
if (color != value)
{
color = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Hue"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Saturation"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Luminosity"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));
}
}
get
{
return color;
}
}
}
ViewModels y la interfaz INotifyPropertyChanged
se describen en el artículo Enlace de datos.
El archivo HslColorSlidersPage.xaml crea una instancia del HslColorViewModel
y lo establece en la propiedad BindingContext
de la página. Esto permite que todos los elementos del archivo XAML se enlacen a las propiedades de ViewModel:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SliderDemos"
x:Class="SliderDemos.HslColorSlidersPage"
Title="HSL Color Sliders">
<ContentPage.BindingContext>
<local:HslColorViewModel Color="Chocolate" />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="10">
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</ContentPage>
A medida que se manipulan los elementos Slider
, los elementos BoxView
y Label
se actualizan desde ViewModel:
El componente StringFormat
de la extensión de marcado de Binding
se establece para un formato de "F2" para mostrar dos posiciones decimales. (El formato de cadena en los enlaces de datos se describe en el artículo Formato de cadena.) Sin embargo, la versión de UWP del programa se limita a los valores de 0, 0.1, 0.2, ... 0.9 y 1.0. Este es un resultado directo de la implementación de la Slider
para UWP tal y como se ha descrito anteriormente en la sección Diferencias de implementación de la plataforma.