Search code examples
c#xamlxamarinxamarin.formsvisual-studio-mac

How to databind a Slider to Viewmodel property in Xamarin forms?


I'm new to Xamarin forms and have only been working with it for a few months. I have a Xamarin forms app that will be used with Android and iOS and is using the MVVM structure. I have an ObservableCollection of objects in my ViewModel that I am iterating over with a custom repeater view that extends StackLayout and inside of the DataTemplate I have a custom Entry class that extends Entry that is binding its Text attribute to the object's Value property (a nullable double).

What I want to do next, is modify the template to hide the Entry tag if a certain boolean that I am adding into the model has a value of true, and then display a Slider and a Label, to show the value of the Slider.

So far I have been able to bind the Maximum/Minimum attributes to properties from my Viewmodel which is great, but whenever I try to bind the Value property to the Slider, my XAML won't load up, and I'm not getting any error. I also will need to implement functionality to only allow the user to slide over a given 'increment' (.1, 1, 2, etc). I would like to slide the Slider and once the user stops sliding, the Value property is set and stored in the model. I also need to it to pre-load if there is a value already existing, but those two I can get to later.

I think I am going about this incorrectly. I have tried searching through lots of different examples but I cannot for the life of me figure this out.

Here is what my XAML looks like for the slider/label

<Slider x:Name="slider" Value="{Binding Value}"
          Maximum="{Binding VM.Question.NumericMaxValue}"/>
   <Label x:Name="displayLabel"  
          Text="{Binding Source={x:Reference slider},  Path=Value,
          StringFormat='The slider value is {0:F0}'}" 
          HorizontalOptions="Center" VerticalOptions="CenterAndExpand" />

A simplified version of my Model because I don't want to put code out there that I maybe shouldn't.

public class MyItemModel
{
    private double? _value;

 public double? Value
   {
       get { return _value; }
       set
       {
           if (_value != value)
           {
               _value = value;


               if (!FormatAsTime)
               {
                   if (VM.Question.NumericMaxValue != -1 && Value > VM.Question.NumericMaxValue)
                   {
                       Value = VM.Question.NumericMaxValue;
                       return;
                   }
                   if (Value < VM.Question.NumericMinValue)
                   {
                       Value = VM.Question.NumericMinValue;
                       return;
                   }

                   RaisePropertyChanged(() => Value);
                   RaisePropertyChanged(() => HasValue);

                   SpinDownCommand.RaiseCanExecuteChanged();
                   SpinUpCommand.RaiseCanExecuteChanged();
               }
               else
               {
                   RaisePropertyChanged(() => Value);
                   RaisePropertyChanged(() => HasValue);
               }

               VM.UpdateCanGoNext();
           }
       }
   }
}

Solution

  • From the code you put (there are some missing key parts), but I presume you should use TwoWay binding:

    <Slider x:Name="slider" 
        Value="{Binding Value, Mode=TwoWay}"
        Maximum="{Binding VM.Question.NumericMaxValue}"
        />
    

    You also don't need to use a Nullable<double> for your property (as @jason mentionned it) because the slider control has always a value.

    And why your second binding (Maximum property) is equal to 'VM.Question...' ? Is it not the same for the Value binding ? ie:

    Value="{Binding VM.Value, Mode=TwoWay}" -- ??
    

    To update the value only when the control's thumb has finished to "move", you should use reactive extensions. It allow to you to manipulate events and select the "last" one of a serie for instance:

    Rx for Xamarin forms presentation

    I let you see and check for more details...