validation not fired unless you type - wpf

I have three textbox in a WPF window with UpdateSourceTrigger="LostFocus".
I have also a validation class (:ValidationRule) that return false or true based on my condition, and to keep it so simple: the condition is to check if the string is empty or not.
<TextBox x:Name="TestBox">
<TextBox.Text>
<Binding ElementName="This" Path="test"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<local:IPv4ValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Margin="2" Foreground="Red" FontWeight="Bold"
Text="{Binding ElementName=TestBox,
Path=(Validation.Errors),
Converter={StaticResource eToMConverter}}" />
the problem is:
if you run the application, and you go through textboxs, no error will be shown on lost focus. I put a button to fire the validation in code, and no error is shown.
ONLY if you type in the textbox, and then clear it, the validation will work.
how can I solve this? because in this case, I cannot confirm if someone leave a textbox empty unless he type in and then delete.

Reading on MSDN, you can find that:
Validation usually occurs when the value of a target is transferred to
the binding source property.
So your validation rule will not be evaluated without typing a key, unless you update the source.
You can do it in you code behind. Supposing that MainWindow is your window, you need to add a Loaded event handler:
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
BindingExpression bindingExpression = TestBox.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
}
As you can see, the handler's code updates the source, therefore the ValidationRule is evaluated.

Related

Validation is not getting fired when setting updatesourcetrigger to lost focus

I am using below code to validate textbox.
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<Validate:RquiredFiledValidation ErrorMessage="Please Provide Login Name"></Validate:RquiredFiledValidation>
</Binding.ValidationRules>
</Binding>
Above code is working fine but when changing updatesourcetrigger to Lostfocus it stop working.
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="LostFocus" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<Validate:RquiredFiledValidation ErrorMessage="Please Provide Login Name"></Validate:RquiredFiledValidation>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
Any help would be appreciated.
The spelling of Field is wrong:
<Validate:RquiredFieldValidation ErrorMessage="Please Provide Login Name"></Validate:RquiredFieldValidation>
UPDATE:
From the UpdateSourceTrigger Enumeration page on MSDN:
PropertyChanged: Updates the binding source immediately whenever the binding target property changes, which means that the source value will be updated each time the bound property changes.
whereas
LostFocus: Updates the binding source whenever the binding target element loses focus i.e., the source value will be updated each time the destination control loses focus. If you don’t make any property change on target, source will stay the same.
As Jim Zhou says here, we could invoke the BindingExpression.UpdateSource method to force the data source to reset. So in this case, we can hook up LostFocus event on the TextBox and invoke the
BindingExpression.UpdateSource method explicitly. Something like this:
private void OnLostFocus(object sender, RoutedEventArgs e)
{
TextBox text = sender as TextBox;
BindingOperations.GetBindingExpression(text, TextBox.TextProperty).UpdateSource();
}
Also have a look at ValidatesOnTargetUpdated property of ValidationRule. It will validate when the data is first loaded. This is good if you're trying to catch empty or null fields. Than you can add something like this in your Binding Validation rule:
<DataErrorValidationRule ValidatesOnTargetUpdated="True" />
Not much related but you may like dkozl's answer.
Hope that helps.:)

ValidationRule Red Border Issue

In my WPF Application, I have created ValidationRules for my TextBoxes so that it will not allow an empty string which works fine and shows a red border with text telling the user it can not be empty. When the application launches, all the fields are blank waiting for input but I still see the red border around them. Is this normal behavior? Note: I would prefer it firing after either a propertychange event or lostfocus event fires when the user using the form not when the form initially loads.
Example of the validation I am doing:
<TextBox x:Name="itemNum" HorizontalAlignment="Left" Height="23" Margin="82,58,0,0" VerticalAlignment="Top" Width="90"
HorizontalContentAlignment="Left" VerticalContentAlignment="Center" PreviewKeyDown="ItemNum_PreviewKeyDown"
PreviewTextInput="ItemNum_PreviewTextInput" TabIndex="0" Validation.ErrorTemplate="{StaticResource validationErrorTemplate}">
<TextBox.Text>
<Binding Path="rxID" Mode="TwoWay" StringFormat="{}{0:#}" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<MY:TextBoxNotEmptyValidationRule x:Name="rxIDValidation" ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
My TextBoxNotEmptyValidationRule Class:
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string str = value as string;
if (string.IsNullOrEmpty(str))
{
return new ValidationResult(false, "Value CAN NOT BE empty");
}
return ValidationResult.ValidResult;
}
According to your logic, it seems that it is normal. Lets define a bool flag and set it false or true, does not matter, than when application is run and check the flag, if flag value is initial value do not do anything. Beside this, you "if" check needs to check also focused element. If focused element is our textbox and your flag is not initial value so you can change the textblock border.
You can look at the following link :
Validation on Load
Ideally this is the normal behavior in XAML applications if you use IDataErorInfo or INotifyDataErrorInfo . you can use beginInit and EndInit to achieve your desired output.

