0

I have a UserControl and an int DependencyProperty called Value. This is bound to a text input on the UserControl.

public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(QuantityUpDown), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged, CoerceValue));

public int Value
{
    get { return (int) GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

private static object CoerceValue(DependencyObject d, object basevalue)
{
    //Verifies value is not outside Minimum or Maximum
    QuantityUpDown upDown       = d as QuantityUpDown;
    if (upDown == null)
        return basevalue;

    if ((int)basevalue <= 0 && upDown.Instrument != null)
        return upDown.Minimum;

    //Stocks and ForEx can have values smaller than their lotsize (which is assigned to Minimum)
    if (upDown.Instrument != null && 
        upDown.Instrument.MasterInstrument.InstrumentType != Cbi.InstrumentType.Stock &&
        upDown.Instrument.MasterInstrument.InstrumentType != Cbi.InstrumentType.Forex)
        return Math.Max(Math.Min(upDown.Maximum, (int)basevalue), upDown.Minimum);

    if (upDown.Instrument == null)
        return Math.Max(Math.Min(upDown.Maximum, (int)basevalue), upDown.Minimum);

    if (upDown.Instrument.MasterInstrument.InstrumentType == Cbi.InstrumentType.Stock ||
        upDown.Instrument.MasterInstrument.InstrumentType == Cbi.InstrumentType.Forex)
        return Math.Min(upDown.Maximum, (int)basevalue);

    return basevalue;
}

If a user enters a value greater than int.MaxValue in the text box, when the value comes into CoerceValue, the baseValue argument is 1. The same occurs if I provide a validation value callback on the DependencyProperty.

I'd like to handle this situation myself, such as setting the incoming value to int.MaxValue. Is there a way to do this?

Suren Srapyan
  • 66,568
  • 14
  • 114
  • 112
Sean Beanland
  • 1,098
  • 1
  • 10
  • 25
  • If you need to do validation before the binding passes the value to the source property (i.e. target == TextBox.Text, source == QuantityUpDown.Value), you can do that with [Binding.ValidationRules](https://msdn.microsoft.com/en-us/library/ms753962(v=vs.110).aspx). – 15ee8f99-57ff-4f92-890c-b56153 Jan 09 '17 at 14:22
  • 1
    The type of the property is `int`. Hence a value passed into the Coerce method can't be larger than `int.MaxValue`. – Clemens Jan 09 '17 at 14:30
  • @Clemens, obviously. Yet a user can enter a value larger than int.MaxValue. How can I handle that case? – Sean Beanland Jan 09 '17 at 14:31

1 Answers1

6

A int property can never be set to something else than an int value. The type of the Text property of a TextBox is however string and the error occurs when when the runtime is trying to set your int property to a string value that doesn't represent a valid integer.

Your dependency property cannot do much about this as it never gets set. As @@Ed Plunkett suggests in his comment you could use a ValidationRule to do something before the value conversion occurs and present an error message to the user if the conversion fails:

public class StringToIntValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        int i;
        if (int.TryParse(value.ToString(), out i))
            return new ValidationResult(true, null);

        return new ValidationResult(false, "Please enter a valid integer value.");
    }
}

<TextBox>
    <TextBox.Text>
        <Binding Path="Value" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:StringToIntValidationRule ValidationStep="RawProposedValue"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Please refer to the following blog post about data validation in WPF for more information: https://blog.magnusmontin.net/2013/08/26/data-validation-in-wpf/

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Is there a way to combine this with `IDataErrorInfo`? I have used this code within my view. But, I also need to implement something in the VM to handle this error. What I do is, check if the ErrorCollection is empty or not, which then determines whether the `RelayCommand` is enabled/disabled. – Eduards Jun 07 '21 at 09:46