I have a WPF dialog with a couple of textboxes on it. Textboxes are bound to my business object and have WPF validation rules attached.
The problem is that user can perfectly click 'OK' button and close the dialog, without actually entering the data into textboxes. Validation rules never fire, since user didn't even attempt entering the information into textboxes.
Is it possible to force validation checks and determine if some validation rules are broken?
I would be able to do it when user tries to close the dialog and prohibit him from doing it if any validation rules are broken.
Thank you.
In 3.5SP1 / 3.0SP2, they also added a new property to the ValidationRule base, namely, ValidatesOnTargetUpdated="True". This will call the validation as soon as the source object is bound, rather than only when the target control is updated. That may not be exactly what you want, but it's not bad to see initially all the stuff you need to fix.
Works something like this:
<TextBox.Text>
<Binding Path="Amount" StringFormat="C">
<Binding.ValidationRules>
<validation:RequiredValidationRule
ErrorMessage="The pledge amount is required."
ValidatesOnTargetUpdated="True" />
<validation:IsNumericValidationRule
ErrorMessage="The pledge amount must be numeric."
ValidationStep="ConvertedProposedValue"
ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
We have this issue in our application as well. The validation only fires when bindings update, so you have to update them by hand. We do this in the Window's Loaded event:
public void Window_Loaded(object sender, RoutedEventArgs e)
{
// we manually fire the bindings so we get the validation initially
txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
txtCode.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
This will make the error template (red outline) appear, and set the Validation.HasError property, which we have triggering the OK button to disable:
<Button x:Name="btnOK" Content="OK" IsDefault="True" Click="btnOK_Click">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="false" />
<Style.Triggers>
<!-- Require the controls to be valid in order to press OK -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=txtName, Path=(Validation.HasError)}" Value="false" />
<Condition Binding="{Binding ElementName=txtCode, Path=(Validation.HasError)}" Value="false" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="true" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Here is an alternative way that doesn't require calling "UpdateSource()" or "UpdateTarget()":
var binding = thingToValidate.GetBinding(propertyToValidate);
foreach (var rule in binding.ValidationRules)
{
var value = thingToValidate.GetValue(propertyToValidate);
var result = rule.Validate(value, CultureInfo.CurrentCulture);
if (result.IsValid)
continue;
var expr = BindingOperations.GetBindingExpression(thingToValidate, propertyToValidate);
if (expr == null)
continue;
var validationError = new ValidationError(rule, expr);
validationError.ErrorContent = result.ErrorContent;
Validation.MarkInvalid(expr, validationError);
}
Just in case anyone happens to find this old question and is looking for an answer that addresses Monstieur's comment about UI guidelines, I did the following:
Xaml
<TextBox.Text>
<Binding Path="TextValue" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RequiredFieldValidationRule>
<local:RequiredFieldValidationRule.IsRequiredField>
<local:BoolValue Value="{Binding Data.Required, Source={StaticResource proxy}}" />
</local:RequiredFieldValidationRule.IsRequiredField>
<local:RequiredFieldValidationRule.ValidationFailed>
<local:BoolValue Value="{Binding Data.HasValidationError, Mode=TwoWay, Source={StaticResource proxy}}" />
</local:RequiredFieldValidationRule.ValidationFailed>
</local:RequiredFieldValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
RequiredFieldValidationRule:
public class RequiredFieldValidationRule : ValidationRule
{
private BoolValue _isRequiredField;
public BoolValue IsRequiredField
{
get { return _isRequiredField; }
set { _isRequiredField = value; }
}
private BoolValue _validationFailed;
public BoolValue ValidationFailed
{
get { return _validationFailed; }
set { _validationFailed = value; }
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
ValidationFailed.Value = IsRequiredField.Value && (value == null || value.ToString().Length == 0);
return new ValidationResult(!ValidationFailed.Value, ValidationFailed.Value ? "This field is mandatory" : null);
}
}
In the class that the Xaml binds to
private bool _hasValidationError;
public bool HasValidationError
{
get { return _hasValidationError; }
set { _hasValidationError = value; NotifyPropertyChanged(nameof(HasValidationError)); }
}
public void InitialisationMethod() // Or could be done in a constructor
{
_hasValidationError = Required; // Required is a property indicating whether the field is mandatory or not
}
I then hide my Save button using a bound property, if any of my objects has HasValidationError = true.
Hope this is helpful to someone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With