Search

Changed behaviour from .Net 3.5 to .Net 4.0 of WPF TextBox formatting when PropertyChanged is used as UpdateSourceTrigger by Thies Schrader

Closed
as Fixed Help for as Fixed

13
0
Sign in
to vote
Type: Bug
ID: 588343
Opened: 8/23/2010 5:37:15 AM
Access Restriction: Public
1
Workaround(s)
9
User(s) can reproduce this bug
Using the .Net 4 Framework and editing a WPF textbox bound to a nullable decimal, where formatting is specified along with the PropertyChanged UpdateSourceTrigger, the number is formatted while being edited, causing incorrect entering of numbers.

Old behavoiur was that formatting was not done to the number until the field was exited, whereas the underlying property was updated as keys where pressed.

Work arounds (not optimal): Do not use PropertyChanged - UpdateSourceTrigger but LostFocus instead.
Details (expand)

Visual Studio/Silverlight/Tooling version

.NET Framework 4

What category (if any) best represents this feedback?

 

Steps to reproduce

Create a new WPF application project in Visual Studio 2010 and update the following files/classes:

MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox Text="{Binding TestMember, StringFormat=0.00, UpdateSourceTrigger=PropertyChanged, TargetNullValue=''}"></TextBox>
    </Grid>
</Window>

MainWindow.xaml.cs:
    public partial class MainWindow : Window
    {
        public class TestClass
        {
            public decimal? TestMember { get; set; }
        }

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = new TestClass();
        }
    }

Assuming Norwegian Locale is set (may cause same issue with English US but replace . with , in test string below)

Compile under .Net 4 Framework, and enter the number "1,23" - the result "12,30"
Compile under .Net 3.5 Framework, and enter the number "1,23" - the result "1,23" as expected.

Product Language

English

Operating System

Windows 7

Operating System Language

English (US)

Actual results

Compile under .Net 4 Framework, and enter the number "1,23" - the result "12,30"

Expected results

Compile under .Net 3.5 Framework, and enter the number "1,23" - the result "1,23" as expected. This is also the same behaviour as is with older binding using older frameworks and WinForms.
File Attachments
0 attachments
Sign in to post a comment.
Posted by Maximilian Haru Raditya on 3/26/2012 at 8:59 AM
@candritzky: That's interesting, I become curious what's meant by "fixed" here. I haven't tried with WPF 4.5 BETA yet, maybe later.
Posted by candritzky on 3/26/2012 at 5:22 AM
I, too, experience the same problem, but in a much simpler context. Simply bind a TextBox.Text property to a property of type TimeSpan with UpdateSourceTrigger=PropertyChanged. You will see that it is hardly possible to enter the desired TimeSpan value.

As you at MS marked this issue as "Fixed" I tried to today with VS11 Beta and .NET 4.5 Beta, but the issue is still present.
Posted by Thies Schrader on 7/18/2011 at 12:38 AM
Thank you for the reply.

The main issues we have are;
- updating other GUI elements based of the values entered. That is, if you enter a number into one field, 5 other fields should be displayed for details. If we use LostFocus, we need to wait until the user exits the field to get an event to update the GUI, which messes up the tab-order in our forms.
- making sure there are not unsaved changes when a form is closed. Given a textbox has focus, and the user updates the value in the text box - the user then closes the form using the red 'X'. When this happens, the underling property is not updated, and the users changes are not caught (and therefore not saved)

In our case, we see this problem most often with text box fields, bound to a decimal number - where we have formatting set to N2. While the user enters a number, the cursor input position changes, and the number entered is different than the user's intent.

Maybe given the decimal value of the formatted version of the input string is the same as the decimal value of the non-formatted value, the formatting of the text box is not updated, e.g.:

if(DecimalValue(Formatted(textstring)) == DecimalValue(textstring))
     do not update textbox

textstring = "123456,5"
Formatted(textstring) == "123 456,50"     (based on N2 and European style locale)
DecimalValue(123456,5) == 123456,5

This would allow the user to enter the text as they see fit, and not see the text box being updated with characters they did not enter - causing repositioning and incorrect entering of numbers.
Posted by Microsoft on 1/12/2011 at 10:27 AM
The workaround you've already mentioned - use LostFocus instead of PropertyChanged - is the best you can do in 4.0. I assume by "not optimal" you mean that you sacrifice some behavior by doing this, chiefly the ability to show validation feedback after every keystroke.

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)
Posted by Microsoft on 8/23/2010 at 9:19 PM
Thanks for your feedback.

We are rerouting this issue to the appropriate group within the Visual Studio Product Team for triage and resolution. These specialized experts will follow-up with your issue.

Posted by Maximilian Haru Raditya on 8/23/2010 at 5:16 PM
This bug also appears to happen when specifying a converter and setting UpdateSourceTrigger to PropertyChanged in a binding as well. For example:

<TextBox Text="{Binding Price, Converter={StaticResource PriceConverter}, UpdateSourceTrigger=PropertyChanged}" />

    [ValueConversion(typeof(decimal), typeof(string))]
    public class PriceAndStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var returnValue = 0.0M;
            if (value is decimal)
            {
                returnValue = (decimal) value;
            }

            var convertedReturnValue = returnValue.ToString("C2", culture);
            return convertedReturnValue;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
            {
                return Binding.DoNothing;
            }

            decimal returnValue;
            if (!decimal.TryParse(value.ToString(), NumberStyles.Currency, culture, out returnValue))
            {
                returnValue = 0.0M;
            }

            return returnValue;
        }
    }
Posted by Microsoft on 8/23/2010 at 5:08 PM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(http://support.microsoft.com)
Sign in to post a workaround.
Posted by Johan Frisk on 5/25/2012 at 4:47 AM
You could bind the textbox to a string property instead and let that property update the underlying decimal as needed. Here is a basic example:

        private decimal? _amount;
        public decimal? Amount
        {
            get { return _amount; }
            set
            {
                if (_amount != value)
                {
                    _amount = value;

                    // Do whatever needs to be done when number is changed here

                    if (!_changingAmountString)
                        if (_amount.HasValue)
                            AmountString = _amount.Value.ToString("N2");
                        else
                            AmountString = String.Empty;

                    OnPropertyChanged("Amount");
                }
            }
        }
        private string _amountString;
        private bool _changingAmountString;
        public string AmountString
        {
            get { return _amountString; }
            set
            {
                if (_amountString != value)
                {
                    _amountString = value;
                    OnPropertyChanged("AmountString");

                    _changingAmountString = true;
                    decimal tmp;
                    if (decimal.TryParse(_amountString, out tmp))
                        Amount = tmp;
                    else
                        Amount = null;
                    _changingAmountString = false;
                }
            }
        }


Hope that helps!