WPF Validation for the whole form - wpf

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.

Related

validation not fired unless you type

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.

Validation between multiple fields in different levels

I have a problem with validations between multiple fields. For example, I have a ViewModel named RangeDateViewModel that contains 2 instances of a class named DateViewModel - they represent a start date and an end date respectively.
So my binding looks like this:
<TextBox Text="{Binding StartDate.Date, ValidateOnDataError=True}">
<TextBox Text="{Binding EndDate.Date, ValidateOnDataError=True}">
My RangeDateViewModel class implements the IDataErrorInfo interface.
In my plan, the RangeDateViewModel would validate that the start date is before the end date, by applying the validation logic in the IDataErrorInfo["propertyName"] function like this:
public string this[string columnName]
{
get
{
return ValidationError();
}
}
The problem is that this is never being called, and instead the IDataErrorInfo properties that reside in each of the DateViewModel classes are being called instead.
I guess this is because the bound property is not in the same level of RangeDateViewModel, but instead inside the child DateViewModel.
I think my need is quite basic and there must be an easy solution for this problem.
I tried using ValidationRules instead of IDataErrorInfo but then I'd problems letting the ViewModel know of the current validation status from the ValidationRules.
Try using the following approach:
Create a DataTemplate for DateViewModel:
<DataTemplate DataType="{x:Type ViewModels:DateViewModel}">
<TextBox Text="{Binding Date}">
</DataTemplate>
Bind the instances of this ViewModel to a ContentControl and set ValidateOnDataError to true on that binding:
<ContentControl Content="{Binding StartDate, ValidateOnDataError=True}" />
<ContentControl Content="{Binding EndDate, ValidateOnDataError=True}" />
In RangeDateViewModel subscribe to the PropertyChanged event of StartDate and EndDate and when raised, raise a PropertyChanged event with StartDate / EndDate:
StartDate.PropertyChanged += (s, e) => InvokePropertyChanged("StartDate");
EndDate.PropertyChanged += (s, e) => InvokePropertyChanged("EndDate");
I had the problem that public string this[string columnName] was simply not called just the other week.
The solution was simple.
The binding WPF binding engine could not follow the nesting of my ViewModels.
I had assumed that I needed to implement the property in the ViewModel that is the current DataContext, but instead it needs to be implemented in the ViewModel that is bound to the control.
Example:
<TextBox Text="{Binding Path=ProductViewModel.DescriptionViewModel.ProductName,
Mode=TwoWay,
ValidatesOnDataErrors=True,
NotifyOnValidationError=True}" />
Here DescriptionViewModel is the class that contains the bound property. IDataErrorInfo needs to be implemented in that class (not in ProductViewModel or another class up the hierarchy that may contain it) then everything will work fine.

Access Datacontext And A Property In Code Behind At The Same Time

I am using MVVM/WPF and trying to do something seemingly simple, but cant find a clean solution.
I want to do the following:
When a property changes in the model (WPF Textbox text would be changed in this case), use a method to perform other operations on the UI relating to the property bound.
Currently i am using a multibinding on the tooltip (to get the textbox datacontext + binding path), but this is a bit of a hack.
<TextBox x:Name="textBox" Text="{Binding Model.MyProperty}">
<TextBox.ToolTip>
<MultiBinding Converter="{StaticResource brNewMultiConverter}">
<!-- This to trigger the converter in all required cases.
Without it, i cant get the event to fire when filling
the model initially
-->
<Binding ElementName="textBox" Path="Text" />
<!-- This has the properties i need, but wont fire without
the binding above -->
<Binding ElementName="textBox" />
</MultiBinding>
</TextBox.ToolTip>
</TextBox>
I would like to make something re-usable and maybe for different controls, hence i am not just using the textchanged event.
If anyone could point me in the right direction, it would be much appreciated.
OK, so far as your Multibinding there, what are you trying to accomplish there? I don't know what your converter is supposed to do, but can it not be done with an IValueConverter implementing class? I am assuming not, it looks like you are passing the textbox to the converter.
As far as having a method do several things when your model properties get updated, you can have the viewmodel subscribe to events on your model class. Just declare the object WithEvents (VB.NET) and add event handlers for On[PropertyName]Changed.
When implementing MVVM, I tend to treat the codebehind as a second class citizen. I do my best to push all logic off to the ViewModel or View if I can. I have almost completely stopped using Converters as much of that logic can be duplicated in ViewModels, and if it is something that I want to re-use, I usually just have a little helper class that gets whatever passed to it, does something, and passes it back out. I have never really had that great a relationship with IValueConverter...
Other than that, it is unclear exactly what you are trying to do. Could we get some more clarification?
It looks like you're trying to have the tooltip have the content of the textbox, if so why not just do this?
<TextBox Text="{Binding Model.MyProperty}" ToolTip="{Binding Model.MyProperty}"/>
If that's not what you want, but want the tooltip to change based on the value of the textbox then do that in your viewmodel e.g.
public class MyViewModel
{
string _MyProperty;
public string MyProperty
{
get { return _MyProperty;}
set
{
_MyProperty = value;
OnPropertyChanged("MyProperty");
OnPropertyChanged("MyToolTipProperty"); //force WPF to get the value of MyToolTipProperty
}
}
public string MyToolTipProperty
{
get
{
//return what you want
}
}
}
and then in your markup:
<TextBox Text="{Binding Model.MyProperty}" ToolTip="{Binding Model.MyToolTipProperty}"/>

