WPF - MVVM - NHibernate Validation - wpf

Im facing a bit of an issue when trying to validate a decimal property on domain object which is bound to a textbox on the view through the viewmodel.
I am using NHibernate to decorate my property on the domain object as below.
private decimal _refurbishmentFee;
[Min(Value=0), NotNull]
public decimal RefurbishmentFee
{
get { return _refurbishmentFee; }
set
{
if (_refurbishmentFee == value) return;
_refurbishmentFee = value;
OnPropertyChanged("RefurbishmentFee");
}
}
The validation works fine if I put in a number less than 0, however if I put in alpha characters or a space then nothing happens, the application doesn't even fall into the setter.
Any help?
Thanks
Faisal

Yes I am using ValidatesOnDataErrors in the XAML. Fortunately one of my colleagues resolved this issue last night so I thought I should put it on here in case someone else is in the same position.
To get the validation to occur in all cases you need to add ValidatesOnExceptions="True" as well as ValidatesOnDataErrors="True" in the XAML.
<Controls:NumberTextBox IsEnabled="{Binding IsEditable}" Grid.Row="1" Grid.Column="3" Name="txtRefurbishmentFee">
<TextBox.Text>
<Binding Path="Entity.RefurbishmentFee" UpdateSourceTrigger="PropertyChanged" ValidatesOnExceptions="True" ValidatesOnDataErrors="True" />
</TextBox.Text>
</Controls:NumberTextBox>
Then on the domain object use standard NHibernate validation decorators.
private decimal _refurbishmentFee;
[Min(Value=0)]
public decimal RefurbishmentFee
{
get { return _refurbishmentFee; }
set
{
if (_refurbishmentFee == value) return;
_refurbishmentFee = value;
OnPropertyChanged("RefurbishmentFee");
}
}
Thanks,
Faisal

Related

WPF Validation Errors

In my current projet, I have to deal with data validation in a WPF form. My form is in a DataTemplate in a ResourceDictionnary. I can save and load the data from my form thanks to two buttons, which serialize and deserialize the data (through two DelegateCommand).
If one field of my form is empty or invalid, the save button is disable. A field is checked everytime it changes thanks to the UpdateSourceTrigger propertie. That's why I need to know in my C# code if a field is invalid to update my save command.
Currently, I use the ExceptionValidationRule in my XAML Binding and I wonder if it's a good pratice. I can't implement a ValidationRule because I need to know in my C# code if a field is invalid, to update the save command (enable or disable the save button).
<TextBox>
<Binding Path="Contact.FirstName" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox>
In this blog, we can read :
Raising exceptions in the Setters is not a very good approach since those properties are also set by code and sometimes it is ok to temporarily leave them with error values.
I already read this post but I can't use it, my TextBox are in a DataTemplate and I can't use them in my C# code.
So, I wonder if I should change my Data Validation and don't use the ExceptionValidationRule.
Thank you blindmeis, your idea was good.
IDataErrorInfo seems to be better than the ExceptionValidationException and it works.
Here is an example which match my project :
IDataErrorInfo sample
It doesn't use the DelegateCommand but is simple enough to be modified. Your model has to implement IDataErrorInfo :
public class Contact : IDataErrorInfo
{
public string Error
{
get { throw new NotImplementedException(); }
}
public string Name { get; set; }
public string this[string property]
{
get
{
string result = null;
if (property== "Name")
{
if (string.IsNullOrEmpty(Name) || Name.Length < 3)
result = "Please enter a Name";
}
return result;
}
}
}
In the XAML code, don't forget to change the Binding :
<TextBox>
<Binding Path="Contact.Name" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" NotifyOnValidationError="True"/>
</TextBox>

ValidationRule with ValidationStep="UpdatedValue" is called with BindingExpression instead of updated value

