滑块。值在不应调整最小值时更改



简而言之:
在某些情况下,设置Slider.Minimum将调整Slider.Value,尽管当前值大于新的Minimum。

代码:(应可复制)
MainWindow.xaml:

<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<Slider Name="MySlider" DockPanel.Dock="Top" AutoToolTipPlacement="BottomRight" />
<Button Name="MyButton1" DockPanel.Dock="Top" Content="shrink borders"/>
<Button Name="MyButton2" DockPanel.Dock="Top" VerticalAlignment="Top" Content="grow borders"/>
</DockPanel>
</Window>

主窗口.xaml.cs:

using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MySlider.ValueChanged += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine("Value changed: " + e.NewValue);
};
System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Slider.MinimumProperty, typeof(Slider)).AddValueChanged(MySlider, delegate
{
System.Diagnostics.Debug.WriteLine("Minimum changed: " + MySlider.Minimum);
});
System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Slider.MaximumProperty, typeof(Slider)).AddValueChanged(MySlider, delegate
{
System.Diagnostics.Debug.WriteLine("Maximum changed: " + MySlider.Maximum);
});
MySlider.Value = 1;
MySlider.Minimum = 0.5;
MySlider.Maximum = 20;
MyButton1.Click += (sender, e) =>
{
MySlider.Minimum = 1.6;
MySlider.Maximum = 8;
};
MyButton2.Click += (sender, e) =>
{
MySlider.Minimum = 0.5;
MySlider.Maximum = 20;
};
}
}
}

复制步骤:
1。在调试模式下运行
2.将Slider移动到最右侧
3.按下"缩小边框"按钮
4.按下"增长边界"按钮。

预期输出:

Value changed: 1
Minimum changed: 0,5
Maximum changed: 20
//Multiple times "Value changed"
Value changed: 20
Minimum changed: 1,6
Value changed: 8
Maximum changed: 8
Minimum changed: 0,5
Maximum changed: 20

滑块保持在8。

实际输出:

Value changed: 1
Minimum changed: 0,5
Maximum changed: 20
//Multiple times "Value changed"
Value changed: 20
Minimum changed: 1,6
Value changed: 8
Maximum changed: 8
Value changed: 1
Minimum changed: 0,5
Maximum changed: 20

(请注意附加的"值已更改:1")
滑块跳到1。

旁注:
绑定Slider。将OneWayToSource(或TwoWay)值设置为双重属性,修复了此问题。

问题:
为什么会发生这种情况?

理论:
我很确定这与价值强制有关。似乎发生了以下情况:
1。将Value设置为1可编程设置Value的"基本值"。它介于"最小值"one_answers"最大值"的默认值之间,因此"有效值"完全相同
2.设置"最小值"one_answers"最大值"不会更改任何内容,因为"值"仍在它们之间
3.手动向右拖动滑块显然会更改"有效值",但由于某些奇怪的原因,不会更改"基本值">
4.再次增加边界,调用强制回调,它意识到(错误的)"基本值"在新的最小值和最大值之间,并将"有效值"改回该值。

假设这确实是正在发生的事情,我们将面临以下问题:
为什么手动拉动Slider(3.)不影响"基本值"?

我不确定用例是什么,但如果您使用MVVM并且所有内容都已绑定,则可能可以避免此问题。如果这就是你正在做的,而这只是你复制问题的另一种方式,那就好了。

看看Slider的源代码。我无法准确指出问题所在,但我更清楚地了解到有一个内部价值在被使用。

添加2个按钮,并且仅在事件处理程序中更改所有按钮的最小值或最大值:

MyButton1.Click += (sender, e) =>
{
MySlider.Minimum = 1.6;
};
MyButton2.Click += (sender, e) =>
{
MySlider.Minimum = 0.5;
};
MyButton3.Click += (sender, e) =>
{
MySlider.Maximum = 20;
};
MyButton4.Click += (sender, e) =>
{
MySlider.Maximum = 8;
};

启动时,单击MyButton1MyButton2:

Value changed: 1.6
Minimum changed: 1.6
Value changed: 1
Minimum changed: 0.5
Value changed: 1.6
Minimum changed: 1.6
Value changed: 1
Minimum changed: 0.5

您可以看到,在内部,它存储原始起始值,并在范围能够显示时将其恢复,并将其设置回原来的值。如果在启动时只更改最大值(不移动滑块),则Value属性不会更改,因为Value在当前范围内:

Maximum changed: 8
Maximum changed: 20
Maximum changed: 8
Maximum changed: 20

但是,当您将滑块最大值更改为20,然后更改Maximum = 8Minimum = 1.6时,Minimum现在超出了(内部)Value(1)的范围,并使用Maximum值作为其Value。再次增长时,设置Minimum = 0.5Maximum = 20,由于内部值=1且介于0.5和20之间,因此会将Value设置回1。

我找到了一个解决方法,那就是每次更改范围时都重置内部值。要重置,只需再次设置Slider.Value即可。如果在更改MaximumMinimum之后执行此操作,则在旧值不在新范围内的情况下,它将保持该值。因此,采取您的初步实施:

MyButton1.Click += (sender, e) =>
{
MySlider.Minimum = 1.6;
MySlider.Maximum = 8;
MySlider.Value = MySlider.Value;
};
MyButton2.Click += (sender, e) =>
{
MySlider.Minimum = 0.5;
MySlider.Maximum = 20;
MySlider.Value = MySlider.Value;
};

输出(符合您的预期):

Value changed: 1
Minimum changed: 0.5
Maximum changed: 20
//Multiple times "Value changed"
Value changed: 20
Minimum changed: 1.6
Value changed: 8
Maximum changed: 8
Minimum changed: 0.5
Maximum changed: 20

编辑

你发布的关于价值强制的链接非常有用。我不确定你的问题是否会被视为第二个问题,但我相信我找到了答案。

当您使用Slider(从技术上讲,ThumbTrack)时,请注意,通过调用Slider.UpdateValue,四处滑动的ThumbSlider.Value绑定。现在的问题是SetCurrentValueInternal不是开源的:(我们可以得出的结论是,神秘函数并没有强制ValueProperty,相反,它只是设置基础("所需")值。尝试:

private void MyButton1_Click(object sender, RoutedEventArgs e)
{
MySlider.Minimum = 1.6;
MySlider.Maximum = 8;
MySlider.CoerceValue(Slider.ValueProperty); //Set breakpoint, watch MySlider.Value before and after breakpoint.
}

通过以上操作,您将看到,即使Value将从20下降到8,当它下降到8时,您实际强制ValueProperty的第二秒,该值也将更改为1.6。事实上,当你扩大范围时就会发生这种情况。设置"最大值"或"最小值"后,您将看到"值"更改,因为它再次强制值属性。尝试翻转最大值和最小值:

private void MyButton2_Click(object sender, RoutedEventArgs e)
{
MySlider.Maximum = 20; //Will change Value to 1.6 after executing
MySlider.Minimum = 0.5; //Will change Value to 1 after executing
}

尽管如此,这还是会让你思考,如果可能的话,它怎么能记得回到1,是否有一个有效的、基本的和初始的值?我不确定。

事实上,试试这个。

  1. 启动应用程序
  2. 将拇指一直向右移动(值=20)
  3. 收缩边框(值=8)
  4. 取拇指向左移动,然后一直向右移动(值=8)
  5. 然后扩展边界

步骤5之后,您将观察到Value仍然等于8。这就是我发现它与神秘功能有关的原因。

注意:我的大脑又死了,所以如果这不清楚,我道歉,我必须回来编辑它。

编辑2

例如,SetCurrentValueInteralSetValue都调用SetValueCommon。差异是SetCurrentValueInternal用基值重新评估当前值,因为强制标志设置为true。(将值保持在最小/最大范围内),参考

通过我的挖掘,我发现SetCurrentValueSetValue有两个完全不同的结果,那就是:指定强制(在绑定属性为值类型的情况下,IsInternal似乎没有使用)。

我的证据是文件,说明:

"SetCurrentValue方法是设置属性的另一种方法,但它不按优先级排列。相反,SetCurrentValue使您能够更改属性的值,而不会覆盖以前值的源。您可以在任何时候使用SetCurrentValue来设置值,而不必给该值本地值的优先级…">

话虽如此,移动SliderThumb不会影响基值,因为它正在调用SetCurrentValueInternal

此外,手动更改Value会更改基本值,因为它调用SetValueValueProperty依赖属性定义了如何使用CoerceValueCallback强制,如最后一段所述。关于Slider.Value中发生的事情的更多详细信息,

强制与基值交互的方式是,强制的约束在当时存在时应用,但基值仍然保留。因此,如果胁迫中的约束后来被解除,胁迫将返回尽可能接近该基本值的值,并且一旦所有约束解除,胁迫对财产的影响就可能停止。

当然,我进一步阅读了依赖属性回调和验证-高级强制和回调场景,发现了这一点:

例如,在Min/Max/Current场景中,您可以选择由用户设置Minimum和Maximum。如果是这样,您可能需要强制"最大值"始终大于"最小值",反之亦然。但是,如果该强制处于活动状态,并且"最大值"强制为"最小值",则会使"当前值"处于不可设置状态,因为它依赖于这两个值,并且被限制在值之间的范围内,即零。然后,如果调整了"最大值"或"最小值",则"电流"似乎会"跟随"其中一个值,因为"电流"的所需值仍在存储中,并且随着约束的放松,该值正试图达到所需值。

总之,我认为这是在Slider中设计的,因为它的编码方式是Value必须始终位于MinimumMaximum之间。Value将遵循Maximum属性,当取消约束(当前值在基值的范围内)时,值属性将返回到其原始值。

最后,IMO WPF以这种方式设计滑块,因为WPF与数据绑定密切相关。我认为他们是在这样的假设下设计的,即开发人员将充分利用数据绑定,并实现防止使用无效值的逻辑。

最新更新