Dual NHibernate Class Level Validators Issue - c#

I'm using WPF and I've got an Entity bound to a series of controls. The entity is decorated with two class level validators as follows:
[ExampleValidator1, ExampleValidator2]
public class Entity
An Entity has a series of fields of which not all are always shown, dependent on the selection from combo box.
A validator exists for each of these selections, if the "type" of entity does not match a particular validator that validator returns true and obviously the correct validator will validate the actual fields as follows:
public bool IsValid(object value, IConstraintValidatorContext constraintValidatorContext)
{
constraintValidatorContext.DisableDefaultError();
var allPropertiesValid = true;
var entity= (Entity)value;
if (supplier.ParticularEntityType)
{
return true;
}
if (String.IsNullOrEmpty(entity.Name) || entity.Name.Length > 50)
{
constraintValidatorContext.AddInvalid<Entity, string>("must be 50 or shorter and not empty", x => x.Name);
allPropertiesValid = false;
}
and the XAML is as follows:
<TextBox Grid.Row="0" Grid.Column="3">
<TextBox.Text>
<Binding Path="Entity.Name" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
</Binding>
</TextBox.Text>
</TextBox>
Obviously I get the nice pretty red box and tool tips informing users of the validation requirements.
My issue is that when the selection in the combobox is changed, the red highlighting persists (becomes a small red square when a control is hidden).
Could someone direct me the right way please!

Solved by firing an OnPropertyChanged when the combobox is altered, not an ideal solution but its workable.

Related

WPF validation error remains until control loses focus

I got a text box on which I added a validation rule in the xaml. The rule works and it detects errors, but only after the user gives the focus to some other element in the window, like another text box.
This is the defintion:
<TextBox x:Name="textBoxLongitude" Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" VerticalContentAlignment="Center">
<TextBox.Text>
<Binding Path="CustomerLongitude">
<Binding.ValidationRules>
<local:IsDoubleValidationRule MaxValue ="180"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
I've tried solving the issue by adding this to the xaml of the text box:
TextChanged="textBoxTextChanged"
And the implementation:
private void textBoxTextChanged(object sender, TextChangedEventArgs e)
{
CommandManager.InvalidateRequerySuggested();
}
It didn't help..
How can I make the validation rule detect the error and when it's fixed even without the user needing to give the focus to another control?
Set the binding's UpdateSourceTrigger to PropertyChanged to commit and re-evaluate a bound value on every change instead of LostFocus.
Bindings that are TwoWay or OneWayToSource listen for changes in the target property and propagate them back to the source. This is known as updating the source. Usually, these updates happen whenever the target property changes. This is fine for check boxes and other simple controls, but it is usually not appropriate for text fields. Updating after every keystroke can diminish performance and it denies the user the usual opportunity to backspace and fix typing errors before committing to the new value. Therefore, the default UpdateSourceTrigger value of the Text property is LostFocus and not PropertyChanged.
This is how to do it: <Binding Path="CustomerLongitude" UpdateSourceTrigger="PropertyChanged">

WPF why isn't error message shown?

I want to validate some Text in a TextBlock
TextBlock xaml:
<TextBlock x:Name="numInput" Validation.ErrorTemplate="{StaticResource errorTemplate}" >
<TextBlock.Text>
<Binding Path="Text" RelativeSource="{RelativeSource self}" NotifyOnValidationError="True">
<Binding.ValidationRules>
<local: NumberValidator />
</Binding.ValidationRules>
</Binding>
</TextBlock.Text>
</TextBlock>
The Text is added in codebehind by some button clicks in the GUI (i.e. a touch screen)
errorTemplate
<ControlTemplate x:Key="errorTemplate">
<StackPanel>
<TextBlock Foreground="Red">error msg</TextBlock>
<AdornedElementPlaceholder/>
</StackPanel>
</ControlTemplate>
NumberValidator
class NumberValidator : ValidationRule {
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) {
Console.WriteLine("validating numbers!!");
int num = -1;
try {
num = Int32.Parse(value.ToString());
}
catch (Exception e) {
return new ValidationResult(false, "input must be numbers!");
}
if (num > 999 || num < 1) {
return new ValidationResult(false, string.Format("must be integers from {0} to {1}", 1, 999));
}
return new ValidationResult(true, null);
}
}
Questions:
No error message is shown. In fact, NumberValidator isn't even called. Why?
How to validate the error only when a Button is clicked?
How to pass valid range (i.e min, max) information to the NumberValidator?
Thanks!
I assume that you want to perform validation in source-to-target direction (provide visual feedback for model errors), therefore my answer only applies if this is the case.
Validation rules are by design only checked in target-to-source direction (the main idea here is to validate user input), so when you change the value on the model, validation rules are not checked. In order to perform validation in source-to-target direction, your model should implement either IDataErrorInfo or INotifyDataErrorInfo (the latter being supported only in .NET 4.5 or newer), and ValidatesOnDataErrors should be set to true on the binding.
The validation occurs whenever binding is updated, so if the button click updates the property on the model (or, more specifically, raises PropertyChanged event), the validation will be performed. Note that if property is changed on some other occasion, the validation will also be performed, so in order to perform the validation only on button click make sure the property is changed (or PropertyChanged is raised) only then.
Despite using ValidationRule derivatives is not appropriate approach in assumed scenario, the answer is to define Max and Min properties on the NumberValidator class, and then use them in XAML like so: <local:NumberValidator Min="0" Max="100"/>.
For more information on bindings see Data Binding Overview.

