滑块值变化的最小和最大的后不更新 [英] Slider Value does not update after changing Minimum and Maximum

查看:118
本文介绍了滑块值变化的最小和最大的后不更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我回答这个的问题,而这样做,我发现了很多怪异的行为。由于我的亲MVVM,我放在一起的解决方案,看看我会看到相同的行为。什么我的解决方案揭示的是,即使我结合双向 Slider.Value ,它不被更新中 Slider.Maximum Slider.Minimum 变更后我的视图模型;即我的视图模型的可以被外界 UpperLimit用户假定下限与此同时 Slider.Value (其中我的虚拟机的属性绑定到)是范围之内。



在上述的问题,改变 Slider.Maximum Slider.Minimum 似乎始终保持 Slider.Value 的范围内,有时还原 Slider.Value 来它采用了先前的值设置为



微软的滑块源代码




  1. 为什么确实 Slider.Value 变更/恢复其值作为链接的问题看到尽管当前值为分钟内/最大范围是多少?

  2. 为什么没有我的视图模型的财产,势必 Slider.Value 与之匹配的双向更改后绑定 UpperLimit用户假定下限


    • 请注意,绑定最大&安培;最小的工作




主窗口。 XAML:

 <&DockPanel中GT; 
<滑块NAME =MySliderDockPanel.Dock =评出的AutoToolTipPlacement =BottomRightVALUE ={绑定值,模式=双向}最大={结合UpperLimit用户}最小={结合假定下限}/>
<按钮名称=MyButton1点击=MyButton1_ClickDockPanel.Dock =评出的CONTENT =缩水边界/>
<按钮名称=MyButton2点击=MyButton2_ClickDockPanel.Dock =评出的VerticalAlignment =评出的CONTENT =增长边界/>
<按钮名称=MyButton3点击=MyButton3_ClickDockPanel.Dock =评出的VerticalAlignment =评出的CONTENT =打印ItemVM值/>
< / DockPanel中>



MainWindow.xaml.cs:



 公共部分类主窗口:窗口
{
私人只读ItemViewModel项目;
公共主窗口()
{
的InitializeComponent();
的DataContext = =项新ItemViewModel(新项目(1,20,0.5));
}

私人无效MyButton1_Click(对象发件人,RoutedEventArgs E)
{
//MySlider.Minimum = 1.6;
//MySlider.Maximum = 8;
item.LowerLimit = 1.6;
item.UpperLimit = 8;

}

私人无效MyButton2_Click(对象发件人,RoutedEventArgs E)
{
//MySlider.Minimum = 0.5;
//MySlider.Maximum = 20;
item.LowerLimit = 0.5;
item.UpperLimit = 20;
}

私人无效MyButton3_Click(对象发件人,RoutedEventArgs E)
{
System.Diagnostics.Debug.WriteLine(项值:+ item.Value) ;
System.Diagnostics.Debug.WriteLine(滑块值:+ MySlider.Value);
}
}



项目/ ItemViewModel:

 公共类ItemViewModel:INotifyPropertyChanged的
{
私人只读项目_item;

公共事件PropertyChangedEventHandler的PropertyChanged;

公共ItemViewModel(项目项)
{
_item =项目;
}

公共双UpperLimit用户
{
得到
{
返回_item.UpperLimit;
}

{
_item.UpperLimit =价值;
NotifyPropertyChanged();
}
}
公共双假定下限
{
得到
{
返回_item.LowerLimit;
}

{
_item.LowerLimit =价值;
NotifyPropertyChanged();
}
}

公共double值
{
得到
{
返回_item.Value;
}

{
_item.Value =价值;
NotifyPropertyChanged();
}
}

私人无效NotifyPropertyChanged([CallerMemberName]字符串参数propertyName =)
{
的PropertyChanged?.Invoke(这一点,新PropertyChangedEventArgs( propertyName的));
}
}
公共类项目
{
私人双_value;
私人双_upperLimit;
私人双_lowerLimit;
公共double值
{
得到
{
返回_value;
}

{
_value =值;
}
}
公共双UpperLimit用户
{
得到
{
返回_upperLimit;
}

{
_upperLimit =价值;
}
}
公共双假定下限
{
得到
{
返回_lowerLimit;
}

{
_lowerLimit =价值;
}
}

公共项目(双精度值,双UPPERLIMIT,双LOWERLIMIT)
{
_value =值;
_upperLimit = UPPERLIMIT;
_lowerLimit = LOWERLIMIT;
}
}




步骤重现:




  1. 点击 MyButton3




    • 项值= 1


    • 滑块值= 1



  2. 移动滑块/拇指一路权


  3. 点击 MyButton3




    • 项目值= 20


    • 滑块值= 20



  4. 点击 MyButton1


  5. 点击 MyButton3




    • 项目值= 20


    • 滑块值= 8






如果你把在 MyButton3_Click 一个破发点,并执行的最后一步,你可以看到, MySlider.Value = 8


解决方案

这是因为看重胁迫,你可以的了解更多关于它这里



一般来说,WPF控件设计与松散的数据结合来使用。加入他们的get / set访问和活动等,以协助从的WinForms过渡,但他们补充逻辑的附加层,这并不总是通过过滤你的绑定属性。这是问题的众多例子中的一个,当你混合与坏(直接访问控制)好WPF代码(数据绑定)可能出现。



