I was answering this question, and while doing so I found a lot of weird behavior. Since I'm pro-MVVM, I put together a solution to see if I would see the same behavior. What my solution uncovers is that even though I'm binding TwoWay
to Slider.Value
, it is not being updated in my ViewModel after Slider.Maximum
and Slider.Minimum
change; i.e. my view model's Value
can be outside UpperLimit
and LowerLimit
, meanwhile Slider.Value
(which my VM's Value
property is bound to) is inside the range.
In the aforementioned question, changing Slider.Maximum
or Slider.Minimum
seems to always keep the Slider.Value
in range, and sometimes "restores" Slider.Value
to a previous value it used to be set to.
Microsoft's Slider Source Code
- Why does
Slider.Value
change/restore its value as seen in linked question even though the current value is within the Min/Max range? - Why doesn't my view model's
Value
property, bound toSlider.Value
match with aTwoWay
binding after changing theUpperLimit
andLowerLimit
?- Note that the bindings to Maximum & Minimum do work
MainWindow.xaml:
<DockPanel>
<Slider Name="MySlider" DockPanel.Dock="Top" AutoToolTipPlacement="BottomRight" Value="{Binding Value, Mode=TwoWay}" Maximum="{Binding UpperLimit}" Minimum="{Binding LowerLimit}"/>
<Button Name="MyButton1" Click="MyButton1_Click" DockPanel.Dock="Top" Content="shrink borders"/>
<Button Name="MyButton2" Click="MyButton2_Click" DockPanel.Dock="Top" VerticalAlignment="Top" Content="grow borders"/>
<Button Name="MyButton3" Click="MyButton3_Click" DockPanel.Dock="Top" VerticalAlignment="Top" Content="Print ItemVM Value"/>
</DockPanel>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private readonly ItemViewModel item;
public MainWindow()
{
InitializeComponent();
DataContext = item = new ItemViewModel(new Item(1, 20, 0.5));
}
private void MyButton1_Click(object sender, RoutedEventArgs e)
{
//MySlider.Minimum = 1.6;
//MySlider.Maximum = 8;
item.LowerLimit = 1.6;
item.UpperLimit = 8;
}
private void MyButton2_Click(object sender, RoutedEventArgs e)
{
//MySlider.Minimum = 0.5;
//MySlider.Maximum = 20;
item.LowerLimit = 0.5;
item.UpperLimit = 20;
}
private void MyButton3_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Item Value: " + item.Value);
System.Diagnostics.Debug.WriteLine("Slider Value: " + MySlider.Value);
}
}
Item/ItemViewModel:
public class ItemViewModel : INotifyPropertyChanged
{
private readonly Item _item;
public event PropertyChangedEventHandler PropertyChanged;
public ItemViewModel(Item item)
{
_item = item;
}
public double UpperLimit
{
get
{
return _item.UpperLimit;
}
set
{
_item.UpperLimit = value;
NotifyPropertyChanged();
}
}
public double LowerLimit
{
get
{
return _item.LowerLimit;
}
set
{
_item.LowerLimit = value;
NotifyPropertyChanged();
}
}
public double Value
{
get
{
return _item.Value;
}
set
{
_item.Value = value;
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Item
{
private double _value;
private double _upperLimit;
private double _lowerLimit;
public double Value
{
get
{
return _value;
}
set
{
_value = value;
}
}
public double UpperLimit
{
get
{
return _upperLimit;
}
set
{
_upperLimit = value;
}
}
public double LowerLimit
{
get
{
return _lowerLimit;
}
set
{
_lowerLimit = value;
}
}
public Item(double value, double upperLimit, double lowerLimit)
{
_value = value;
_upperLimit = upperLimit;
_lowerLimit = lowerLimit;
}
}
Steps to reproduce:
Click
MyButton3
Item Value = 1
Slider Value = 1
Move Slider/Thumb all the way to right
Click
MyButton3
Item Value = 20
Slider Value = 20
Click
MyButton1
Click
MyButton3
Item Value = 20
Slider Value = 8
If you put a break point in MyButton3_Click
and execute the last step, you can see that MySlider.Value = 8