Can't click textbox after validation fails

I'm working on trying to implement validation in WPF, and running into an issue where I can't click on or change the value of a textbox after validation fails.
I have a User class (which implements IDataErrorInfo), which contains the following relevant code:
public virtual string Error
{
get
{
return null;
}
}
public virtual string this[string name]
{
get
{
string result = null;
if (name == "uFirstName")
{
if (String.IsNullOrEmpty(this.uFirstName))
{
return "Must enter a first name!";
}
}
return result;
}
}
Then, over in my MainWindow code-behind, I have this code to hookup my combobox:
comboBox1.ItemsSource = Users; //Users is a collection of Users
Finally, in my MainWindow xaml, I have this:
<ComboBox Name="comboBox1" ItemTemplate="{StaticResource userTemplate}" />
<TextBox Name="textBox1" DataContext="{Binding ElementName=comboBox1,
Path=SelectedItem}" Text="{Binding Path=uFirstName, ValidatesOnDataErrors=True,
NotifyOnValidationError=True}"/>
And indeed, the validation does fire when I delete the text and the textbox gets a nice red border. However, the changes are still sent back to Users (uFirstName gets set to nothing!). Even worse though, I now cannot edit the value in that textbox unless I tab back into it.
What is needed to make sure the value isn't sent back if it isn't valid, and allow the textbox to be edited if is invalid?
The IDataErrorInfo implementation is only there to report errors. You should implement your verification in the uFirstName property. If you don't think a certain verification on a property is true for all instances, you should start looking into ValidationRule. This is a better way of implementing Validation in WPF. ValidationRule will not set value to the source, if the validation fails.
The textbox might be acting strange because of a system level style? Not exactly sure on why you are having trouble there.

Validation order for an entire form of controls