编辑



对于一个依赖属性胁迫回调处理程序被调用时的电流值需要确定。你可以把它作为最后的机会来修改的结果;它不改变的返回什么结合本身,仅值。如果您有包含10值的视图模型(说)整数属性,并可以在文本框绑定到它是这样的:

 < TextBlock的文本={结合myvalue的}/> 

这值会明显显示为10.现在让我们假设您创建了一个整数依赖属性的用户控件所谓myProperty的,让我们说,强迫回调乘任何电流值是2:

 <局部:MyControl X:名称=显示myControlmyProperty的={结合myvalue的}/> 

这会做两手空空。我们结合myProperty的给myValue属性,但它只是一个DP。我们从来没有真正调用它。现在,让我们说,我们添加第二个文本框,但这次绑定到MyControl.MyProperty:

 < TextBlock的文本={绑定路径= myProperty的,的ElementName = myControl}/> 



第一控制将继续显示10(这是价值仍然在我们的视图模型),但第二个会显示20,因为强制呼吁myProperty的DP修改,它得到了它自己的绑定到myvalue的值。 (有趣的是在做双向绑定也适用,在价值胁迫回调结果每当值改变正在加倍)。



重要线索,所有这一切的是,强制回调只是调用时的值需要或者通过其他依赖自身更新或手动调用的getter代码解决。很显然,你呼吁滑块所得的值吸气剂会发生,而是简单地改变最小的值,最高不。这就像更改您的视图模型属性的值,而不必调用属性更改通知......你知道你做了什么,但没有人这么做



延伸阅读:的的RangeBase源代码(特别是 ConstrainToRange 强制回调)和的滚动条源代码(即UpdateValue这是只有调用时滑块或缩略图被拖动)。


I was answering this question, and while doing so I found a lot of weird behavior. Since I'm pro-MVVM, I put together a solution to see if I would see the same behavior. What my solution uncovers is that even though I'm binding TwoWay to Slider.Value, it is not being updated in my ViewModel after Slider.Maximum and Slider.Minimum change; i.e. my view model's Value can be outside UpperLimit and LowerLimit, meanwhile Slider.Value (which my VM's Value property is bound to) is inside the range.

In the aforementioned question, changing Slider.Maximum or Slider.Minimum seems to always keep the Slider.Value in range, and sometimes "restores" Slider.Value to a previous value it used to be set to.

Microsoft's Slider Source Code

  1. Why does Slider.Value change/restore its value as seen in linked question even though the current value is within the Min/Max range?
  2. Why doesn't my view model's Value property, bound to Slider.Value match with a TwoWay binding after changing the UpperLimit and LowerLimit?
    • Note that the bindings to Maximum & Minimum do work

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>

MainWindow.xaml.cs:

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

Item/ItemViewModel:

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

Steps to reproduce:

  1. Click MyButton3

    • Item Value = 1

    • Slider Value = 1

  2. Move Slider/Thumb all the way to right

  3. Click MyButton3

    • Item Value = 20

    • Slider Value = 20

  4. Click MyButton1

  5. Click MyButton3

    • Item Value = 20

    • Slider Value = 8

If you put a break point in MyButton3_Click and execute the last step, you can see that MySlider.Value = 8

解决方案

This is due to value coercion, you can read more about it here.

Generally speaking, WPF controls are designed to be used with loose data binding. Their get/set accessors and events etc were added to assist in the transition from Winforms but they add an additional layer of logic that doesn't always filter through to your bound properties. This is one of the many examples of problems that can arise when you mix "good" WPF code (data binding) with "bad" (accessing controls directly).

EDIT:

The Coercion callback handler for a dependency property is invoked whenever the current value needs to be determined. Think of it as the very last chance to modify the result; it doesn't change the binding itself, only the value of what's being returned. If you have an integer property in a view model (say) containing the value of 10 and you bind a textbox to it like this:

    <TextBlock Text="{Binding MyValue}" />

That value will obviously display as 10. Now let's say you create a user control with an integer dependency property called "MyProperty", and let's say that the coercion callback multiplies whatever the current value is by 2:

    <local:MyControl x:Name="myControl" MyProperty="{Binding MyValue}" />

This will do nothing whatsoever. We're binding MyProperty to the MyValue property, but it's just a DP. We're never actually invoking it. Now let's say we add a second TextBox but this time bind to MyControl.MyProperty:

    <TextBlock Text="{Binding Path=MyProperty, ElementName=myControl}" />

The first control will continue to display 10 (which is the value still in our view model) but the second will display 20, because the coercion call for the MyProperty DP modified the value that it got from it's own binding to MyValue. (Interestingly doing two-way binding also works, the coercion callback results in the value being doubled whenever the value is changed).

The important clue to all this is that the coercion callback is only called when the value needs to be resolved either by another dependency updating itself or by the code manually calling the getter. Obviously you calling the Value getter on Slider results in this happening, but simply changing the values of Minimum and Maximum does not. It's like changing the value of a property in your view model without invoking property change notification...you know what you've done but nothing else does.

Further reading: the RangeBase source code (specifically the ConstrainToRange coercion callback) and the Slider source code (namely UpdateValue which is only called when the slider or thumbnail are dragged).

这篇关于滑块值变化的最小和最大的后不更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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