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

C# 更改最小值和最大值后,滑块值不会更新,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,我在回答问题的时候,发现了很多奇怪的行为。因为我是支持MVVM的,所以我制定了一个解决方案,看看是否会看到相同的行为。我的解决方案揭示的是,即使我将双向绑定到Slider.Value,它也不会在Slider.Maximum和Slider.Minimum更改之后在我的ViewModel中更新;i、 e.我的视图模型的值可以超出上限和下限,同时滑块。值(我的虚拟机的值属性绑定到该值)在范围内 在上述问题中,更改滑块.max或滑块.min似乎总是将滑块.Value保持在范围内,有时会将滑块.Value

我在回答问题的时候,发现了很多奇怪的行为。因为我是支持MVVM的,所以我制定了一个解决方案,看看是否会看到相同的行为。我的解决方案揭示的是,即使我将
双向
绑定到
Slider.Value
,它也不会在
Slider.Maximum
Slider.Minimum
更改之后在我的ViewModel中更新;i、 e.我的视图模型的
可以超出
上限
下限
,同时
滑块。值
(我的虚拟机的
属性绑定到该值)在范围内

在上述问题中,更改
滑块.max
滑块.min
似乎总是将
滑块.Value
保持在范围内,有时会将
滑块.Value
恢复为以前设置的值

  • 为什么即使当前值在最小/最大范围内,
    Slider.Value
    也会更改/恢复链接问题中显示的值
  • 为什么我的视图模型的
    属性没有绑定到
    滑块。在更改
    上限
    下限
    后,值
    双向
    绑定匹配?
    • 请注意,到最大值和最小值的绑定不起作用
  • 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>
    
    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);
        }
    }
    
    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;
        }
    }
    
    项目/项目视图模型:

    <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>
    
    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);
        }
    }
    
    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;
        }
    }
    
    复制步骤:

    <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>
    
    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);
        }
    }
    
    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;
        }
    }
    
  • 单击
    MyButton3

    • 项目值=1

    • 滑块值=1

  • 将滑块/拇指一直向右移动

  • 单击
    MyButton3

    • 项目值=20

    • 滑块值=20

  • 单击
    MyButton1

  • 单击
    MyButton3

    • 项目值=20

    • 滑块值=8


  • 如果您在
    MyButton3\u单击
    并执行最后一步,您可以看到
    MySlider.Value=8
    这是由于值强制造成的,您可以

    一般来说,WPF控件设计用于松散的数据绑定。它们的get/set访问器和事件等被添加以帮助从Winforms转换,但它们添加了一个附加的逻辑层,并不总是过滤到绑定属性中。这是混合使用“好”WPF代码(数据绑定)和“坏”(直接访问控件)时可能出现的许多问题示例之一

    编辑:

    每当需要确定当前值时,就会调用依赖项属性的强制回调处理程序。将其视为修改结果的最后机会;它不改变绑定本身,只改变所返回内容的值。如果视图模型(例如)中有一个integer属性,该属性的值为10,并将文本框绑定到该属性,如下所示:

        <TextBlock Text="{Binding MyValue}" />
    
    
    
    该值显然将显示为10。现在,假设您创建了一个用户控件,该控件具有一个名为“MyProperty”的整数依赖属性,并假设强制回调将当前值乘以2:

        <local:MyControl x:Name="myControl" MyProperty="{Binding MyValue}" />
    
    
    
    这根本没用。我们正在将MyProperty绑定到MyValue属性,但它只是一个DP。我们从来没有真正调用过它。现在,假设我们添加了第二个文本框,但这次绑定到MyControl.MyProperty:

        <TextBlock Text="{Binding Path=MyProperty, ElementName=myControl}" />
    
    
    
    第一个控件将继续显示10(这仍然是视图模型中的值),但第二个控件将显示20,因为mydp属性的强制调用修改了它从自身绑定到MyValue的值。(有趣的是,双向绑定也可以工作,强制回调会导致值在每次更改时加倍)

    所有这一切的重要线索是,只有当值需要由另一个依赖项自身更新或由代码手动调用getter来解析时,才会调用强制回调。显然,调用滑块上的值getter会导致这种情况发生,但简单地更改最小值和最大值并不会导致这种情况。这就像在视图模型中更改属性的值而不调用属性更改通知一样……您知道自己做了什么,但其他什么都不知道


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

    我能够将大部分内容分解为这一部分,但我无法确定故障在何处。您是否清楚代码隐藏和绑定中改变值的行为有何不同;对我来说不是。我还质疑这样一个事实,即胁迫是起泡沫还是起隧道作用。当我试图弄清楚选择Start/End是否在Value和Max/Min之前被强制,当我无法单步执行代码时,我的思维过程变成了意大利面条。我得仔细研究一下强迫行为。如果你对如何理解逻辑或发生了什么有更多的见解,我洗耳恭听。谢谢相应地进行编辑。:)@MarkFeldman我已经研究了WPF源代码,设置Minimum显然会调用强制回调以获得max和Value。我是不是误解了你的帖子?@MarkFeldman P.S.欢迎来到2015年。可能是一个比你的答案更可靠的链接。我现在将回顾您的答案和Tim的问题。@Tim:这无疑是我在这方面知识的极限,但据我所知,对已设置的属性调用强制值不会调用属性更改通知。这很容易复制…绑定两个DPs,让一个强制另一个;第二个的绑定没有得到更新。