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");
}
}
Related
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.
I have a TextBox binding to a double property. Here is the xaml
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
Margin="0,0,0,15"
Name="textBox_algorthWheight"
VerticalAlignment="Center">
<TextBox.Text>
<Binding Path="AlgorithmWeight" StringFormat="N1">
<Binding.ValidationRules>
<ExceptionValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
The property Code as shown below
public double AlgorithmWeight
{
get { return _algorithmWeight; }
set
{
if (value < 1.0)
{
value = 1.0;
}
_algorithmWeight = value;
OnPropertyChanged("AlgorithmWeight");
}
}
MVVM WPF will take care of the exception when I do enter a non number value it will highlight the textbox in red colour which is good but I really want to add an error message next to the text box to notify the user the value is not acceptable, and I will use the previous value or a default value.
Thank you in advance
You should implement IDataErrorInfo. See here for a tutorial, this will show you how to create the message and style your textbox when an error is present:
Link
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.
I am having problems with Binding. Since RelativeSource needs the visual tree to travel up and find the desired Ancestor, you are only allowed to use it on an UIElement but I am trying to do a RelativeSource binding on an Non-UIElement, such as is a ValidationRule, which as you all know isnt inside the VisualTree nor its UIElement. As you can expect the binding breaks. RelativeSource couldn't be found because like i said there is no VisualTree or LogicalTree available. I need to make it work though.
Here is an example of XAML:
<StackPanel DataContext{Binding}>
<Grid>
<ContentControl Content{Binding MVPart1>
<TextBox>
<TextBox.Text>
<Binding Path="VMPart1Property1">
<Binding.ValidationRules>
<my:MyValidationRule>
<my:ValidationRule.DOC>
<my:DepObjClass DepProp={Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}/>
</my:ValidationRule.DOC>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</ContentControl>
</Grid>
</StackPanel>
So basically MyValidationRule is derivering from ValidationRule class, but thats not UIElement nor DependencyObject and therefore I had to create a class which derivates from DependencyObject called DepObjClass to be able to write down the xaml binding expression.
Here is code:
public class MyValidationRule : ValidationRule
{
public DepObjClass DOC
{
get;
set;
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string text = value as string;
if (!string.IsNullOrEmpty(text))
{
return new ValidationResult(true, string.Empty);
}
return new ValidationResult(false, "Not working blahhh");
}
}
public class DepObjClass : DependencyObject
{
public object DepProp
{
get
{
return (object)GetValue(DepPropProperty);
}
set
{
SetValue(DepPropProperty, value);
}
}
public static DependencyProperty DepPropProperty
= DependencyProperty.Register(typeof(object), typeof(DepObjClass)......);
}
Now to sum up. MyValidatonRule is not UIElement its not DependencyObject but it has a property of a type that is, hence why the xaml binding expression compiles.
When I run the application the binding itself isnt working because StackPanel couldnt be found because ValidationRule doesnt have VisualTree nor my validation rule participates in Logical or Visual Tree.
The question is how do I make such case work, how to find StackPanel from an Non-UIElement such as my ValidationRule?
I appologize for my code not comipiling but I hope you can understand what I am trying to do.
I am giving 50 points to you guys for the right answer.
You can do the following:
Create a helper component which derives from Freezable and defines a DependencyProperty for what you want to bind.
Create a ValidationRule with a property which takes an object of the helper component, similar to what you have done already.
Declare an instance of the helper component in the Resources of an object which can bind to whatever you want to bind. Freezable and its derived classes inherit the binding context (the location in the logical tree) of any control in whose Resources they are declared, so there you can create your binding.
When declaring the ValidationRule, use {StaticResource} to assign the helper component to the property in the ValidationRule. StaticResource works without a binding context, as long as the resource is declared before it is used.
The XAML would look like this:
<StackPanel>
<StackPanel.Resources>
<my:Helper x:Key="helper" ValProperty="{Binding}"/>
</StackPanel.Resources>
<Grid>
<TextBox DataContext="{Binding MVPart1}">
<TextBox.Text>
<Binding Path="VMPart1Property1">
<Binding.ValidationRules>
<my:MyValidationRule Helper="{StaticResource helper}"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</StackPanel>
I'm using a WPF ComboBox whose IsEditable value is set to true.
Basically, I have a list of items displayed in the ComboBox. The user can type in a time if he does not find a suitable time in the ComboBox.
I have attached a ValidationRule to my ComboBox.SelectedItem so that whenever the user selects a time my ValidationClass is invoked (Derived from ValidationRule). All this works fine.
Since my ComboBox is editable users can enter their own time. The validation class gets invoked everytime I type in a value int the ComboBox and the value that gets passed to that class is the value I type in. Now the problem is, if the user types in a value that is not a part of the comobbox item the validation class gets invoked with a null value and thus im not able to validate anything.
Can anyone tell me how to validate the ComboBox.Text item entered by the user?
My Validation class:
public class TimeValidateRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
TimeClass timeObj = value as TimeClass;
TimeSpan time;
if(timeObj == null)
return new ValidationResult(false, "Invalid Time");
if(timeObj.Time.Length < 5)
return new ValidationResult(false, "Invalid Time");
try
{
time = TimeSpan.Parse(timeObj.Time);
}
catch
{
return new ValidationResult(false, "Invalid Time");
}
// Get Current time (Arizona time)
if(!CheckAgainstArizonaTime(time))
return new ValidationResult(false, "Invalid Time");
return new ValidationResult(true, null);
}
}
ComboBox declaration in xaml:
<ComboBox ItemsSource="{Binding Source={StaticResource TimeSelections}}"
ItemTemplate="{StaticResource TimeListTemplate}"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
Height="30" Width="100"
Name="cbTimes"
>
<ComboBox.SelectedItem>
<Binding
Path="SelectedTime"
UpdateSourceTrigger="PropertyChanged"
>
<Binding.ValidationRules>
<validators:TimeValidateRule/>
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedItem>
</ComboBox>
Thanks,
Jithu
I know It's too late, but maybe this will help someone:
Bind to Text property of ComboBox, instead of SelectedItem
<ComboBox Name="myComboBox">
<ComboBox.Text>
<Binding Path="SelectedTime" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validators:TimeValidateRule/>
</Binding.ValidationRules>
</Binding>
</ComboBox.Text>
</ComboBox>
you need to handle the code and exceptions using event handler etc check on the net for further samples