4

I have an app I've recently converted from a VS 2008 .NET 3.5 project, to a VS2010 .NET 4 project. Some of the WPF dialogs in the project behave differently after the conversion. I'd like to understand what is causing this difference in behaviour, so I can find and fix other areas that may now have problems.

As an example, I have a MVVM dialog that lets the user type in a number. The number is stored internally as a double, and the user can only accept the dialog if the text they type is a valid double. So I have a text box bound to a string in the ViewModel, and an OK button which is only enabled when the string is a valid double. The relevant Xaml looks like this:

<TextBox Text="{Binding ValueString, UpdateSourceTrigger=PropertyChanged}"/>
<Button IsEnabled="{Binding ValueIsValid}">OK</Button>

And the ViewModel looks like:

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

This worked fine in .NET 3.5, but when it's running on .NET 4 it has a problem when the user is entering a number. For example, if the user enters "3.05555" into the text box on the .NET 3.5 version, everything is fine. However in the .NET 4 version, they can enter 3.05 fine but when they type the next "5" the value of text box changes to "3.06" and then changes to "3.07" if they press 5 again. It's as if the value is being read-back from the ValueString property as soon as it has been set (thus being formatted as "G3"), but this didn't used to happen with .NET 3.5.

I've had a look over What's New in the .NET Framework 4 (including What's New in WPF Version 4), but I didn't find anything about this change.

If you want to see this for yourself, I've created a small example VS2010 solution you can download from here. The BindingTest2008 project has been converted from VS 2008 and targets .NET 3.5, and the BindingTest2010 project was created in VS 2010 targeting .NET 4. The code is the same in both projects, but the .NET 4 project has this problem.

I'd appreciate any help on understanding why this is happening. Thanks.

Updated: Removing the call the RaisePropertyChanged("ValueIsValid"); doesn't change the behaviour and typing an invalid number (e.g. "3.1a") doesn't intermediately get replaced by the last valid number (e.g. "3.1" in that case). Also numbers can be entered to a higher precision than 3 significant digits. E.g. "3.0545555" - the problem only seems to happen when the thing you've just typed would cause a rounding on the 3rd significant figure.

Wilka
  • 28,701
  • 14
  • 75
  • 97

2 Answers2

5

The reason for this behaviour difference is:

In 3.5, the binding would write a new value back to the source after each keystroke, without changing the TextBox text. But that text might not represent the source's value accurately, perhaps because it doesn't include formatting and conversion, or because the source changed the value (in the property-setter) to something else. This led to frequent and vehement complaints - people wanted the TextBox to show the source's value, exactly as a TextBlock would if bound to the same property with the same converters and formatting. The UI should display what's actually in the data, not what the end-user typed.

To fix this class of bugs in 4.0, the binding now applies formatting and conversion to the source's new value after every update. (LostFocus bindings already did this in 3.5.) The TextBox now shows what's in the data, but that can make the user's typing more complex.

We plan to improve this scenario in the next release in at least two ways: 1. When the TextBox text is replaced with a revised string, the insertion point (cursor) that worked for the old string may no longer be correct for the new string. The heuristic that guesses where to put the cursor can be improved. 2. Bindings will expose a way to do LostFocus (or Explicit) updates with partial validation after each keystroke. The formatting/conversion only gets applied when focus changes, but the user gets validation feedback after every keystroke.

  • Sam (WPF team)

From "Changed behaviour from .Net 3.5 to .Net 4.0 of WPF TextBox formatting when PropertyChanged is used as UpdateSourceTrigger"

Wilka
  • 28,701
  • 14
  • 75
  • 97
  • 2
    Here we are, 4.5 is out, no improvement in the behaviour, and no real alternative. Editing behaviour is bizarre to say the least. Time to roll that change back, maybe? – Robin Davies Apr 13 '13 at 10:46
2

It looks like problem is in this line:

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

The .Net 4 version behaves correctly because the value gets formatted using "G3" format string, which means that there will be 3 significant digits in the result string (3.055 becomes 3.06).

The difference between 3.5 and 4 is that, apparently, the binding system was slightly changed. In 3.5, when the setter of a property is called and it raises the PropertyChanged event, the binding doesn't get re-evaluated (the getter is not being called). While in .Net 4, after the PropertyChanged event gets fired, the binding gets re-evaluated, i.e. the getter of the property IS called and the value returned by the getter is displayed in the text box.

Pavlo Glazkov
  • 20,498
  • 3
  • 58
  • 71
  • It's correct that the reason for the rounding is because of the formatting in the get method (which I do want, so that the value is displayed as 3 signification digits when you first bring up the dialog). However, even if I remove the call to RaisePropertyChanged the problem still happens. – Wilka Jan 17 '11 at 13:24
  • Yes, RaisePropertyChanged doesn't make any difference. The binding system just works this way - it refreshes value from the source. You can achieve the desired behavior by changing the UpdateSourceTrigger of the binding to LostFocus. – Pavlo Glazkov Jan 17 '11 at 13:59
  • 1
    If it was always refreshing the value from the source, then wouldn't that also mean if I type 'a' it would replace it with the last valid number? Seeing as the source only stores the last valid number. – Wilka Jan 17 '11 at 15:08
  • 2
    It doesn't replace the text in the text box with the old valid number because value of the property that getter returns hasn't been changed. The binding system caches the value and doesn't unnecessarily update the UI. – Pavlo Glazkov Jan 17 '11 at 15:53
  • Ah, yes - I hadn't considered the caching. – Wilka Jan 17 '11 at 16:02