C# 更改最小值和最大值后,滑块值不会更新
我在回答问题的时候,发现了很多奇怪的行为。因为我是支持MVVM的,所以我制定了一个解决方案,看看是否会看到相同的行为。我的解决方案揭示的是,即使我将C# 更改最小值和最大值后,滑块值不会更新,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,我在回答问题的时候,发现了很多奇怪的行为。因为我是支持MVVM的,所以我制定了一个解决方案,看看是否会看到相同的行为。我的解决方案揭示的是,即使我将双向绑定到Slider.Value,它也不会在Slider.Maximum和Slider.Minimum更改之后在我的ViewModel中更新;i、 e.我的视图模型的值可以超出上限和下限,同时滑块。值(我的虚拟机的值属性绑定到该值)在范围内 在上述问题中,更改滑块.max或滑块.min似乎总是将滑块.Value保持在范围内,有时会将滑块.Value
双向
绑定到Slider.Value
,它也不会在Slider.Maximum
和Slider.Minimum
更改之后在我的ViewModel中更新;i、 e.我的视图模型的值
可以超出上限
和下限
,同时滑块。值
(我的虚拟机的值
属性绑定到该值)在范围内
在上述问题中,更改滑块.max
或滑块.min
似乎总是将滑块.Value
保持在范围内,有时会将滑块.Value
恢复为以前设置的值
Slider.Value
也会更改/恢复链接问题中显示的值值
属性没有绑定到滑块。在更改上限
和下限
后,值
与双向
绑定匹配?
- 请注意,到最大值和最小值的绑定不起作用
<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,让一个强制另一个;第二个的绑定没有得到更新。