1
votes

Bound property not called for textbox value > int.MaxValue

I need to revert to last value in a textbox, if entered value is greater than int.MaxValue. Please refer below code for this.

MainWindow.xaml

<Window x:Class="SampleWPFApplication.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">
    <StackPanel>
        <TextBox Text="{Binding Count, UpdateSourceTrigger=LostFocus}" />
        <Button Content="Click!" Width="100" Height="50"/>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System.Windows;
namespace SampleWPFApplication
{
     /// <summary>
     /// Interaction logic for MainWindow.xaml
     /// </summary>
     public partial class MainWindow : Window
     {
         public MainWindow()
         {
             InitializeComponent();
             this.DataContext = this;
         }

         private int _count = 32;
     public int Count
         {
            get { return _count;}
            set 
            {
                if(value <= int.MaxValue)
                    _count = value;
            }
         }
     }
}

Set a breakpoint at the setter of the Count property. This breakpoint is hit for values less than int.MaxValue. int.Maxvalue has a value 2147483647 on my system. if you give a value greater than this, breakpoint won't hit and textbox is encircled with red rectangle. I would like to revert to previous value in the textbox for out of range values.

If I replace int.MaxValue in the above property setter with a value not equal to int.MaxValue(For e.g 999), it works fine. I believe textbox internally has a Max value as int.MaxValue and on giving a value greater than this it does its own validation, which fails and binding is not updated.

I have set the PresentationTraceSources.TraceLevel=High in the xaml as explained in link http://blogs.msdn.com/b/wpfsldesigner/archive/2010/06/30/debugging-data-bindings-in-a-wpf-or-silverlight-application.aspx and got the StackOverFlowException.

System.Windows.Data Warning: 95 : BindingExpression (hash=52085517): Got LostFocus event from TextBox (hash=10223660) System.Windows.Data Warning: 90 : BindingExpression (hash=52085517): Update - got raw value '2147483649' 'SampleWPFApplication.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework-SystemData\v4.0_4.0.0.0__b77a5c561934e089\PresentationFramework-SystemData.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled. System.Windows.Data Error: 7 : ConvertBack cannot convert value '2147483649' (type 'String'). BindingExpression:Path=Count; DataItem='MainWindow' (Name=''); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') OverflowException:'System.OverflowException: Value was either too large or too small for an Int32. at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info) at System.String.System.IConvertible.ToInt32(IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) at MS.Internal.Data.SystemConvertConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture) at System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)' System.Windows.Data Warning: 93 : BindingExpression (hash=52085517): Update - implicit converter produced {DependencyProperty.UnsetValue} System.Windows.Data Warning: 94 : BindingExpression (hash=52085517): Update - using final value {DependencyProperty.UnsetValue}

Can someone explain this behavior and way yto overcome this.? Also, is there a way to override the RED rectangle behavior?

Similar question (TextBox - Issue with validation using IDataErrorInfo with integer Value more than int.MaxValue (2147483647)) with no solution

Workaround: One workaround is to change the datatype of property from int to long.

2

2 Answers

0
votes

The reason you can't get to the Setter is that WPF's automatic Binding Validation procedure has halted (IsValid = false) which is what you want as an Integer cannot be greater than int.MaxValue. Its basically already asked what your setter is asking beforehand and then shown the ErrorTemplate on failure

You can change the ErrorTemplate. this puts an orange border around your textbox and displays the error message reported.

<TextBox Text="{Binding Count, UpdateSourceTrigger=LostFocus}">
        <Validation.ErrorTemplate>
            <ControlTemplate>
                <StackPanel>
                    <!-- Placeholder for the TextBox itself -->
                    <Border BorderBrush="Orange" BorderThickness="2">
                        <AdornedElementPlaceholder x:Name="textBox"/>
                    </Border>
                    <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Green"/>
                </StackPanel>
            </ControlTemplate>
        </Validation.ErrorTemplate>
      </TextBox>

Here is a really good article on this behaviour

0
votes

The reason why the setter is not called is that before the property is set, the value is first converted by the binding to appropriate type (in this case string is converted to int). Since 2147483649 is not a valid int value (it's larger than int.MaxValue) the conversion fails and the binding aborts source update. In this case, as @dellywheel mentioned in his/her answer, the IsValid property of the binding is set to false and error template is shown on the text box.

One way to achieve described behavior would be to define a proxy property which would take care of validation/conversion and bind to it instead of the Count property. Here's an example:

public string CountProxy
{
    get { return Count.ToString(); }
    set
    {
        int n;
        if (int.TryParse(value, out n) && n <= MaxValue)
            Count = n;
    }
}

If MaxValue is going to be int.MaxValue you can skip the comparison, because int.TryParse will return false for all out-of-range values.