滑块。当不应该调整最小值时,价值会发生变化

时间:2022-11-21 16:05:29

In short:
Under certain circumstances setting Slider.Minimum will adjust Slider.Value although the current Value is bigger then the new Minimum.

简而言之:在某些情况下设置滑块。最低将滑块调整。值,虽然当前值比新的最小值大。

Code: (should be reproducible)
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>

MainWindow.xaml.cs:

MainWindow.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;
            };
        }
    }
}

Steps to reproduce:
1. Run in Debug mode.
2. Move Slider to the far right.
3. Press "shrink borders" Button.
4. Press "grow borders" Button.

重现步骤:1。在调试模式下运行。2。将滑块移动到最右边。3所示。按“收缩边框”按钮。按“边界”按钮。

Expected Output:

预期的输出:

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 stays at 8.

滑块呆在8。

Actual Output:

实际输出:

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

(Note the additional "Value changed: 1")
Slider jumps to 1.

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

Sidenote:
Binding Slider.Value OneWayToSource (or TwoWay) to a double property, fixes the issue.

旁注:绑定滑块。将OneWayToSource(或TwoWay)值为双属性,修复问题。

Question:
Why is this happening?

问:为什么会这样?

Theory:
I'm pretty sure it has something to do with Value Coercion. It seems like the following happens:
1. Setting Value to 1 programmatical sets Value's "base value". It is between the default values for Minimum and Maximum, so the "effective value" is exactly the same.
2. Setting Minimum and Maximum does not change anything, because Value is still in between them.
3. Manually pulling the Slider to the right apparently changes the "effective value" but for some weird reason not the "base value".
4. Increasing the borders again, calls the coercion callback, which realizes, that the (wrong) "base value" is between the new Minimum and Maximum and changes the "effective value" back to it.

理论:我很确定这与价值强制有关。似乎发生了以下情况:1。将值设置为1程序化设置值的“基值”。它介于最小值和最大值之间,因此“有效值”是完全相同的。2。设置最小值和最大值不会改变任何东西,因为值仍然在它们之间。3所示。手动向右拉滑块显然会改变“有效值”,但出于某种奇怪的原因,不会改变“基本值”。4所示。再次增加边界,调用强制回调,它意识到(错误的)“基值”介于新的最小值和最大值之间,并将“有效值”更改为新的最小值和最大值。

Assuming this is indeed what is happening, leads us to the following question:
Why is manually pulling the Slider (3.) not affecting the "base value"?

假设确实发生了这种情况,那么我们就会问:为什么手动拖动滑块(3.)而不影响“基本值”呢?

1 个解决方案

#1


6  

I'm not sure what the use case is, but this issue could probably be avoided if you were using MVVM and everything was bound. If that's what you're doing and this is just another way you're reproducing the issue, good find.

我不确定用例是什么,但是如果您使用MVVM,那么这个问题很可能会被避免。如果这是你正在做的,这只是你再现问题的另一种方式,很好的发现。

Take a look at the Slider source code. I couldn't pinpoint the issue, but I have a better understanding that there is an internal value being used.

看一下滑块源代码。我无法准确地指出问题的症结所在,但我更清楚地认识到,我们正在使用一种内在价值。

Add 2 buttons, and only change Minimum or Maximum in the event handlers for all buttons:

增加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;
};

At startup, click MyButton1 and MyButton2:

在启动时,单击MyButton1和MyButton2:

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

You can see, internally, it stores the original start value and restores it when the range is capable of showing it, it sets it back to what it was. If you change only the maximum's at startup (without moving slider bar), the Value property isn't changed because the Value is in the current range:

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

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

However, when you change the slider bar maximum to 20, then change the Maximum = 8 and Minimum = 1.6, the Minimum is now out of range from (internal) Value (1) and uses the Maximum value for it's Value. When you grow it again, you set Minimum = 0.5 and Maximum = 20, and since the internal value = 1 and is between 0.5 and 20, it sets the Value back to 1.

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

