Slider.Value关于调整变化最小的时候它不应该 [英] Slider.Value changes on adjusting Minimum when it shouldn't

查看:307
本文介绍了Slider.Value关于调整变化最小的时候它不应该的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简而言之:结果
在某些情况下设置Slider.Minimum将调整Slider.Value虽然目前的值越大,则新的最低



代码:(应该是可重复的)结果
MainWindow.xaml:

 <窗口x:类=WpfApplication1.MainWindow
的xmlns =http://schemas.microsoft.com/winfx/2006/xaml/presentation
的xmlns:X =http://schemas.microsoft.com/winfx/2006/xaml
标题=主窗口HEIGHT =350WIDTH =525>
<&DockPanel中GT;
<滑块NAME =MySliderDockPanel.Dock =评出的AutoToolTipPlacement =BottomRight/>
<按钮名称=MyButton1DockPanel.Dock =评出的CONTENT =缩水边界/>
<按钮名称=MyButton2DockPanel.Dock =评出的VerticalAlignment =评出的CONTENT =增长边界/>
< / DockPanel中>
< /窗GT;

MainWindow.xaml.cs:



 使用System.Windows;使用System.Windows.Controls的
;

命名空间WpfApplication1
{
公共部分类主窗口:窗口
{
公共主窗口()
{
的InitializeComponent() ;

MySlider.ValueChanged + =(发件人,E)=>
{
System.Diagnostics.Debug.WriteLine(值改为:+ e.NewValue);
};

System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Slider.MinimumProperty的typeof(滑块))。AddValueChanged(MySlider,代表
{
System.Diagnostics.Debug.WriteLine(最低改为:+ MySlider.Minimum);
});

System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Slider.MaximumProperty的typeof(滑块))。AddValueChanged(MySlider,代表
{
System.Diagnostics.Debug.WriteLine(最大改为:+ MySlider.Maximum);
});

MySlider.Value = 1;

MySlider.Minimum = 0.5;
MySlider.Maximum = 20;

MyButton1.Click + =(发件人,E)=>
{
MySlider.Minimum = 1.6;
MySlider.Maximum = 8;
};

MyButton2.Click + =(发件人,E)=>
{
MySlider.Minimum = 0.5;
MySlider.Maximum = 20;
};
}
}
}



重现步骤:结果
1.在调试模式下搜索
2.将滑块移动到最右边的结果
3.按缩水边框按钮运行。结果
4.按增长边界按钮。



预期输出:



 值改变:1 
最小变化:0,5
最大改变:20
//多次值改为
值改为:20
最小变化:1, 6
值改为:8
最大改变:8
最小变化:0,5
最大改变:20


滑块保持在8



实际输出:

 值改为:1 
最小变化:0,5
最大改变:20
//多次的价值改为
值改为:20
最小变化:1,6
值改为:8
最大改变:8
值改为:1
最低值变:0,5
最大改变:20

(注意额外的值改为:1 )结果
滑块跳转到1



旁注:结果
结合Slider.Value OneWayToSource (或双向),以双重属性,修复该问题。



问:结果
为什么会这样? / p>

理论:结果
我敢肯定它是与的Value强制。这似乎是发生以下情况:结果
1.设置值1的程序化设定值的基准值。它 2.设置最小和最大不会改变任何东西,因为值仍处于两者之间。 >
3.手动拉动滑块向右显然改变了有效价值,但对于一些奇怪的原因,不是基值。结果
4.再次增加边框,来电胁迫回调,实现,即(错误)基本价值是新的最小值和最大值之间,并改变了有效价值回去吧。



假设这是的确发生了什么,使我们的以下问题:?搜索
为什么手动拉动滑块(3)不影响基值


解决方案

我不知道该用例是什么,但很可能如果你使用MVVM和避免这个问题一切都被约束。如果这是你在做什么,这只是你再现问题,善于发现的另一种方式。



看看的的 滑块源代码。我不能确定的问题,但我有一个更好的了解,有正在使用的内部价值。



添加2个按钮,只更改最小或最大的事件处理程序的所有按钮:

  MyButton1.Click + =(发件人,E)=> 
{
MySlider.Minimum = 1.6;
};

MyButton2.Click + =(发件人,E)=>
{
MySlider.Minimum = 0.5;
};

MyButton3.Click + =(发件人,E)=>
{
MySlider.Maximum = 20;
};

MyButton4.Click + =(发件人,E)=>
{
MySlider.Maximum = 8;
};



在启动时,点击 MyButton1 MyButton2

 值改为:1.6 
最小变化:$ 1.6 b $的b值变化:1
最小变化:0.5
值改为:1.6
最小变化:1.6
值改为:1
最小变化:0.5

您可以看到,在内部,它存储原始起始值,当范围是能够显示它的恢复它,它设置它回到它是什么。如果你只改变最大的在启动时(没有移动滑块),则属性没有改变,因为在当前范围:

 最大改变:8 
最大改变:20
最大改变: 8
最大改变:20

然而,当你滑块最大变化为20,然后更改最大值= 8 最小值= 1.6 最小现在出的范围(内部)(1),并使用最高价值为它的。当你再次增长,你设置最小值= 0.5 最大值= 20 ,而且由于内在价值= 1在0.5至20之间,它集回1。



