Coerce a WPF TextBox not working anymore in .NET 4

2019-01-08 01:52发布

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?

2条回答
Evening l夕情丶
2楼-- · 2019-01-08 02:25

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.

查看更多
闹够了就滚
3楼-- · 2019-01-08 02:30

You can use PropertyChanged. However, try binding to the EditValueProperty dependency instead of the TextProperty dependency (or events). It will work as desired.

查看更多
登录 后发表回答