I found one workaround, and that is resetting the internal value every time you change the range. To reset, you just set the Slider.Value again. If you do this after you change the Maximum and Minimum it will persist the value in the event the old value isn't in the range of the new range. So taking your initial implementation:

我找到了一个变通方法,那就是每次改变范围时重新设置内部值。要重置,只需设置滑块。值了。如果您在更改最大值和最小值之后执行此操作,那么在旧值不在新范围内的情况下,该值将持续存在。所以我们开始实施

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

Output (matches your expected):

你期望输出(匹配):

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

Edit

The link you posted about value coercion is very helpful. I'm not sure if your question would be considered a second question, but I believe I found the answer.

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

When you use the Slider (technically the Thumb with the Track), realize that the Thumb sliding around is bound to the Slider.Valuethrough Slider.UpdateValue gets called. This issue now is that SetCurrentValueInternal is not open source :( What we can conclude is that the mystery function isn't coercing ValueProperty, instead it's only setting the base ("desired") value. Try:

当您使用滑块(技术上是指与轨道的拇指)时,要意识到拇指滑动是与滑块绑定的。Valuethrough滑动条。UpdateValue被调用。现在的问题是,SetCurrentValueInternal并不是开源的:(我们可以得出的结论是,神秘函数并没有强制ValueProperty,而是只设置了base(“所需的”)值。试一试:

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

Doing the above you will see that even though Value will go from 20 down to 8, the second you actually Coerce ValueProperty when it drops down to 8, the value will then change to 1.6. In fact, that's what happens when you grow the range. After you set Max or Min you will see the Value change since it is coercing value property again. Try flipping Max and Min:

在上面你会看到,即使值从20下降到8,当它下降到8时,你实际上会强制ValueProperty,值会变为1。6。事实上,这就是当你扩大范围时所发生的事情。设置Max或Min之后,您将看到值的变化,因为它再次强制值属性。试试翻动Max和Min:

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
}

Still, this makes you think, how can it remember to go back to 1 when possible, is there an effective, base, and initial values? I'm not sure.

尽管如此,这让你思考,它如何能记得在可能的情况下回到1,是否存在有效的、基数和初始值?我不确定。

Matter of fact try this.

事实上,试试这个。

  1. Start application
  2. 启动应用程序
  3. Move thumb all the way to right (Value = 20)
  4. 向右移动拇指(值= 20)
  5. Shrink borders (Value = 8)
  6. 收缩边框(值= 8)
  7. Take thumb and move it to the left, then all the way to the right (Value = 8)
  8. 把大拇指移到左边,然后一直往右移动(Value = 8)
  9. Then grow borders
  10. 然后增长边界

After step 5 you will observe that Value still equals 8. This is how I found out that its something to do with that mystery function.

在步骤5之后,您将看到该值仍然等于8。这就是我发现它与那个神秘的函数有关的原因。

Note: My brain is dead again, so if this isn't clear, I apologize, I'll have to come back and edit it.

注意:我的大脑又死机了,如果不清楚的话,我很抱歉,我得回去修改一下。

Edit 2

For one, both SetCurrentValueInteral and SetValue both call SetValueCommon. The difference is SetCurrentValueInternal re-evaluates the current value with the base value since the coerce flag is set to true. (keeps the value in-bounds of Min/Max), ref.

首先,SetCurrentValueInteral和SetValue都调用SetValueCommon。差异是SetCurrentValueInternal使用基本值重新评估当前值,因为强制标志被设置为true。(保持最小/最大值的值),ref。

Through my digging, I found that SetCurrentValue and SetValue have two completely different results, and that is: specifying the coercion (IsInternal seems to be unused in the case where the bound property is of value type).

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

My proof is documentation that states:

我的证明文件表明:

"The SetCurrentValue method is another way to set a property, but it is not in the order of precedence. Instead, SetCurrentValue enables you to change the value of a property without overwriting the source of a previous value. You can use SetCurrentValue any time that you want to set a value without giving that value the precedence of a local value..."

“SetCurrentValue方法是另一种设置属性的方法,但它不是按照优先级排序的。相反,SetCurrentValue允许您更改属性的值,而不覆盖前一个值的源。您可以在任何需要设置值的时候使用SetCurrentValue,而不需要赋予该值本地值的优先级……

With that being said, moving the Thumb of the Slider doesn't effect the base value because it is calling SetCurrentValueInternal.

话虽如此,移动滑块的拇指并不会影响基值,因为它正在调用SetCurrentValueInternal。

Moreover, changing the Value manually changes the base value because it calls SetValue. The ValueProperty dependency property defines how to coerce using the CoerceValueCallback as stated in the last paragraph here. In more detail of what's going on in Slider.Value,

此外,手动更改值会更改基值,因为它调用SetValue。ValueProperty依赖属性定义了如何使用最后一段中所述的强制方法来强制执行。在幻灯片。value中,

Coercion interacts with the base value in such a way that the constraints on coercion are applied as those constraints exist at the time, but the base value is still retained. Therefore, if constraints in coercion are later lifted, the coercion will return the closest value possible to that base value, and potentially the coercion influence on a property will cease as soon as all constraints are lifted.

强制与基值交互的方式是,当这些约束存在时,强制的约束被应用,但是基值仍然保留。因此,如果稍后取消强制中的约束,强制将返回与该基本值可能最接近的值,并且一旦取消所有约束,强制对属性的影响就可能停止。

Of course I read further, Dependency Property Callbacks and Validation - Advanced Coercion and Callback Scenarios and found this:

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

For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. But if that coercion is active, and Maximum coerces to Minimum, it leaves Current in an unsettable state, because it is dependent on both and is constrained to the range between the values, which is zero. Then, if Maximum or Minimum are adjusted, Current will seem to "follow" one of the values, because the desired value of Current is still stored and is attempting to reach the desired value as the constraints are loosened.

例如,在最小/最大/当前场景中,可以选择最小和最大为可设置用户。如果是这样,您可能需要强制最大值总是大于最小值,反之亦然。但是如果强制是活动的,并且最大的强制是最小的,那么它就会使电流处于一种不可调的状态,因为它同时依赖于这两者,并且被限制在值之间的范围,也就是0。然后,如果调整最大值或最小值,那么电流似乎会“跟随”其中一个值,因为当前的期望值仍然被存储,并试图在约束放松时达到期望值。

In conclusion, I think this is as-designed in Slider because it is coded in such a way that Value must always be in between Minimum and Maximum. The Value will follow the Maximum property, and when constraints are lifted (the current value is in range of the base value) the value property will return to its original value.

综上所述,我认为这是按照Slider设计的,因为它的编码方式必须总是在最小值和最大值之间。值将遵循最大值属性,当取消约束(当前值在基值范围内)时,值属性将返回到原始值。

Lastly, IMO WPF designed the slider this way because WPF goes hand in hand with data binding. I assume they designed under the assumption that developers would take full advantage of data-binding and would implement the logic to prevent invalid values from being used.

最后,国际海事组织WPF通过这种方式设计了滑块,因为WPF与数据绑定密切相关。我假设他们的设计是基于这样的假设:开发人员将充分利用数据绑定,并实现逻辑来防止无效的值被使用。

#1


6  

I'm not sure what the use case is, but this issue could probably be avoided if you were using MVVM and everything was bound. If that's what you're doing and this is just another way you're reproducing the issue, good find.

我不确定用例是什么,但是如果您使用MVVM,那么这个问题很可能会被避免。如果这是你正在做的,这只是你再现问题的另一种方式,很好的发现。

Take a look at the Slider source code. I couldn't pinpoint the issue, but I have a better understanding that there is an internal value being used.

看一下滑块源代码。我无法准确地指出问题的症结所在,但我更清楚地认识到,我们正在使用一种内在价值。

Add 2 buttons, and only change Minimum or Maximum in the event handlers for all buttons:

增加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;
};