WPF TextBox Red Border Still Displayed After ValidationRule succeeds

I have a textbox with a very simple ValidationRule:
<TextBox x:Name="textFirstName" Width="120">
<TextBox.Text>
<Binding
Path="CurrentSelectionData.Tables[cpeople].Rows[0][FirstName]"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:NonEmptyStringValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
public class NonEmptyStringValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
return new ValidationResult(false, "Must provide a value.");
return ValidationResult.ValidResult;
}
}
The problem is that the red validation error border displays on start-up even though the textbox is bound to non-empty data.
Watching a breakpoint on the validation rule, I see that it is called once for an empty string (before the binding is changed to valid data) and once again after the binding updates to valid data. Sure enough, the second call returns ValidResult, yet the red border remains.
Manually clearing the textbox and typing new text into it clears the red border, but simply typing new text into it without first clearing it does not.
The one potential trickiness I can see, from reading other folks' questions, is that this TextBox is in a tab control. However, my problem is the opposite of those other folks (they weren't getting a red border despite a failed validation), nor am I moving to a different tab control at any point (which was the cause of the other issues).
Any ideas what I'm missing here?
It turns out that changing validated bound data during a Window's Loaded event caused the problem. In my case, the problem was solved by performing the data change during Initialized or ContentRendered instead. Initialized has the advantage of happening before the first (invalid) binding, thus avoiding a temporary red border to flash up during app load.
I tried to replicate your issue but it seems to work ok in my tests, so the problem must be with the data you are binding to as you said the validationRule is working fine.
Is the Table your binding to TwoWay?
My Test:
xaml:
<TextBox x:Name="textFirstName" Width="120" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}">
<TextBox.Text>
<Binding ElementName="UI" Path="TextTest" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" >
<Binding.ValidationRules>
<local:NonEmptyStringValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private string _textTest;
public string TextTest
{
get { return _textTest ; }
set { _textTest = value; }
}
}
public class NonEmptyStringValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
return (value is string && !string.IsNullOrEmpty(value.ToString()))
? new ValidationResult(true, null)
: new ValidationResult(false, "Invalid Text");
}
}

WPF/XAML: ExceptionValidationRule different when applying in code vs markup?

I've run across the need to apply the ExceptionValidationRule to many textboxes on a form in WPF. I can do this with markup and I get the desired result (the textbox gets a red outline when an invalid value is entered) but only when I supply the rule in markup:
<TextBox x:Name="Name" Width="150" >
<TextBox.Text>
<Binding Path="Name" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
But when I apply the value using code:
Name.GetBindingExpression(TextBox.TextProperty).ParentBinding.ValidationRules.Add(new ExceptionValidationRule());
I don't get the desired results. This code is applied in a userControl's constructor after the InitalizeComponent() call. The user control has the textbox "Name" as a child control.
I've gone through and I can see, when using both, that two validation rules are put in the ValidationRules collection but when I am using just the code version I don't get the desired result of a red outline around the textbox when an invalid value is entered.
Am I just misunderstanding a fundamental rule to WPF?
Or, is there a way I can apply this validation rule using a Style? I'd prefer that, to tell you the truth.
Thanks,
M
You can't change a Binding after it has been used, and apperently that goes for the ValidationRules as well. You can create a new Binding in code but that's probably not what you're after.
Binding binding = new Binding("Name");
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
binding.NotifyOnValidationError = true;
binding.ValidationRules.Add(new ExceptionValidationRule());
nameTextBox.SetBinding(TextBox.TextProperty, binding);
A Style won't work either since a Binding or ValidationRule doesn't derive from FrameworkElement. What I would do in your situation is a subclassed Binding where you add all the things you need. Something like this
<TextBox x:Name="Name" Width="150" >
<TextBox.Text>
<local:ExBinding Path="Name"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
The ExBinding, adding ValidationRule etc.
public class ExBinding : Binding
{
public ExBinding()
: base()
{
NotifyOnValidationError = true;
ValidationRules.Add(new ExceptionValidationRule());
}
}

WPF Validation for the whole form

