Integer-Based Extended Slider for Xamarin.Forms

I was trolling the Xamarin.Forms forum when I saw this question:

Can you limit a Slider to only allow integer values? (Hopefully snapping to the next integer)

I dug around a bit I found this solution on StackOverflow.  I did a quick and dirty conversion to Xamarin.Forms, but wanted to make it a little better, more Xamarin.Formy.

So, here is the answer to this question:

 

ExtendedSlider Class

The first thing we need to do is create a new Slider class that has our “stop” value, which we do using the following code:

public class ExtendedSlider : Slider
{
    public static readonly BindableProperty CurrentStepValueProperty = 
           BindableProperty.Create<ExtendedSlider, double>(p => p.StepValue, 1.0f);

    public double StepValue
    {
        get { return (double)GetValue(CurrentStepValueProperty); }

        set { SetValue(CurrentStepValueProperty, value); }
    }

    public ExtendedSlider()
    {
        ValueChanged += OnSliderValueChanged;
    }

    private void OnSliderValueChanged(object sender, ValueChangedEventArgs e)
    {
        var newStep = Math.Round(e.NewValue / StepValue);

        Value = newStep * StepValue;
    }
}

All this really does is converts the current slider value from a double, into an integer.

 

Demonstration Page

Here is a fully functional demonstration page:

public class SliderDemo : ContentPage
{
    public SliderDemo()
    {
        var sliderMain = new ExtendedSlider
        {
            Minimum = 0.0f,
            Maximum = 5.0f,
            Value = 0.0f,
            StepValue = 1.0f,
            HorizontalOptions = LayoutOptions.FillAndExpand,
        };

        var labelCurrentValue = new Label
        {
            HorizontalOptions = LayoutOptions.CenterAndExpand,
            BindingContext = sliderMain,
        };

        labelCurrentValue.SetBinding(Label.TextProperty, 
                                        new Binding("Value", BindingMode.OneWay, 
                                            null, null, "Current Value: {0}"));

        var grid = new Grid
        {
            Padding = 10,
            RowDefinitions =
            {
                new RowDefinition {Height = GridLength.Auto},
            },
            ColumnDefinitions =
            {
                new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)},
                new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)},
                new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)},
                new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)},
                new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)},
            },
        };

        for (var i = 0; i < 6; i++)
        {
            var label = new Label
            {
                Text = i.ToString(CultureInfo.InvariantCulture),
            };

            var tapValue = i; // Prevent modified closure

            label.GestureRecognizers.Add(new TapGestureRecognizer
            {
                Command = new Command(() => { sliderMain.Value = tapValue; }),
                NumberOfTapsRequired = 1
            });

            grid.Children.Add(label, i, 0);
        }

        Content = new StackLayout
        {
            Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 10),
            Children = { grid, sliderMain, labelCurrentValue },
            Orientation = StackOrientation.Vertical,
            HorizontalOptions = LayoutOptions.FillAndExpand,
            VerticalOptions = LayoutOptions.FillAndExpand
        };
    }
}

If anyone has a better way of doing this, then please let me know.

I’ll probably add this to the Xamarin.Forms Labs project sometime this week.

Leave a Reply 2 comments