我找到了一个解决办法,那就是重置每次改变时间范围内在价值。要重置,你只要设置再次 Slider.Value 。如果你以后做更改最高最小将坚持事件的旧值ISN'的值吨新范围范围。所以,把你的初始实现:

  MyButton1.Click + =(发件人,E)=> 
{
MySlider.Minimum = 1.6;
MySlider.Maximum = 8;
MySlider.Value = MySlider.Value;
};

MyButton2.Click + =(发件人,E)=>
{
MySlider.Minimum = 0.5;
MySlider.Maximum = 20;
MySlider.Value = MySlider.Value;
};



输出(符合您的预期):



 值改为:1 
最小变化:0.5
最大改变:20
//多次值改为$ b $的b值改为:20
最小变化:1.6
值改为:8
最大改变:8
最小变化:0.5
最大改变:20



修改



在公布有关价值胁迫的链接是很有帮助。我不知道,如果你的问题会被认为是第二个问题,但我相信我找到了答案。



当您使用滑块(技术上的拇指跟踪),实现了拇指左右滑动通过的 Slider.Value e804f0ead88d96c9.html相对=nofollow> Slider.UpdateValue 被调用。这个问题现在是 SetCurrentValueInternal 不是开源:(我们可以断定的是,神秘的功能不强迫 ValueProperty ,相反它只是设置基地(理想)值尝试:

 私人无效MyButton1_Click(对象发件人,RoutedEventArgs E)
{
MySlider.Minimum = 1.6;
MySlider.Maximum = 8;
MySlider.CoerceValue(Slider.ValueProperty); //设置断点,前观看MySlider.Value之后断点。
}

做好以上,你会看到,即使从20下降到8,第二你居然强制转换ValueProperty当它下降到8,该值将然后换 1.6 事实上,这就是当你长大的范围内发生的事情在设置最大值或最小值,你会看到值的变化,因为它再次强迫值属性尝试翻转最大和最小:

 私人无效MyButton2_Click(对象发件人,RoutedEventArgs E)
{
MySlider.Maximum = 20; //执行
MySlider.Minimum = 0.5后,将值更改为1.6; //执行
}
后,将值更改为1



不过,这让你觉得,它怎么能记得回去的时候1可能的,是有一个有效的,基地,初始值?我不知道。



说实在的,试试这个。




  1. 开始应用

  2. 将拇指一路向右(值= 20)

  3. 收缩边界(值= 8)

  4. 以拇指和其移动到左侧,然后一路向右(值= 8)

  5. 然后成长边界



第5步之后,你会发现,价值仍然等于8。这是我的发现,它的东西,与神秘的功能做的。



请注意:我的大脑已经死亡了,所以如果这是不明确的,我道歉,我会回来,并进行修改



编辑2



有关一体,既 SetCurrentValueInteral 的SetValue 这两个叫 SetValueCommon 。所不同的是 SetCurrentValueInternal 重新评估与基值电流值,因为裹胁标志设置为真。 (保持在边界最小/最大的值),裁判



通过我挖,我发现 SetCurrentValue 的SetValue 有两个完全不同的结果,那就是:指定胁迫( IsInternal 似乎是在绑定属性是值类型的情况下,未使用)



我的证据是的文档指出:




的SetCurrentValue方法是另一种方式来设置属性,但它是不是在优先级,而是SetCurrentValue允许您更改属性的值不覆盖前一个值的来源的顺序。您可以使用SetCurrentValue要设定值时,没有给该值的优先级当地的价值......




有了这样说,移动拇指的滑块,因为它是调用 SetCurrentValueInternal 不影响基值。



此外,改变了,因为它要求手动更改基值的SetValue 。在 ValueProperty 依赖属性定义了如何使用要挟 CoerceValueCallback 在最后一段的此处。在什么样的事情在 Slider.Value



的更详细

强制与之交互在如在时间存在这些约束对胁迫的约束应用这种方式的基值,但是基准值仍然保留。因此,如果强制约束后解除,强制将最接近的值可能返回到基值,并有可能在财产胁迫影响会尽快的所有限制被取消停止。


< /块引用>

当然我继续读下去,的依赖项属性回调和验证 - 高级胁迫和回调场景,并发现这一点:




例如,在最小/最大/当前情况下,你可以选择有最低和最高是可由用户设置。如果是这样,您可能需要强制的最大总是大于最小,反之亦然。但如果强制处于活动状态,并且最大要挟到最小,它的叶子在不能设置的当前状态,因为它依赖于两个和约束到值之间的范围内,这是零。然后,如果最大或最小调节,电流将似乎跟随中的一个值,因为电流的所需值仍然存储,正在试图达到作为约束松开所需的值。




在最后,我想这是因为设计在滑块,因为它以这样的方式编码该必须始终最小最高之间。在将按照最高属性,当约束被解除(当前值在基准值范围)的值,属性将返回到其原始值。



最后,IMO WPF设计了滑盖这种方式,因为WPF齐头并进数据绑定。我以为他们设计,开发人员能充分利用数据绑定的,并会实施,以防止使用无效值逻辑的假设下。


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:

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.

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.

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.

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

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.

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

解决方案

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.

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:

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:

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:

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.

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:

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:

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.

Matter of fact try this.

  1. Start application
  2. Move thumb all the way to right (Value = 20)
  3. Shrink borders (Value = 8)
  4. Take thumb and move it to the left, then all the way to the right (Value = 8)
  5. Then grow borders

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.

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.

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

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

With that being said, moving the Thumb of the Slider doesn't effect the base value because it is calling 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,

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.

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.

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.

这篇关于Slider.Value关于调整变化最小的时候它不应该的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