I am getting started with using ValidationRules in my WPF application, but quite confused.
I have the following simple rule:
class RequiredRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (String.IsNullOrWhiteSpace(value as string))
{
return new ValidationResult(false, "Must not be empty");
}
else
{
return new ValidationResult(true, null);
}
}
}
Used in XAML as follows:
<TextBox>
<TextBox.Text>
<Binding Path="Identity.Name">
<Binding.ValidationRules>
<validation:RequiredRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
This mostly works as I would expect. I was surprised to see that my source property (Identity.Name) was not being set; I have an undo function that never sees the change, and there is no way to revert the value other than re-type it (not good).
Microsoft's Data Binding Overview describes the validation process near the bottom, which explains this behavior very well. Based on this, I would want to have my ValidationStep set to UpdatedValue.
<validation:RequiredRule ValidationStep="UpdatedValue"/>
This is where things get weird for me. Instead of Validate() being called with object value being the property value that was set (i.e., a string), I get a System.Windows.Data.BindingExpression! I don't see anything in Microsoft's documentation that describes this behavior.
In the debugger, I can see the source object (the DataContext of the TextBox), navigate the path to the property, and see that the value has been set. However, I don't see any good way to get to the right property within the validation rule.
Note: With ValidationStep as ConvertedProposedValue, I get the entered string (I don't have a converter in use), but it also blocks the source property update when validation fails, as expected. With CommittedValue, I get the BindingExpression instead of the string.
There are several questions in here:
Why do I get an inconsistent argument type being passed to Validate() based on the ValidationStep setting?
How can I get to the actual value from the BindingExpression?
Alternately, is there a good way to allow the user to revert the TextBox to the previous (valid) state? (As I mentioned, my own undo function never sees the change.)
I have solved the problem of extracting the value from the BindingExpression, with a minor limitation.
First, some more complete XAML:
<Window x:Class="ValidationRuleTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ValidationRuleTest"
Title="MainWindow" Height="100" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="String 1"/>
<TextBox Grid.Column="1">
<TextBox.Text>
<Binding Path="String1" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RequiredRule ValidationStep="RawProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Text="String 2" Grid.Row="1"/>
<TextBox Grid.Column="1" Grid.Row="1">
<TextBox.Text>
<Binding Path="String2" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RequiredRule ValidationStep="UpdatedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</Window>
Note that the first TextBox uses ValidationStep="RawProposedValue" (the default), while the second one uses ValidationStep="UpdatedValue", but both use the same validation rule.
A simple ViewModel (neglecting INPC and other useful stuff):
class MainWindowViewModel
{
public string String1
{ get; set; }
public string String2
{ get; set; }
}
And finally, the new RequiredRule:
class RequiredRule : ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
{
// Get and convert the value
string stringValue = GetBoundValue(value) as string;
// Specific ValidationRule implementation...
if (String.IsNullOrWhiteSpace(stringValue))
{
return new ValidationResult(false, "Must not be empty");
}
else
{
return new ValidationResult(true, null);
}
}
private object GetBoundValue(object value)
{
if (value is BindingExpression)
{
// ValidationStep was UpdatedValue or CommittedValue (Validate after setting)
// Need to pull the value out of the BindingExpression.
BindingExpression binding = (BindingExpression)value;
// Get the bound object and name of the property
object dataItem = binding.DataItem;
string propertyName = binding.ParentBinding.Path.Path;
// Extract the value of the property.
object propertyValue = dataItem.GetType().GetProperty(propertyName).GetValue(dataItem, null);
// This is what we want.
return propertyValue;
}
else
{
// ValidationStep was RawProposedValue or ConvertedProposedValue
// The argument is already what we want!
return value;
}
}
}
The GetBoundValue() method will dig out the value I care about if it gets a BindingExpression, or simply kick back the argument if it's not. The real key was finding the "Path", and then using that to get the property and its value.
The limitation: In my original question, my binding had Path="Identity.Name", as I was digging into sub-objects of my ViewModel. This will not work, as the code above expects the path to be directly to a property on the bound object. Fortunately, I have already flattened my ViewModel so this is no longer the case, but a workaround could be to set the control's datacontext to be the sub-object, first.
I'd like to give some credit to Eduardo Brites, as his answer and discussion got me back to digging on this, and did provide a piece to his puzzle. Also, while I was about to ditch the ValidationRules entirely and use IDataErrorInfo instead, I like his suggestion on using them together for different types and complexities of validation.
This is an extension to mbmcavoy's answer.
I have modified the GetBoundValue method in order to remove the limitation for binding paths. The BindingExpression conveniently has the properties ResolvedSource and ResolvedSourcePropertyName, which are visible in the Debugger but not accessible via normal code. To get them via reflection is no problem though and this solution should work with any binding path.
private object GetBoundValue(object value)
{
if (value is BindingExpression)
{
// ValidationStep was UpdatedValue or CommittedValue (validate after setting)
// Need to pull the value out of the BindingExpression.
BindingExpression binding = (BindingExpression)value;
// Get the bound object and name of the property
string resolvedPropertyName = binding.GetType().GetProperty("ResolvedSourcePropertyName", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null).ToString();
object resolvedSource = binding.GetType().GetProperty("ResolvedSource", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null);
// Extract the value of the property
object propertyValue = resolvedSource.GetType().GetProperty(resolvedPropertyName).GetValue(resolvedSource, null);
return propertyValue;
}
else
{
return value;
}
}
This is an alternative extension to mbmcavoy's and adabyron's answer.
In order to remove the limitation for binding paths, I get the property value using such method:
public static object GetPropertyValue(object obj, string propertyName)
{
foreach (String part in propertyName.Split('.'))
{
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
Now simply change
object propertyValue = dataItem.GetType().GetProperty(propertyName).GetValue(dataItem, null);
to
object propertyValue = GetPropertyValue(dataItem, propertyName);
Related post: Get property value from string using reflection in C#
In order to answer to your 2 question:
string strVal = (string)((BindingExpression)value).DataItem

Silverlight control's input validation

I want to implement two types of validation in my silverlight application. I want "business-logic" rules to be implemented in viewmodel(like end date is not earlier than start date) which i have already accomplished, and input validation somewhere on main control, where input fields are(like date is in bad format). Is there anything silverlight can "help" me with? I mean there is at least UnsetValue there for me, but is there any event associated or i have to catch all OnChanged events? Also is there a way to manually display red border around control when i want to?
Sorry, it was not obvious from my question, but i finished with the part that includes "business-logic" rules - my viewmodel indeed implements INotifyDataErrorInfo, i'm troubled with second type of validation.
Implement INotifyDataErrorInfo on your ViewModel to enable validation on View Model level.
Implement INotifyDataErrorInfo on your properties
then on your property that is binded in XAML use a friendly display name:
private DateTime? _datumP = DateTime.Now;
[Display(Name = "Date", ResourceType = typeof(CommonExpressions))]
public DateTime? DatumP
{
get
{
return _datumP;
}
set
{
if (_datumP != value)
{
_datumP = value;
RaisePropertyChanged(DatumPonudbePropertyName);
}
ValidateDate(DatumPonudbe, DatumPonudbePropertyName);
}
}
Then your method to validate dates:
public void ValidateDate(DateTime? value, string propertyName)
{
RemoveError(propertyName, CommonErrors.DatumNull_ERROR);
if (value == null)
AddError(propertyName, CommonErrors.DatumNull_ERROR, false);
}
And now for the XAML part:
<sdk:DatePicker Width="100" SelectedDate="{Binding DatumP, Mode=TwoWay,
NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True,
ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
P.S.
CommonExpressions and CommonErrors are my Resource files for multilanguage, you can use plain strings here.

How can I disable a WPF dialogue OK button in response to invalid input determined by a custom ValidationRule class?

I have a simple WPF dialogue allowing the user to enter a name. We are using a Mvvm approach without any code-behind files. I need to validate the input and only enable the OK button when the input is valid. I am currently doing the validation using a custom error template in my view and a custom implementation of the ValidationRule class.
The text box in the dialogue is defined as:
<TextBox Width="250" Height="25" Margin="5"
Validation.ErrorTemplate="{StaticResource customErrorTemplate}">
<TextBox.Text>
<Binding Path="WitnessName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ValidationRules:NameRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
The NameRule is defined as:
public class NameRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
var isValid = (value as string == null) || Regex.IsMatch(value.ToString(), #"^[\p{L} \.'\-]+$");
return new ValidationResult(isValid, "Name can contain only letters, apostrophes and hyphens.");
}
}
The OK button IsEnabled property is bound to an IsOkEnabled property on the view model, which is only true when the input is valid.
<Button Name="OnOkClick" Margin="5" IsEnabled="{Binding IsOkEnabled}">OK</Button>
The IsOkEnabled property is updated in response to a change in the WitnessName text by monitoring the WitnessName PropertyChanged event.
public string WitnessName
{
get
{
return this.witnessName;
}
set
{
this.witnessName = value;
this.NotifyOfPropertyChange(() => this.IsOkEnabled);
}
}
The problem is that with the validation operating in the view the PropertyChanged event isn't being fired on the vew model when invalid input is entered, so the IsOkEnabled property isn't being updated and the OK button remains enabled.
Is there a way of forcing the update on the IsOkEnabled property in response to a even invalid input on my current implementation?
I have looked at both Karl Shifflet's and Josh Smith's suggestions but neither uses (as far as I can tell) a view error template and I'd like to use one to provide the visual feedback.
Update: Trying this with a bound ICommand as suggested by Danny
I have tried this by creating a VM specific to the OK button, which implements ICommand. The OkButtonViewModel has a property to hold the dialogue VM (set by IoC and unity) and the CanExecute and Execute implementations refer to the relevant properties/methods on this VM.
In OkButtonViewModel:
public bool CanExecute(object parameter)
{
return this.witnessDialogue.IsValid;
}
public void Execute(object parameter)
{
this.witnessDialogue.OnOkClick();
}
How do I bind my button to this VM rather than the dialogueVM? I can do it if the OkButtonViewModel is a property on the dialogue VM but not when the dependence is this other way around, which it needs to be for the button to be able to use the implementations on the dialogue VM.
Get rid of the OnClick event handler, instead use a binding to a command.
Register the command in the VM with a CanExecute method that returns Model.IsValid.
If the validation failure is in the binding of the view to the viewmodel,
then the viewmodel can still be valid - since it didn't store the update (due to type mismatch, range check, etc).
In this case consider storing error state in VM, before throwing exception, then clearing error state if same property was successfully set.
Modified code:
XAML:
(Note that the scope of ValidationRules:NameRule instance is in TextBox, other usages will receive another instance with different field values)
<TextBox Width="250" Height="25" Margin="5"
Validation.ErrorTemplate="{StaticResource customErrorTemplate}">
<TextBox.Text>
<Binding Path="WitnessName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ValidationRules:NameRule x:Name="nameValidator" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Validation rule:
public class NameRule : ValidationRule, INotifyPropertyChanged
{
public bool HasFailed // set default of field behind to false
{
get; // change to support INotifyPropertyChanged
set; // change to support INotifyPropertyChanged
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
var isValid = (value as string == null) || Regex.IsMatch(value.ToString(), #"^[\p{L} \.'\-]+$");
HasFailed = !isValid;
return new ValidationResult(isValid, "Name can contain only letters, apostrophes and hyphens.");
}
}
Command:
public bool CanExecute(object parameter)
{
return this.witnessDialogue.IsValid && !this.witnessDialogue.nameValidator.HasFailed;
}
public void Execute(object parameter)
{
this.witnessDialogue.OnOkClick();
}
I know this is a really old question but for those that are lead here by Google like I was...
The best way (that I know of) to do this in WPF with MVVM is to use IDataErrorInfo interface on your view model where the command definition is. This lets you validate and have the validation info in your view model to work with on your CanExecute implementation. I found a good thorough description of using this at https://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/ (did not write that, just a useful link I found).
Hope that helps someone!

Applying a validation rule on a binding to display a validation result only on the UI

I am applying a validation rule to the binding on a text box. I have got the validation right from the UI perspective in that I see the error message on the tool tip and have the error template applied too(Just the usual red border).
However, the validation that I have to display is not super critical and is sufficient to just be displayed on the UI. The problem that I have with the binding is that the validation rule prevents updates on the source object once a validation rule gets violated I want the source to get updated with exactly the content of the textbox.
Is there a way to display the error template on the UI without affecting the bound source.
My code looks something like
<TextBox Name="texBox">
<TextBox.Text>
<Binding Path="ProductCode" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<jas:RegexValidationRule
RegexText="^[A-Z]{3}\.[0-9]{3}$"
ErrorMessage="Invalid product code. (Examples: ABC.123 xyz.789)"
RegexOptions="IgnoreCase"
/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
This happens because if a validation error or other type of error occurs at any time during the binding process, the process is halted.I guess you have to set the ValidationStep property to UpdatedValue
Sample:
<jas:RegexValidationRule ValidationStep="UpdatedValue"
RegexText="^[A-Z]{3}\.[0-9]{3}$"
ErrorMessage="Invalid product code. (Examples: ABC.123 xyz.789)"
RegexOptions="IgnoreCase"
/>
Please check the "Validation Process" section in Data Binding Overview.This will give you good overview of what you are tying to do
You could try looking into IDataErrorInfo instead. Then you'll get the validation in your backing class (ViewModel) so the Text in the displayed TextBox will be in sync with the backing property. In your case it will look something like this
<TextBox Name="texBox">
<TextBox.Text>
<Binding Path="ProductCode" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
In the datacontext backing class
public class YourClass : IDataErrorInfo
{
//...
#region IDataErrorInfo Members
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "ProductCode")
{
// Do your Regex Validation.
if (regexValidationFailed)
{
result = "Validation Error Text/Tooltip";
}
}
if (columnName == "SomeOtherProperty)
//...
return result;
}
}
#endregion
}

Resources