Reputation: 6378
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
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
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
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 theExceptionValidationRule
explicitly is to set theValidatesOnExceptions
property to true on yourBinding
orMultiBinding
object.
Upvotes: 0