views:

51

answers:

1

In my WPF application I have a TextBox where the user can enter a percentage (as int, between 1 and 100). The Text property is databound to a property in a ViewModel, where I coerce the value to be in the given range in the setter.

However, in .NET 3.5, the data is not shown properly in the UI after being coerced. In this post on MSDN, Dr. WPF states that you have to manually update the binding so the correct will be shown. Therefore, I have a TextChanged handler (in the View) which calls UpdateTarget(). In code:

View XAML:

<TextBox Text="{Binding Percentage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, TargetNullValue={x:Static sys:String.Empty}}"
    TextChanged="TextBox_TextChanged"/>

View codebehind:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    // Removed safe casts and null checks
    ((TextBox)sender).GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}

ViewModel:

private int? percentage;
public int? Percentage
{
    get
    {
        return this.percentage;
    }

    set
    {
        if (this.Percentage == value)
        {
            return;
        }

        // Unset = 1
        this.percentage = value ?? 1;

        // Coerce to be between 1 and 100.
        // Using the TextBox, a user may attempt setting a larger or smaller value.
        if (this.Percentage < 1)
        {
            this.percentage = 1;
        }
        else if (this.Percentage > 100)
        {
            this.percentage = 100;
        }
        this.NotifyPropertyChanged("Percentage");
    }
}

Unfortunately, this code breaks in .NET 4.0 (same code, simply changed TargetFramework to 4.0). Specifically, after I coerced the value for the first time, the TextBox ignores any further coerced values as long as I continue to enter integer values (since I am binding to an int).

So if I enter "123", after the 3 I see the value "100". Now if I enter "4", the setter in the ViewModel gets the value "1004", which it coerces to 100. The TextChanged event then fires (and the sender's TextBox.Text is "100"!), but the TextBox shows "1004". If I then enter "5", the setter gets the value "10045", etc.

If I then enter an "a", suddenly the TextBox shows the correct value, i.e. "100". The same occurs if I continue to enter numbers until the int overflows.

How can I fix this?

+1  A: 

Try using in xaml Explicit instead of PropertyChanged:

<TextBox Text="{Binding Percentage, Mode=TwoWay, UpdateSourceTrigger=Explicit, TargetNullValue={x:Static System:String.Empty}}"
             TextChanged="TextBox_TextChanged" />

and in code behind UpdateSource instead of UpdateTarget

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        // Removed safe casts and null checks
        ((TextBox)sender).GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }

Tested it and it works. Btw this problem will probably be resolved in a later version of .NET.

RonaldV