WPF 4.5 empty TextBox validation on startup - wpf

I have a simple textbox with a validation rule, but a very weird behavior in a special situation.
If I use my code as this, and that my property is 0 (as it comes from db), it works as expected. The TextBox gets its red border at start.
<TextBox x:Name="TxtOfferNumber"
IsReadOnly="{Binding SelectedOffer.IsValid}"
GotKeyboardFocus="TxtOfferNumber_GotKeyboardFocus"
GotMouseCapture="TxtOfferNumber_GotMouseCapture">
<TextBox.Text>
<Binding Path="OfferNumberLookup" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validators:OfferNumberValidator ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
My validation rule:
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
return string.IsNullOrWhiteSpace(value as string) || Convert.ToInt32(value) != 0 ?
ValidationResult.ValidResult :
new ValidationResult(false, "Le numéro d'offre doit être informé!");
}
The problem is that I want this field as required, so empty is not valid.
As I want my validation rule to be:
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
return !string.IsNullOrWhiteSpace(value as string) && Convert.ToInt32(value) != 0 ?
ValidationResult.ValidResult :
new ValidationResult(false, "Le numéro d'offre doit être informé!");
}
And there is my big headache. The validation function returns correct result, but no red border. If I enter correct value and change to wrong value (empty or 0), now I get the red border.

If the problem is that the TextBox does not become validated on start-up, only when the input changes, a quick and dirty fix could be to set the Text (or binding property) to something correct and then something false during load.

Related

Change WPF Textbox Foreground Color on Event

I would like to change the foreground color of a textbox based on an incoming event (the incoming number differs from the one in the textbox) but then change it back to black if any text is changed through the UI. I have this working in a circuitous way, but I'm not sure of the correct way to do it.
The XAML:
<TextBox Style="{StaticResource recParm}" Foreground="{Binding Path=AcquisitionTimeChangedByInstrument, Converter={StaticResource BooleanToBrush}}" Name="acquisitionTxtBox" TextChanged="onAcquisitionTimeChanged" >
<TextBox.Text>
<Binding Path="AcquisitionTime" Mode="TwoWay" StringFormat="{}{0:F6}" UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<vm:AcquisitionTimeRule Min="200e-6" Max="40" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
The code behind:
private void onAcquisitionTimeChanged(object sender, TextChangedEventArgs e)
{
//acquisitionTxtBox.Foreground = Brushes.Black;
((ViewModel)Application.Current.Resources["vm"]).AcquisitionTimeChangedByInstrument = false;
}
AcquisitionTimeChangedByInstrument is a property that raises PropertyChanged on ViewModel. The converter will change the color to black for false and blue for true.
In the form above, it seems to work as described, but it seems an odd way to go about it. If I use the commented line to change the color directly, the binding seems to break. That is, the view stops checking for changes to AcquisitionTimeChangedByInstrument. Why?
What is the correct way to do this?
Please keep in mind I have only been using WPF for a few days; I don't understand advanced features yet.
EDIT (by request)
Eventually I will check to see if the value in the textbox has changed in AcquisitionTime. For now, I am simply setting AcquisitionTimeChangedByInstrument=true when a button is clicked. This will send the PropertyChanged event, but the get will only be called if I haven't previously change acquisitionTxtBox.Foreground in the callback.
[ValueConversion(typeof(bool), typeof(SolidColorBrush))]
public class BooleanToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (null == value)
{
return null;
}
if (value is bool)
{
if ((bool)value)
{
return (SolidColorBrush)Brushes.DeepSkyBlue;
}
return (SolidColorBrush)Brushes.Black;
}
Type type = value.GetType();
throw new InvalidOperationException("No type " + type.Name);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Setting a dependencyproperty locally can take precedence over other settings. Effectively, you end up overwriting the binding. Use
acquisitionTxtBox.SetCurrentValue(ForegroundProperty, Brushes.Black);
Blog entry explaining setcurrentvalue

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
}

How to declare a namespace in WPF XAML?

I am trying to use in WPF a validating input of databound controls with validation rules. In the code behind file of a wpf window I have a class:
public class posintValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string _strInt = value.ToString();
int _int = -1;
if (!Int32.TryParse(_strInt, out _int))
return new ValidationResult(false, "Value must be an integer");
if (_int < 0)
return new ValidationResult(false, "Value must be positive");
return new ValidationResult(true, null);
}
}
In XAML there is also a style error template.
When I put a textbox with validation in XAML:
<TextBox.Text>
<Binding Path="seconds" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:posintValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
I get a compile time error:
''local' is an undeclared namespace.' XML is not valid.
How I should declare local:posintValidationRule in my XAML?
At the top of your XAML file, you need to declare what your "local" namespace is; alongside the default Microsoft XAML stuff. Something like this:
xmlns:local="clr-namespace:YourApplication"
Note this assumes that "posintValidationRule" is defined at the root namespace in "YourApplication".

Changing the message (or exception) in WPF ValidatesOnException binding

I have a WPF application using MVVM.
I am using binding to a POCO object.
The Textbox is bound to a property in the object like:
<TextBox.Text>
<Binding Path="CertainProperty" Mode="TwoWay" >
<Binding.ValidationRules>
<ExceptionValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
Now this property is a a int property and when the user tried to enter a non-numeric value, they get the "input string was not in a correct format".
What I need to do is customize this message to a more user friendly one.
How can I do that ?
Unfortunately, that exception and its message originate deep within the framework code and can't simply be swapped out. But one thing you can do is circumvent the exception completely by writing your own ValidationRule. For example:
public class Int32ValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (string.IsNullOrEmpty((string)value))
return ValidationResult.ValidResult;
int number;
return int.TryParse(value.ToString(), out number)
? ValidationResult.ValidResult
: new ValidationResult(false, "Please enter a valid integer");
}
}
Of course, the nuclear option is to turn CertainProperty into a string and let your viewmodel or model validate it via IDataErrorInfo, but that would be a major change from your existing code.

Resources