I'm learning this wpf stuff and trying to get my head around validation of controls. Specifically what I'm looking for is this...
A form can have 100 controls on it (exaggerating, but possible). The form's layout and flow are a specific order (via tabbed sequence for user). A user may never get to some "required" fields and click on a "Save" button. How can I trigger it so all the controls force triggering their own respective "Validation" events.
Based on above, does the WPF framework process the validation rules in the tab order the user is looking at. If not, how can that be controlled to match the data entry flow instead of bouncing around in the sequential order the application happens to create objects and their respective validation rules.
Is there a way to have ALL failed controls triggered for the default behavior of putting a red border box around the failed control instead of only one at a time.
Thanks
Typically, to accomplish what you are looking for you use an MVVM type pattern. This means that you bind each control that collects data in your WPF form to a backing field or property. You add validation to the binding, with a style that will cause the red border box. For controls with required data, part of the validation is that they are filled in. You could define a single validation rule for this called "ValidWhenHasData" or some such.
To cause the validations to trigger only when you press "save" or the like, there are a number of ways you can do this. I typically make a property in each validation rule called "IsEnabled" and set it to false by default; if set to false, the validation rule always returns valid. I then add a list in the code-behind of the controls that I want to validate. When "save" is clicked, I go through the list and set all the validation rules' IsEnabled to true, clear all errors on the controls in the list, and then refresh the binding on each. This will display the red rectangles on any that are not filled in or whatever else you have defined as an error condition. You can also use this list to set focus to the first control that failed validation, in the order you choose.
Example validation control template, which includes placeholder for validation error tooltip:
<ControlTemplate x:Key="errorTemplate">
<Canvas Width="{Binding Path=AdornedElement.ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}" Height="{Binding Path=AdornedElement.ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder/>
</Border>
<Border Canvas.Top="-5" Canvas.Right="-5" BorderBrush="Gray" BorderThickness="1" >
<TextBlock x:Name="errorBlock" TextAlignment="Center" Background="Red" Foreground="White" Width="10" Height="10" FontSize="9" ctl:valTooltip.MessageBody="{Binding Path=AdornedElement.(Validation.Errors)[0].ErrorContent,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}">*</TextBlock>
</Border>
</Canvas>
</ControlTemplate>
Example validation binding:
<TextBox x:Name="TBNumItems" Margin="2,2,2,2" MinWidth="40" HorizontalAlignment="Left" Validation.ErrorTemplate="{StaticResource errorTemplate}">
<TextBox.Text>
<Binding x:Name="NumItemsBinding" Path="NumItems" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<cal:UIntValidationRule x:Name="NumItemsValidationRule" MinValue="1" MaxValue="99999" IsEnabled="False"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Example code behind for validation:
/// <summary>
/// Clears all validation errors
/// </summary>
void ClearAllValidationErrors()
{
Validation.ClearInvalid(TBNumItems.GetBindingExpression(TextBox.TextProperty));
}
/// <summary>
/// Revalidates everything
/// </summary>
void RevalidateAll()
{
ClearAllValidationErrors();
TBNumItems.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
Make your data object implement IDataErrorInfo, which will perform a validation check on a property when the user changes it, then use the following style to apply the red border to controls that have a validation error:
<!-- ValidatingControl Style -->
<Style TargetType="{x:Type FrameworkElement}" x:Key="ValidatingControl">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding
Path=(Validation.Errors)[0].ErrorContent,
RelativeSource={x:Static RelativeSource.Self}}" />
</Trigger>
</Style.Triggers>
</Style>
This will
Only perform a validation check for a single property when that property gets changed
Only (and always) show the red validation error border on controls that are bound to an invalid property
Edit
Here's a sample of a how I would implement validation on an object:
public class MyObject : ValidatingObject
{
public MyObject()
{
// Add Properties to Validate here
this.ValidatedProperties.Add("SomeNumber");
}
// Implement validation rules here
public override string GetValidationError(string propertyName)
{
if (ValidatedProperties.IndexOf(propertyName) < 0)
{
return null;
}
string s = null;
switch (propertyName)
{
case "SomeNumber":
if (SomeNumber <= 0)
s = "SomeNumber must be greater than 0";
break;
}
return s;
}
}
And my ValidatingObject base class which implements IDataErrorInfo usually contains the following:
#region IDataErrorInfo & Validation Members
/// <summary>
/// List of Property Names that should be validated
/// </summary>
protected List<string> ValidatedProperties = new List<string>();
public abstract string GetValidationError(string propertyName);
string IDataErrorInfo.Error { get { return null; } }
string IDataErrorInfo.this[string propertyName]
{
get { return this.GetValidationError(propertyName); }
}
public bool IsValid
{
get
{
return (GetValidationError() == null);
}
}
public string GetValidationError()
{
string error = null;
if (ValidatedProperties != null)
{
foreach (string s in ValidatedProperties)
{
error = GetValidationError(s);
if (error != null)
{
return error;
}
}
}
return error;
}
#endregion // IDataErrorInfo & Validation Members
I faced the same problem. I wanted controls who know if they are required and report automatically any change to the hosting Window. I didn't want to have to write complicated XAML or other code, just placing the control and setting a property to indicate if user input is required. The control searches then automatically the host window and informs it when the user keys in required data or deletes it, so the window can change the state of the Save button. The final solution looks like this:
<wwl:CheckedWindow x:Class="Samples.SampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wwl="clr-namespace:WpfWindowsLib;assembly=WpfWindowsLib">
<StackPanel>
<wwl:CheckedTextBox x:Name="TestCheckedTextBox" MinWidth="100" MaxLength="20" IsRequired="True"/>
<Button x:Name="SaveButton" Content="_Save"/>
</StackPanel>
</wwl:CheckedWindow>
For this to work added to the most common WPF controls an IChecker, which does the following:
know when the data has changed
know when the data has been unchanged (user undid his change)
know when a "required" control is lacking data
know when a "required" control has data
find automatically the window the control is in and inform the Window about each state change
If the window gets informed by any control that the control's state has changed, the window then queries automatically all other controls about their state. If all required controls have data, the Save Button gets enabled.
Knowing that the user has changed some data it is useful, when the user tries to close the window without saving . He gets then automatically a warning and gives him the choice if he wants to save or discard the data.
I had to write too much code to be posted here, but now life is very easy. Just place the control into the XAML and add few lines of code to the Window for the Save Button, that's all. Here is an detailed explanation: https://www.codeproject.com/Articles/5257393/Base-WPF-Window-functionality-for-data-entry
The code is on GitHub: https://github.com/PeterHuberSg/WpfWindowsLib