At startup, click MyButton1 and MyButton2:

在启动时,单击MyButton1和MyButton2:

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

You can see, internally, it stores the original start value and restores it when the range is capable of showing it, it sets it back to what it was. If you change only the maximum's at startup (without moving slider bar), the Value property isn't changed because the Value is in the current range:

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

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

However, when you change the slider bar maximum to 20, then change the Maximum = 8 and Minimum = 1.6, the Minimum is now out of range from (internal) Value (1) and uses the Maximum value for it's Value. When you grow it again, you set Minimum = 0.5 and Maximum = 20, and since the internal value = 1 and is between 0.5 and 20, it sets the Value back to 1.

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

I found one workaround, and that is resetting the internal value every time you change the range. To reset, you just set the Slider.Value again. If you do this after you change the Maximum and Minimum it will persist the value in the event the old value isn't in the range of the new range. So taking your initial implementation:

我找到了一个变通方法,那就是每次改变范围时重新设置内部值。要重置,只需设置滑块。值了。如果您在更改最大值和最小值之后执行此操作,那么在旧值不在新范围内的情况下,该值将持续存在。所以我们开始实施

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

Output (matches your expected):

你期望输出(匹配):

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

Edit

The link you posted about value coercion is very helpful. I'm not sure if your question would be considered a second question, but I believe I found the answer.

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