EF EntityObject not updating databindings of relationships

I'm using EntityFramework, WPF and MVVM in my application and got some problems with updating the databinding of relationships between EntityObjects. I was able to downsize my problem to only a few lines of XAML and I hope someone can help me as I'm still not very confident with EF and MVVM.
Anyway, here we go with the simplified XAML:
<DatePicker Grid.Row="2" Grid.Column="1"
SelectedDate="{Binding Path=File.SentDate,
StringFormat={}{0:dd/MM/yyyy}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center" IsEnabled="{Binding Path=IsEnabled}"/>
<ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding Contacts}" DisplayMemberPath="Name"
SelectedItem="{Binding Path=File.Sender, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"
VerticalAlignment="Center">
</ComboBox>
<Label Content="{Binding Path=File.SenderId}" Grid.Row="4"/>
<Label Content="{Binding Path=File.Sender.Name}" Grid.Row="5"/>
<Label Content="{Binding Path=File.SentDate}" Grid.Row="6"/>
I'm using the last 3 Labels to test my databinding. Changing the File.SentDate using the DatePicker updates the databinding to the last Label without problem.
Now File is of type EntityObject and has a SenderId property of type GUID. It also has a relationship to my Contacts through the Sender property. Obvisouly, SenderId is the GUID of the corresponding Contact EntityObject which is related to File through the Sender relationship. A File can have only 1 single Sender of type Contact.
Anyway, what happens is that when I select another sender using the combobox, the Label displaying the File.SenderId property get properly updated. However, the one with the File.Sender.Name property i.e. the one using the reléationship does not get updated.
So I'm guessing that there is something special about updating the databinding of relationships in EF.
Can someone please suggest a solution to this?
Unfortunately, the Entity Framework doesn’t notify when an association property changes. That’s the reason why your Binding didn’t work.
The issue is reported to Microsoft: http://connect.microsoft.com/VisualStudio/feedback/details/532257/entity-framework-navigation-properties-don-t-raise-the-propertychanged-event
Another workaround is shown by the BookLibrary sample application of the WPF Application Framework (WAF). The Book class listens to the AssociationChanged event and raises the appropriate PropertyChanged event.
public Book()
{
…
LendToReference.AssociationChanged += LendToReferenceAssociationChanged;
}
private void LendToReferenceAssociationChanged(object sender,
CollectionChangeEventArgs e)
{
// The navigation property LendTo doesn't support the PropertyChanged event.
// We have to raise it ourselves.
OnPropertyChanged("LendTo");
}
Looks like I've found a solution, though to me its more like a workaround. It's not the solution I
would have expected but it works.
The XAML is still the same as above, except for one thing. Instead of binding to File.Sender.Name, I bind to File.SenderName like this:
<Label Content="{Binding Path=File.SenderName}" Grid.Row="4"/>
SenderName in this case is a property of the object File which I added in a partial class like this:
public partial class File
{
public string SenderName
{
get
{
if (this.Sender != null)
{
return this.Sender.Name;
}
return string.Empty;
}
}
protected override void OnPropertyChanged(string property)
{
if (property == "SenderId")
{
OnPropertyChanged("SenderName");
}
base.OnPropertyChanged(property);
}
}
So what happens here is that if the SenderId property is changed, I tell the framework to also update the SenderName property. That's it. Works like a charm. Although I'm still not convinced that this is the way it is supposed to work.
Another workaround if you simply want a name is to overide ToString() for the Sender and bind directly to sender. This workaround is good because most of the time when we are databinding to Property of a Property we do it in order to get a "name" of object set as property value. Also this method works for Database First approach too if you edit tt files to add partial to all class definitions.
So you add a file to contain ToString extensions of your Entites and in it you add something like this:
public partial Contacts
{
public override string ToString()
{
return Name;
}
}
so you can databind
<Label Content="{Binding Path=File.Sender}" Grid.Row="5"/>
Now the databinding will detect if the Sender changes, and when it does it will call ToString to determine what to display.
On the other hand if you need to bind to another non standard property you might have problems. I do remember having success with using DataContext and templates to get around it. You bind to Sender and use DataTemplate to determine what to display.

