I'm rather new to WPF and XAML, and I'm attempting to create a custom class that extends TextBox, so that I can add some properties for easier validation setup. The validation works fine. The problem is, when I swap out the TextBox in the XAML, the new TextBox updates the border to display an error, but the tool-tip does not show up as expected (see below).
In the picture below, the top 's error tool tip displays correctly, but the 's tool-tip doesn't.
Here's the XAML...
<TextBox x:Name="StrTextBox3" Width="200" Height="50">
<TextBox.Text>
<Binding Path="BinaryIntText" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validationRules:StrValidationRule ValidatorType="{x:Static validators:StringValidator.BinaryOnly_Validator}"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<validationRules:ValidatedTextBox x:Name="VText" Width="200" Height="50">
<TextBox.Text>
<Binding Path="BinaryIntText2" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validationRules:StrValidationRule ValidatorType="{x:Static validators:StringValidator.BinaryOnly_Validator}"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</validationRules:ValidatedTextBox>
And here's the extending class...
class ValidatedTextBox : TextBox
{
public ValidatedTextBox()
{
//
}
}
And the ValidationRule for good measure...
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
return ValidatorType.Validate(Convert.ToString(value)).Match (
Right: result => new ValidationResult(true, null),
Left: error => new ValidationResult(false, error));
}
How do I go about enabling this functionality? Have I not initialized a particular field or two? Am I missing calls to something else? Am I plagued by styles, or some other straight-forward thing that is a quicker fix than doing this write up?
Thank you
Turns out I'm dense; by simply extending the TextBox, the default Style for TextBoxes was not applied, and setting the new Style to an existing TextBox's Style, or just setting in at all, fixed the issue.
Related
I'm using ValidationRules in my xaml forms like this
<TextBox Name="email">
<TextBox.Text>
<Binding Path="email" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:NotEmptyString ValidationStep="RawProposedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
In code, before my transaction begins i check for errors like this
email.GetBindingExpression(TextBox.TextProperty).UpdateSource();
if (Validation.GetHasError(item))
return false;
I have classes that inherit ValidationRule for every validation i need, and this works fine.
But now, i need to call a post method and that method returns me an JSON error when the email already exists, I want to show that error as a validation error. is there a way to set the error to the TextBox?
If you want to stick to this quite unflexible binding validation, then the simplest solution would be to use Binding.ValidatesOnException:
<TextBox Name="email">
<TextBox.Text>
<Binding Path="email"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnException="True">
<Binding.ValidationRules>
<local:NotEmptyString ValidationStep="RawProposedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Then validate your JSON response:
class ViewModel
{
public string Email { get; set; }
private ValidateJsonResponse(Json jsonObject)
{
if (!JsonIsValid(jsonObject))
{
throw new ArgumentException("Email already exists.");
}
}
}
But since exceptions can have some impact on the performance, you generally should avoid them. You can argue, if failed input validation is a good reason to throw an exception. I doubt it is.
Alternatively implement a second ValidationRule and trigger the binding to execute it. You must find a way to access the JSON response to check whether it is valid or not. Since the ValidationRule is instantiated in the XAML and can't have a DependencyProperty to allow binding, this could be slightly complicated. That's why binding validation is not very flexible. The ValidationRule instance of the binding is quite isolated from the rest of the code e.g., the view model.
The recommended pattern is to have your view model implement he INotifyDataErrorInfo interface. See this post for an example or links regarding the implementation: How to add validation to view model properties or how to implement INotifyDataErrorInfo
I have a textbox that has validation on it, in the validation checks to see if its isNullOrEmpty. The validation works but what I am having difficulty is if the user never selects the textbox and clicks save, I want the validation to run again.
I was able to accomplish this in the XAML.cs file using:
Validation.MarkInvalid(cb.GetBindingExpression(dp), validationError);
Now with MVVM I'm confused how I am able to accomplish this from the viewmodel.
Textbox in question:
<TextBox>
<TextBox.Text>
<Binding Path="LastName">
<Binding.ValidationRules>
<validationRules:IsNullOrEmptyValidationRule/>
</Binding.ValidationRules>
<Binding.UpdateSourceTrigger>PropertyChanged</Binding.UpdateSourceTrigger>
<Binding.Mode>TwoWay</Binding.Mode>
</Binding>
</TextBox.Text>
</TextBox>
Any ideas?
I have a WPF UserControl which allows the editing of an data object (VariableDataGroup). When I use the following syntax the binding works and my user control displays correctly:
<vdi:VariableDataPageView DataContext="{Binding VariableData}" Grid.Row="4" Grid.Column="1" >
Where VariableData is a property on the parent screen's ViewModel.
However, when I try and use the "<Binding>" syntax then my user control doesn't display the data.
<vdi:VariableDataPageView Grid.Row="4" Grid.Column="1" >
<Binding Path="VariableData" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<!--<Binding.ValidationRules>
<validation:VDIComittedValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>-->
</Binding>
</vdi:VariableDataPageView>
The reason I want to switch to using the syntax is to use a custom validation rule to fit in with the exiting code.
First Question: What would be the equivalent syntax to 'DataContext="{Binding VariableData}"' ?
Second Question: Is there a recommended way of performing validation in a UserControl and integrating that with the validation on the parent view? The way I think it should work is that the UserControl performs all its own validation and just passes a validated yes/no result to the parent page but the whole WPF model of programming is new to me.
Thanks,
Canice.
The equivalent syntax to DataContext="{Binding VariableData}"
<vdi:VariableDataPageView Grid.Row="4" Grid.Column="1" >
<vdi:VariableDataPageView.DataContext>
<Binding Path="VariableData" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<!--<Binding.ValidationRules>
<validation:VDIComittedValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>-->
</Binding>
</vdi:VariableDataPageView.DataContext>
</vdi:VariableDataPageView>
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.
A ComboBox.SelectedItemProperty is bound TowWay to a DependencyProperty in
The Control.
In the ControlTemplate :
<ComboBox IsEditable="True">
<ComboBox.SelectedItem>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="SomeDP" Mode="TwoWay" NotifyOnValidationError="True">
<Binding.ValidationRules>
<vld:DeleteAfterInitValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedItem>
</ComboBox>
When the ValidationRule returns false
return ValidationResult(false,msg);
The Dependency Property bound to selected item is not updated.
Is there a way to force the binding to update the source ?
*Please if any one is going to raise a discussion here about
BindingExpression.UpdateSource() , please supply a working example, and not just blurt it out because it sounds like the solution ,
i do not intend to use Explicit mode on my Binding.
Further more i can easily code my way around this but a good .net programmer should thrive to write less code and use the built in mechanisms supplied by the .net framework ,
And that is the soul purpose of this question , is there a built in way of updating the source while
notifying of a DataError ?