Darf Zon
Darf Zon

Reputation: 6378

How to set a null value to a property when it throws an error?

Binding is so powerful in WPF. Supposed that we have a Number property (nullable int) and is bound to a textbox.

I realized when it throws an error, the property has the last correct value.

I mean these are the processes:

TEXTBOX: ""     PROPERTY: null
TEXTBOX: "2"    PROPERTY: 2
TEXTBOX: "2b"   PROPERTY: 2   <-- here is the problem, should be null instead 2(by the error)

Is there a way which the binding set a null value when it produce an error?

Some persons told me I need to implement IDataErrorInfo, but I guess that interface is to validate business rules. So I wouldn't prefer user it.

UPDATE:

    <TextBox Text="{Binding Number, UpdateSourceTrigger=PropertyChanged,
        ValidatesOnExceptions=True, ValidatesOnDataErrors=True,
        NotifyOnValidationError=True, TargetNullValue={x:Static sys:String.Empty}}"

Upvotes: 2

Views: 2294

Answers (3)

Steve Greatrex
Steve Greatrex

Reputation: 15999

I think that the simplest way to get that behaviour is to use an IValueConverter to convert from string to int?:

public class NullableIntConverter : IValueConverter
{
    public static NullableIntConverter Instance = new NullableIntConverter();
    public void ConvertBack(object value, ...)
    {
        int intValue = 0;
        if (int.TryParse((string)value, out intValue))
            return intValue;

        return null;
    }

    public void Convert(object value, ...)
    {
        return value.ToString();
    }
}

Then you can specify this in your binding as below (where local is mapped to your converter namespace):

<TextBox Text="{Binding Number, Converter="{x:Static local:NullableIntConverter.Instance}" ... />

Upvotes: 1

Rachel
Rachel

Reputation: 132568

You are using UpdateSourceTrigger=PropertyChanged, which means that anytime the user hits a key, it is storing the data in your data context

For example, user types 2, then your property is equal to "2". User types b and it will attempt to replace "2" with "2b", which fails, so the original property of "2" remains.

Remove the UpdateSourceTrigger and it will revert to the default of LostFocus, which means it will only update the property when the TextBox loses focus.

You could set the property to null when an error is produced, but I would not recommend doing that because then if a user accidently hits the wrong key, the TextBox would get cleared.

As a side note, use IDataErrorInfo for all validation, not just business rule validation. WPF is built to work with it. My Models use it to verify their data is the correct length, type, etc, and my ViewModels use it to verify that business rules are being followed

Edit

The alternative suggestion I would have would be to bind to a string value, not a number field. This way when the value changes, you can try and cast it to your Int and return an error if it can't be cast.

public class SomeObject : IDataErrorInfo
{
    public string SomeString { get; set; }
    public Int32? SomeNumber { get; set; }

    #region IDataErrorInfo Members

    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    public string this[string columnName]
    {
        get
        {
            if (columnName == "SomeString")
            {
                int i;
                if (int.TryParse(SomeString, i))
                {
                    SomeNumber = i;
                }
                else
                {
                    SomeNumber = null;
                    return "Value is not a valid number";
                }
            }
            return null;
        }
    }

    #endregion
}

Upvotes: 4

Grant Thomas
Grant Thomas

Reputation: 45083

It gets more powerful yet. You probably ought to go the route of validation via the interface/binding itself - WPF has built-in support for this, examples of which can be found in the Data Binding Overview over at MSDN.

An example of implementing this could go as follows:

<...>
  <Binding.ValidationRules>
    <ExceptionValidationRule />
  </Binding.ValidationRules>
</...>

The linked documentation covers quite a bit on the topic of binding, so here is an excerpt from the relevant section 'Data Validation':

A ValidationRule object checks whether the value of a property is valid.

A ExceptionValidationRule checks for exceptions thrown during the update of the binding source property. In the previous example, StartPrice is of type integer. When the user enters a value that cannot be converted to an integer, an exception is thrown, causing the binding to be marked as invalid. An alternative syntax to setting the ExceptionValidationRule explicitly is to set the ValidatesOnExceptions property to true on your Binding or MultiBinding object.

Upvotes: 0

Related Questions