How can I get my validation code to execute on an item with no binding?

The Problem:
I am creating user control that handles data conversions (via a converter/validation rule). This works 100% as desired, but the validation only fires when the control is bound to something, which is not always the case.
Is there a way to force validation even if the control is not bound? OR is there a way to set up basically a dummy binding. (The solution needs to be done in code so that the end result is a drag and drop user control with no xaml customization needed by the programmer.
Thanks in advance for any advice.
EDIT : Really the code in question is this:
Binding TextBinding = BindingOperations.GetBinding(this, TextBox.TextProperty);
TextBinding.ValidationRules.Add(MyValidationRule);
This is how I am assigning my validation rule, but it will only work if the TextBinding is not null. So I either need a dummy binding for my TextBox, or another way to add the validation rule.
Sounds to me like what you want to be doing is defining a dependency property on your user control which the XAML in your user control binds to. This binding would incorporate your validation rule. Consumers of your user control would then bind the property on your user control to whatever they want.
It's really hard to be more specific than that without your exact use case, but consider this:
CoolUserControl.xaml.cs:
public class CoolUserControl : UserControl
{
public static readonly DependencyProperty CoolProperty = ...;
public string Cool
{
// get / set
}
}
CoolUserControl.xaml:
<UserControl x:Name="root" ...>
<TextBox>
<TextBox.Text>
<Binding Path="Cool" ElementName="root">
<Binding.ValidationRules>
<!-- your rule here -->
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</UserControl>
SomeConsumer.xaml:
<local:CoolUserControl Cool="{Binding SomePropertyOnMyViewModel}"/>

Categories