为什么在.NET 4和.NET 3.5中绑定设置行为不同?

4
我最近将一个VS 2008 .NET 3.5项目转换为了一个VS2010 .NET 4项目,其中一些WPF对话框在转换后的项目中表现不同。我想了解是什么原因导致了这种行为差异,以便找到和修复其他可能存在问题的领域。
例如,我有一个MVVM对话框,让用户输入一个数字。该数字在内部存储为double类型,只有当用户输入的文本为有效的double时,才能接受对话框。因此,我有一个绑定到ViewModel中字符串的文本框,以及一个OK按钮,只有在字符串为有效double时才启用。相关的Xaml代码如下:
<TextBox Text="{Binding ValueString, UpdateSourceTrigger=PropertyChanged}"/>
<Button IsEnabled="{Binding ValueIsValid}">OK</Button>

而且ViewModel看起来像:

class ViewModel : INotifyPropertyChanged
{
    private double actualValue;
    public string ValueString
    {
        get { return actualValue.ToString("G3"); }
        set
        {
            double doubleValue;
            if (double.TryParse(value, NumberStyles.Float, CultureInfo.CurrentCulture, out doubleValue))
            {
                actualValue = doubleValue;
                ValueIsValid = true;
                RaisePropertyChanged("ValueString");
            }
            else
            {
                ValueIsValid = false;
            }
        }
    }

    private bool valueIsValid = true;
    public bool ValueIsValid
    {
        get { return valueIsValid; }
        set
        {
            if (valueIsValid != value)
            {
                valueIsValid = value;
                RaisePropertyChanged("ValueIsValid");
            }
        }
    }

    private void RaisePropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

这在.NET 3.5中运行良好,但当它在.NET 4上运行时,当用户输入数字时就会出现问题。例如,如果用户在.NET 3.5版本的文本框中输入"3.05555",一切正常。然而,在.NET 4版本中,他们可以输入3.05,但当他们输入下一个“5”时,文本框的值就会变为“3.06”,如果再次按下5,则变为“3.07”。就像该值被从ValueString属性读回一样(因此被格式化为“G3”),但在.NET 3.5中没有发生过这种情况。
我查看了.NET Framework 4中的新功能(包括WPF Version 4中的新功能),但没有找到关于此更改的任何信息。
如果您想亲自查看,我创建了一个小例子VS2010解决方案,您可以从这里下载。BindingTest2008项目已从VS 2008转换并针对.NET 3.5进行了目标设置,而BindingTest2010项目是在VS 2010中创建的,针对.NET 4进行了目标设置。两个项目的代码相同,但.NET 4项目存在此问题。
我希望能得到任何有关为什么会发生这种情况的帮助。谢谢。
更新:删除对“RaisePropertyChanged(“ValueIsValid”);” 的调用不会改变行为,并且输入无效数字(例如“3.1a”)不会立即被上一个有效数字(例如在那种情况下的“3.1”)替换。还可以输入比3个有效数字更高的精度。例如“3.0545555”-当您刚刚输入的内容会导致第三个有效数字四舍五入时,才会出现问题。
2个回答

5
这种行为差异的原因是:
在3.5中,绑定会在每次按键后向源写入一个新值,而不更改TextBox文本。但是,这个文本可能不能准确地表示源的值,可能因为它不包括格式和转换,或者因为源将值(在属性设置器中)更改为其他值。这导致了频繁和激烈的抱怨 - 人们希望TextBox显示源的值,就像如果使用相同的转换器和格式绑定到相同的属性的TextBlock一样。UI应该显示实际数据中的内容,而不是最终用户输入的内容。 为了在4.0中修复此类错误,绑定现在在每次更新后对源的新值应用格式化和转换。(LostFocus绑定在3.5中已经这样做了。)TextBox现在显示数据中的内容,但这可能使用户的输入更加复杂。 我们计划通过以下至少两种方式改进此场景: 1.当TextBox文本被替换为修改后的字符串时,适用于旧字符串的插入点(光标)可能不再适用于新字符串。可以改进猜测放置光标的启发式方法。 2.绑定将公开一种方式,在每次键入后进行丢失焦点(或显式)更新,并进行部分验证。格式化/转换仅在焦点更改时应用,但用户每次键入后都会得到验证反馈。 - Sam(WPF团队)

来自 "当使用PropertyChanged作为UpdateSourceTrigger时,WPF TextBox在从.Net 3.5到.Net 4.0的行为变化" 的内容


2
我们来了,4.5版本发布了,行为没有改进,也没有真正的替代方案。编辑行为至少可以说是奇怪的。也许是时候回滚这个更改了? - Robin Davies

2

看起来问题出在这一行:

 get { return actualValue.ToString("G3"); }

.Net 4版本表现正确,因为该值使用“G3”格式字符串进行格式化,这意味着结果字符串中将有3个有效数字(3.055变为3.06)。

3.5和4之间的区别在于,显然绑定系统略有改变。在3.5中,当调用属性的setter并引发PropertyChanged事件时,绑定不会被重新评估(getter不会被调用)。而在.Net 4中,触发PropertyChanged事件后,绑定会被重新评估,即属性的getter被调用,并显示getter返回的值在文本框中。


正确的是,四舍五入的原因是由于get方法中的格式设置(我确实希望如此,这样当您首次打开对话框时,该值将显示为3个有效数字)。但是,即使我删除对RaisePropertyChanged的调用,问题仍然会发生。 - Wilka
是的,RaisePropertyChanged 没有任何区别。绑定系统只是这样工作 - 它从源刷新值。您可以通过将绑定的 UpdateSourceTrigger 更改为 LostFocus 来实现所需的行为。 - Pavlo Glazkov
1
如果它总是从源刷新值,那么这是否意味着,如果我输入'a',它会用最后一个有效数字替换它?因为源仅存储最后一个有效数字。 - Wilka
2
它不会用旧的有效数字替换文本框中的文本,因为getter返回的属性值没有改变。绑定系统缓存该值并且不会不必要地更新UI。 - Pavlo Glazkov
啊,是的 - 我没有考虑到缓存。 - Wilka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接