When you use the Slider (technically the Thumb with the Track), realize that the Thumb sliding around is bound to the Slider.Valuethrough Slider.UpdateValue gets called. This issue now is that SetCurrentValueInternal is not open source :( What we can conclude is that the mystery function isn't coercing ValueProperty, instead it's only setting the base ("desired") value. Try:

当您使用滑块(技术上是指与轨道的拇指)时,要意识到拇指滑动是与滑块绑定的。Valuethrough滑动条。UpdateValue被调用。现在的问题是,SetCurrentValueInternal并不是开源的:(我们可以得出的结论是,神秘函数并没有强制ValueProperty,而是只设置了base(“所需的”)值。试一试:

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

Doing the above you will see that even though Value will go from 20 down to 8, the second you actually Coerce ValueProperty when it drops down to 8, the value will then change to 1.6. In fact, that's what happens when you grow the range. After you set Max or Min you will see the Value change since it is coercing value property again. Try flipping Max and Min:

在上面你会看到,即使值从20下降到8,当它下降到8时,你实际上会强制ValueProperty,值会变为1。6。事实上,这就是当你扩大范围时所发生的事情。设置Max或Min之后,您将看到值的变化,因为它再次强制值属性。试试翻动Max和Min:

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
}

Still, this makes you think, how can it remember to go back to 1 when possible, is there an effective, base, and initial values? I'm not sure.

尽管如此,这让你思考,它如何能记得在可能的情况下回到1,是否存在有效的、基数和初始值?我不确定。

Matter of fact try this.

事实上,试试这个。

  1. Start application
  2. 启动应用程序
  3. Move thumb all the way to right (Value = 20)
  4. 向右移动拇指(值= 20)
  5. Shrink borders (Value = 8)
  6. 收缩边框(值= 8)
  7. Take thumb and move it to the left, then all the way to the right (Value = 8)
  8. 把大拇指移到左边,然后一直往右移动(Value = 8)
  9. Then grow borders
  10. 然后增长边界

After step 5 you will observe that Value still equals 8. This is how I found out that its something to do with that mystery function.

在步骤5之后,您将看到该值仍然等于8。这就是我发现它与那个神秘的函数有关的原因。

Note: My brain is dead again, so if this isn't clear, I apologize, I'll have to come back and edit it.

注意:我的大脑又死机了,如果不清楚的话,我很抱歉,我得回去修改一下。

Edit 2

For one, both SetCurrentValueInteral and SetValue both call SetValueCommon. The difference is SetCurrentValueInternal re-evaluates the current value with the base value since the coerce flag is set to true. (keeps the value in-bounds of Min/Max), ref.

首先,SetCurrentValueInteral和SetValue都调用SetValueCommon。差异是SetCurrentValueInternal使用基本值重新评估当前值,因为强制标志被设置为true。(保持最小/最大值的值),ref。

Through my digging, I found that SetCurrentValue and SetValue have two completely different results, and that is: specifying the coercion (IsInternal seems to be unused in the case where the bound property is of value type).

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

My proof is documentation that states:

我的证明文件表明:

"The SetCurrentValue method is another way to set a property, but it is not in the order of precedence. Instead, SetCurrentValue enables you to change the value of a property without overwriting the source of a previous value. You can use SetCurrentValue any time that you want to set a value without giving that value the precedence of a local value..."

“SetCurrentValue方法是另一种设置属性的方法,但它不是按照优先级排序的。相反,SetCurrentValue允许您更改属性的值,而不覆盖前一个值的源。您可以在任何需要设置值的时候使用SetCurrentValue,而不需要赋予该值本地值的优先级……

With that being said, moving the Thumb of the Slider doesn't effect the base value because it is calling SetCurrentValueInternal.

话虽如此,移动滑块的拇指并不会影响基值,因为它正在调用SetCurrentValueInternal。

Moreover, changing the Value manually changes the base value because it calls SetValue. The ValueProperty dependency property defines how to coerce using the CoerceValueCallback as stated in the last paragraph here. In more detail of what's going on in Slider.Value,

此外,手动更改值会更改基值,因为它调用SetValue。ValueProperty依赖属性定义了如何使用最后一段中所述的强制方法来强制执行。在幻灯片。value中,

Coercion interacts with the base value in such a way that the constraints on coercion are applied as those constraints exist at the time, but the base value is still retained. Therefore, if constraints in coercion are later lifted, the coercion will return the closest value possible to that base value, and potentially the coercion influence on a property will cease as soon as all constraints are lifted.

强制与基值交互的方式是,当这些约束存在时,强制的约束被应用,但是基值仍然保留。因此,如果稍后取消强制中的约束,强制将返回与该基本值可能最接近的值,并且一旦取消所有约束,强制对属性的影响就可能停止。

Of course I read further, Dependency Property Callbacks and Validation - Advanced Coercion and Callback Scenarios and found this:

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

For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. But if that coercion is active, and Maximum coerces to Minimum, it leaves Current in an unsettable state, because it is dependent on both and is constrained to the range between the values, which is zero. Then, if Maximum or Minimum are adjusted, Current will seem to "follow" one of the values, because the desired value of Current is still stored and is attempting to reach the desired value as the constraints are loosened.

例如,在最小/最大/当前场景中,可以选择最小和最大为可设置用户。如果是这样,您可能需要强制最大值总是大于最小值,反之亦然。但是如果强制是活动的,并且最大的强制是最小的,那么它就会使电流处于一种不可调的状态,因为它同时依赖于这两者,并且被限制在值之间的范围,也就是0。然后,如果调整最大值或最小值,那么电流似乎会“跟随”其中一个值,因为当前的期望值仍然被存储,并试图在约束放松时达到期望值。

In conclusion, I think this is as-designed in Slider because it is coded in such a way that Value must always be in between Minimum and Maximum. The Value will follow the Maximum property, and when constraints are lifted (the current value is in range of the base value) the value property will return to its original value.

综上所述,我认为这是按照Slider设计的,因为它的编码方式必须总是在最小值和最大值之间。值将遵循最大值属性,当取消约束(当前值在基值范围内)时,值属性将返回到原始值。

Lastly, IMO WPF designed the slider this way because WPF goes hand in hand with data binding. I assume they designed under the assumption that developers would take full advantage of data-binding and would implement the logic to prevent invalid values from being used.

最后,国际海事组织WPF通过这种方式设计了滑块,因为WPF与数据绑定密切相关。我假设他们的设计是基于这样的假设:开发人员将充分利用数据绑定,并实现逻辑来防止无效的值被使用。