I have been seriously disappointed with WPF validation system. Anyway! How can I validate the complete form by clicking the "button"?
For some reason everything in WPF is soo complicated! I can do the validation in 1 line of code in ASP.NET which requires like 10-20 lines of code in WPF!!
I can do this using my own ValidationEngine framework:
Customer customer = new Customer();
customer.FirstName = "John";
customer.LastName = String.Empty;
ValidationEngine.Validate(customer);
if (customer.BrokenRules.Count > 0)
{
// do something display the broken rules!
}
A WPF application should disable the button to submit a form iff the entered data is not valid. You can achieve this by implementing the IDataErrorInfo interface on your business object, using Bindings with ValidatesOnDataErrors=true. For customizing the look of individual controls in the case of errors, set a Validation.ErrorTemplate.
XAML:
<Window x:Class="Example.CustomerWindow" ...>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Save"
CanExecute="SaveCanExecute"
Executed="SaveExecuted" />
</Window.CommandBindings>
<StackPanel>
<TextBox Text="{Binding FirstName, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding LastName, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="ApplicationCommands.Save" IsDefault="True">Save</Button>
<TextBlock Text="{Binding Error}"/>
</StackPanel>
</Window>
This creates a Window with two TextBoxes where you can edit the first and last name of a customer. The "Save" button is only enabled if no validation errors have occurred. The TextBlock beneath the button shows the current errors, so the user knows what's up.
The default ErrorTemplate is a thin red border around the erroneous Control. If that doesn't fit into you visual concept, look at Validation in Windows Presentation Foundation article on CodeProject for an in-depth look into what can be done about that.
To get the window to actually work, there has to be a bit infrastructure in the Window and the Customer.
Code Behind
// The CustomerWindow class receives the Customer to display
// and manages the Save command
public class CustomerWindow : Window
{
private Customer CurrentCustomer;
public CustomerWindow(Customer c)
{
// store the customer for the bindings
DataContext = CurrentCustomer = c;
InitializeComponent();
}
private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ValidationEngine.Validate(CurrentCustomer);
}
private void SaveExecuted(object sender, ExecutedRoutedEventArgs e)
{
CurrentCustomer.Save();
}
}
public class Customer : IDataErrorInfo, INotifyPropertyChanged
{
// holds the actual value of FirstName
private string FirstNameBackingStore;
// the accessor for FirstName. Only accepts valid values.
public string FirstName {
get { return FirstNameBackingStore; }
set {
FirstNameBackingStore = value;
ValidationEngine.Validate(this);
OnPropertyChanged("FirstName");
}
}
// similar for LastName
string IDataErrorInfo.Error {
get { return String.Join("\n", BrokenRules.Values); }
}
string IDataErrorInfo.this[string columnName]
{
get { return BrokenRules[columnName]; }
}
}
An obvious improvement would be to move the IDataErrorInfo implementation up the class hierarchy, since it only depends on the ValidationEngine, but not the business object.
While this is indeed more code than the simple example you provided, it also has quite a bit more of functionality than only checking for validity. This gives you fine grained, and automatically updated indications to the user about validation problems and automatically disables the "Save" button as long as the user tries to enter invalid data.
I would suggest to look at the IDataErrorInfo interface on your business object. Also have a look at this article: Self Validating Text Box
You might be interested in the BookLibrary sample application of the WPF Application Framework (WAF). It shows how to use validation in WPF and how to control the Save button when validation errors exists.
ValidatesOnDataError is used to validate business rules against your view models, and it will validate only if the binding succeeds.
ValidatesOnExceptions needs to be applied along with ValidatesOnDataError to handle those scenarios where wpf cannot perform binding because of data type mismatch, lets say you want to bind a TextBox to the Age (integer) property in your view model
<TextBox Text="{Binding Age, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
If the user enters invalid entry by typing alphabets rather than numbers as age, say xyz, the wpf databinding will silently ignores the value as it cannot bind xyz to Age, and the binding error will be lost unless the binding is decorated with ValidatesOnExceptions
<TextBox Text="{Binding Age, ValidatesOnDataErrors=true, ValidatesOnExceptions="True", UpdateSourceTrigger=PropertyChanged}" />
ValidatesOnException uses default exception handling for binding errors using ExceptionValidationRule, the above syntax is a short form for the following
<TextBox>
<TextBox.Text>
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
You can defined your own rules to validate against the user input by deriving from ValidationRule and implementing Validate method, NumericRule in the following example
<TextBox.Text>
<Binding Path="Age" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<rules:NumericRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
The validation rules should be generic and not tied to business, as the later is accomplished through IDataErrorInfo and ValidatesOnDataError.
The above syntax is quite messy compared to the one line binding syntax we have, by implementing the ValidationRule as an attached property the syntax can be improved and you can take a look at it here
The description of your problem is a little vague to me. I mean, I'm not exactly sure what your difficulty is.
Assuming that the DataContext is some sort of presenter or controller that has a propetry representing the customer instance, and ValidateCommand is a property of type ICommand:
<StackPanel>
<TextBox Text="{Binding CurrentCustomer.FirstName}" />
<TextBox Text="{Binding CurrentCustomer.LastName}" />
<Button Content="Validate"
Command="{Binding ValidateCommand}"
CommandParameter="{Binding CurrentCustomer}" />
<ItemsControl ItemsSource="{Binding CurrentCustomer.BrokenRules}" />
</StackPanel>
This XAML is really simplified, of course, and there are other ways to do it.
As a Web developer who is now heavily involved with WPF, I find most tasks like this significantly easier in WPF.

Resources