kaycee
kaycee

Reputation: 1309

wpf textbox custom control type error

i wrote custom control base on TextBox which has also Minimum and Maximum inputs as follows:

    public class NumericTextBox : TextBox
    {
        static NumericTextBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericTextBox), new FrameworkPropertyMetadata(typeof(NumericTextBox)));
        }


        public static readonly DependencyProperty MinimumProperty =
            DependencyProperty.Register("Minimum", typeof(int), typeof(NumericTextBox), new PropertyMetadata(default(int)));

        public int Minimum
        {
            get { return (int)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        }

        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register("Maximum", typeof(int), typeof(NumericTextBox), new PropertyMetadata(100));

        public int Maximum
        {
            get { return (int)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }


        public new static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(int), typeof(NumericTextBox),
                                        new FrameworkPropertyMetadata(
                                            default(int),
                                            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                            null,
                                            CoerceCurrentValue),
                                            IsValid);

        public new int Text
        {
            get { return (int)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }


        private static object CoerceCurrentValue(DependencyObject d, object baseValue)
        {
            var numericTextBox = (NumericTextBox)d;
            var intValue = (int)baseValue;
            if (intValue < numericTextBox.Minimum) intValue = numericTextBox.Minimum;
            if (intValue > numericTextBox.Maximum) intValue = numericTextBox.Maximum;

            if ((int)baseValue != intValue)
                numericTextBox.Text = intValue;

            return intValue;
        }

        private static bool IsValid(object value)
        {

            if (value == null)
                return false;

            int intValue;
            var result = Int32.TryParse(value.ToString(), out intValue);

            return result;


        }
}

and in my xaml i call it:

<controls:NumericTextBox
    Grid.Row="0"
    Grid.Column="1"
    Margin="5"
    VerticalAlignment="Center"
    Text="{Binding Test, UpdateSourceTrigger=PropertyChanged}"
    Minimum="0"
    Maximum="100"
    />

it is bind to Test property in my view model (as int). everything works good until i type a character and i get binding error:

System.Windows.Data Error: 7 : ConvertBack cannot convert value '1a' (type 'String'). BindingExpression:Path=Text; DataItem='NumericTextBox' (Name=''); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') FormatException:'System.FormatException: Input string was not in a correct format. at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) 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)'

it may be because the original Text property in TextBox is string... but i'm not sure. please assist on that.

Upvotes: 0

Views: 1389

Answers (1)

Sheridan
Sheridan

Reputation: 69959

You are going about this in the wrong way. You are not restricting the input of the TextBox to only numerical numbers. The Framework is trying to convert a string into an int to fit into your new int Text property, but as the error says: Input string was not in a correct format., eg. not an int. Try adding this into the constructor instead:

PreviewTextInput += new TextCompositionEventHandler((s, e) => e.Handled = 
    !e.Text.All(c => Char.IsNumber(c) && c != ' '));
PreviewKeyDown += new KeyEventHandler((s, e) => e.Handled = e.Key == Key.Space);

They simply work by setting e.Handled to true if non numerical values are input and this has the effect of ignoring the input on those occasions.

Also, you don't need to implement your own Text property... that is just confusing matters. Just use the original one and parse the value into an int wherever you need to, resting assured that it will be a number. I think that these two handlers should ensure that. Let me know if you have any problems.

Another idea is to simply create an AttachedProperty using these handlers and then you can apply it to any TextBox control. Of course, you'd then need to implement your Minimum and Maximum properties as AttachedProperties too, but then you could do something like this:

<TextBox Text={Binding Test} Attached:TextBoxProperties.IsNumeric="True" 
    Attached:TextBoxProperties.Minimum="0" Attached:TextBoxProperties.Maximum="100" />

You can find out more from the Attached Properties Overview page on MSDN.

UPDATE >>>

If you don't want the user to be able to delete the last character from the TextBox, then we can just change one of the handlers to disallow it:

PreviewKeyDown += PreviewKeyDown;
...
private void PreviewKeyDown(object sender, KeyEventArgs e)
{
    TextBox textBox = sender as TextBox;
    e.Handled = e.Key == Key.Space || 
    (textBox.Text.Length == 1 && (e.Key == Key.Delete || e.Key == Key.Back));
}

Upvotes: 1

Related Questions