Select the Initial Text in a Silverlight TextBox

I am trying to figure out the best way to select all the text in a TextBox the first time the control is loaded. I am using the MVVM pattern, so I am using two-way binding for the Text property of the TextBox to a string on my ViewModel. I am using this TextBox to "rename" something that already has a name, so I would like to select the old name when the control loads so it can easily be deleted and renamed. The initial text (old name) is populated by setting it in my ViewModel, and it is then reflected in the TextBox after the data binding completes.
What I would really like to do is something like this:
<TextBox x:Name="NameTextBox" Text="{Binding NameViewModelProperty, Mode=TwoWay}" SelectedText="{Binding NameViewModelProperty, Mode=OneTime}" />
Basically just use the entire text as the SelectedText with OneTime binding. However, that does not work since the SelectedText is not a DependencyProperty.
I am not completely against adding the selection code in the code-behind of my view, but my problem in that case is determining when the initial text binding has completed. The TextBox always starts empty, so it can not be done in the constructor. The TextChanged event only seems to fire when a user enters new text, not when the text is changed from the initial binding of the ViewModel.
Any ideas are greatly appreciated!
Dan,
I wrote a very simple derived class, TextBoxEx, that offers this functionality. The TextBoxEx class derives from TextBox, and can be referenced in XAML for any and all of your TextBox’s. There are no methods to call. It just listens for Focus events and selects it own text. Very simple.
Usage is as follows:
In XAML, reference the assembly where you implement the TextBoxEx class listed below, and add as many TextBoxEx elements as you need. The example below uses data binding to display a username.
<UserControl x:Class="MyApp.MainPage"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:ClassLibrary;assembly=ClassLibrary"
>
.
.
.
<c:TextBoxEx x:Name="NameTextBox" Text="{Binding NameViewModelProperty, Mode=TwoWay}" Width="120" />
This code below works with Silverlight 3.
using System.Windows;
using System.Windows.Controls;
namespace ClassLibrary
{
// This TextBox derived class selects all text when it receives focus
public class TextBoxEx : TextBox
{
public TextBoxEx()
{
base.GotFocus += OnGotFocus;
}
private void OnGotFocus(object sender, RoutedEventArgs e)
{
base.SelectAll();
}
}
}
Good luck.
I'm leaving Jim's solution as the answer, since calling SelectAll() on the GotFocus event of the TextBox did the trick.
I actually ended up making a Blend TriggerAction and an EventTrigger to do this instead of subclassing the TextBox or doing it in code-behind. It was really simple to do and nice to be able to keep the behavior logic encapsulated and just add it declaratively in XAML to an existing TextBox.
Just posting this in case anyone else comes across this thread and is interested:
XAML:
<TextBox x:Name="NameTextBox" Text="{Binding NameViewModelProperty, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<local:SelectAllAction/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
C#
public class SelectAllAction : TriggerAction<TextBox>
{
protected override void Invoke(object parameter)
{
if (this.AssociatedObject != null)
{
this.AssociatedObject.SelectAll();
}
}
}
Just wanna add a link I found pertaining to this - here is a fantastic discussion (read comments) on Behaviours vs subclassing vvs attached properties...

Resources