I created a DoubleIntSlider
custom view with 2 sliders inside it, with 2 BindableProperties
associated one to each Slider.Value
. But I cannot get the BindableProperties
to update the properties assigned to it by the parent page. While using a regular Slider
instead of my custom view with the exact same setup works correctly.
I have my FilterPage
page defined like so:
public partial class FilterPage : ContentPage
{
public RestaurantFilter Filter { get; }
public FilterPage(RestaurantFilter filter)
{
Filter = filter;
InitializeComponent();
}
}
public class RestaurantFilter : INotifyPropertyChanged
{
private int _minQuality = Restaurant.MIN_QUALITY;
public int MinQuality
{
get => _minQuality;
set => SetField(ref _minQuality, value);
}
private int _minPrice = Restaurant.MIN_PRICE;
public int MinPrice
{
get => _minPrice;
set => SetField(ref _minPrice, value);
}
private int _maxPrice = Restaurant.MAX_PRICE;
public int MaxPrice
{
get => _maxPrice;
set => SetField(ref _maxPrice, value);
}
// ...
}
In the XAML for this page, I have 2 views: a Slider
and a custom DoubleIntSlider
view, defined like so:
<Label
Text="Min Quality"
HorizontalOptions="FillAndExpand" />
<views:IntegerSlider Value="{Binding .Filter.MinQuality}" Maximum="10" Minimum="1" />
<Label
Text="Price"
HorizontalOptions="FillAndExpand" />
<views:DoubleIntSlider
MinValue="{Binding .Filter.MinPrice}"
MaxValue="{Binding .Filter.MaxPrice}"
Maximum="10" Minimum="1" />
With the binding context being the instance of the page. When touch the Sliders in the page, only the RestaurantFilter.MinQuality
property has its setter called.
The DoubleIntSlider
MinValue
and MaxValue
properties are defined like this:
public partial class DoubleIntSlider : StackLayout
{
public static readonly BindableProperty MinValueProperty = BindableProperty.Create(
nameof(MinValue),
typeof(int),
typeof(DoubleIntSlider),
0,
BindingMode.TwoWay,
coerceValue: CoerceMinValue,
propertyChanged: MinChanged);
public int MinValue
{
get => (int)GetValue(MinValueProperty);
set => SetValue(MinValueProperty, value);
}
public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(
nameof(MaxValue),
typeof(int),
typeof(DoubleIntSlider),
10,
BindingMode.TwoWay,
coerceValue: CoerceMaxValue,
propertyChanged: MaxChanged);
public int MaxValue
{
get => (int)GetValue(MaxValueProperty);
set => SetValue(MaxValueProperty, value);
}
private static object CoerceMinValue(BindableObject bindable, object value)
{
var instance = (DoubleIntSlider)bindable;
int minValue = (int)value;
return minValue.Clamp(instance.Minimum, instance.MaxValue);
}
private static object CoerceMaxValue(BindableObject bindable, object value)
{
var instance = (DoubleIntSlider)bindable;
int maxValue = (int)value;
return maxValue.Clamp(instance.MinValue, instance.Maximum);
}
private static void MinChanged(BindableObject bindable, object oldValue, object newValue)
{
var ctrl = (DoubleIntSlider)bindable;
ctrl.MinValue = (int)newValue;
}
private static void MaxChanged(BindableObject bindable, object oldValue, object newValue)
{
var ctrl = (DoubleIntSlider)bindable;
ctrl.MaxValue = (int)newValue;
}
// ...
}
And the DoubleIntSlider
view has, you guessed it, 2 sliders like so:
<Slider
Value="{Binding .MinValue}"
Maximum="{Binding .Maximum}"
Minimum="{Binding .Minimum}"
HorizontalOptions="FillAndExpand"
/>
<Slider
Value="{Binding .MaxValue}"
Maximum="{Binding .Maximum}"
Minimum="{Binding .Minimum}"
HorizontalOptions="FillAndExpand"
/>
I tried all the possible BindingMode
s for the BindableProperty
objects. I also tried with and without a propertyChanged
delegate. I also tried putting the coerce code directly in the DoubleIntSlider
property setter.
But I could not get the setters for RestaurantFilter.MinPrice
and RestaurantFilter.MaxPrice
to be called when the sliders are updated.
Edit:
Here DoubleIntSlider
's constructor:
public DoubleIntSlider()
{
PropertyChanged += (_, __) =>
{
UpdateBar(); // This method only reads the values
};
InitializeComponent();
}
And the complete XAML for DoubleIntSlider
:
<?xml version="1.0" encoding="utf-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:App1.Views;assembly=App1"
x:Class="App1.Views.DoubleIntSlider"
x:Name="this"
BindingContext="{x:Reference this}"
Padding="10">
<Slider
Value="{Binding .MinValue}"
Maximum="{Binding .Maximum}"
Minimum="{Binding .Minimum}"
HorizontalOptions="FillAndExpand"
/>
<StackLayout Orientation="Horizontal"
HeightRequest="50"
Spacing="0"
Padding="10"
x:Name="BarLayout">
<Rectangle x:Name="BarLeftMargin"
Stroke="Gray"
StrokeThickness="4"
StrokeDashArray="1,1"
StrokeDashOffset="6"
HeightRequest="5"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Rectangle Fill="Blue"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
<Rectangle x:Name="BarRightMargin"
Stroke="Gray"
StrokeThickness="4"
StrokeDashArray="1,1"
StrokeDashOffset="6"
HorizontalOptions="Center"
HeightRequest="5"
VerticalOptions="Center" />
</StackLayout>
<Slider
Value="{Binding .MaxValue}"
Maximum="{Binding .Maximum}"
Minimum="{Binding .Minimum}"
HorizontalOptions="FillAndExpand"
/>
</StackLayout>