更改最小值和最大值后,滑块值不会更新

时间:2015-08-17 22:52:22

标签: c# wpf xaml mvvm

我正在回答this问题,在这样做的过程中,我发现了很多奇怪的行为。由于我是专业MVVM,我整理了一个解决方案,看看我是否会看到相同的行为。我的解决方案揭示的是,即使我将TwoWay绑定到Slider.Value,在Slider.MaximumSlider.Minimum更改后,我的ViewModel中也没有更新它;即我的视图模型Value可以在UpperLimitLowerLimit之外,同时Slider.Value(我的VM Value属性被绑定to)在范围内。

在上述问题中,更改Slider.MaximumSlider.Minimum似乎始终将Slider.Value保持在范围内,有时候"恢复" Slider.Value为之前设置的值。

Microsoft's Slider Source Code

  1. 为什么 Slider.Value更改/恢复其值,如链接问题中所示,即使当前值在最小/最大范围内?
  2. 为什么不包含我的视图模型的Value属性,在更改{后绑定到Slider.ValueTwoWay绑定绑定{1}}和UpperLimit
    • 请注意,绑定到Maximum&最低执行工作
  3. MainWindow.xaml:

    LowerLimit

    MainWindow.xaml.cs:

    <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>
    

    物品/ ItemViewModel:

    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);
        }
    }
    
      

    重现的步骤:

         
        
    1. 点击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; } }

           
          
      • 项目值= 1

      •   
      • 滑块值= 1

      •   
    2.   
    3. 将滑块/拇指一直移到右侧

    4.   
    5. 点击MyButton3

           
          
      • 项目值= 20

      •   
      • 滑块值= 20

      •   
    6.   
    7. 点击MyButton3

    8.   
    9. 点击MyButton1

           
          
      • 项目值= 20

      •   
      • 滑块值= 8

      •   
    10.   

    如果您在MyButton3中设置断点并执行最后一步,则可以看到MyButton3_Click

1 个答案:

答案 0 :(得分:1)

这是因为价值强制,你可以read more about it here

一般来说,WPF控件设计用于松散数据绑定。添加了它们的get / set访问器和事件等,以帮助从Winforms过渡,但是它们添加了一个额外的逻辑层,并不总是过滤到绑定的属性。这是当您混合“好”时可能出现的众多问题之一。 WPF代码(数据绑定)与&#34;坏&#34; (直接访问控件)。

编辑:

只要需要确定当前值,就会调用依赖项属性的强制回调处理程序。将其视为修改结果的最后机会;它不会改变绑定本身,只会改变返回的值。如果视图模型中有一个包含值10的整数属性,并将文本框绑定到它,就像这样:

    <TextBlock Text="{Binding MyValue}" />

该值显然会显示为10.现在让我们假设您创建一个带有整数依赖属性的用户控件,名为&#34; MyProperty&#34;,并且让我们说强制回调相乘无论目前的价值是2:

    <local:MyControl x:Name="myControl" MyProperty="{Binding MyValue}" />

这无济于事。我们将MyProperty绑定到MyValue属性,但它只是一个DP。我们从未真正调用过它。现在让我们说我们添加第二个TextBox,但这次绑定到MyControl.MyProperty:

    <TextBlock Text="{Binding Path=MyProperty, ElementName=myControl}" />

第一个控件将继续显示10(这仍然是我们视图模型中的值)但第二个控件将显示20,因为MyProperty DP的强制调用修改了它从它获得的值绑定到MyValue。 (有趣的是,双向绑定也有效,强制回调导致值在值改变时加倍)。

所有这一切的重要线索是,仅当需要通过另一个依赖项更新本身或通过手动调用getter的代码来解析值时,才会调用强制回调。显然你在Slider上调用Value getter会导致这种情况发生,但只是更改Minimum和Maximum的值不会。这就像在不调用属性更改通知的情况下更改视图模型中属性的值一样......您知道自己已经完成了什么,但没有其他任何操作。

进一步阅读:the RangeBase source code(特别是ConstrainToRange强制回调)和the Slider source code(即仅在拖动滑块或缩略图时调用的UpdateValue)。