Form Validation in WPF - wpf

I have been working with WPF and the MVVM pattern for a while now. I'm having difficulty getting validation working in a "normal" way:
1) I'm implement the IDataErrorInfo interface in my ViewModel. The XAML looks something like:
<TextBox Grid.Column="1"
Grid.Row="1"
Text="{Binding Path=ProjectKey, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" />
The problem here is that whether LostFocus and PropertyChanged triggers are used, the textbox is validated before the user ever tabs to that control. This means if I'm validating empty fields, the user will see a whole lot of red when they first open the form. Ideally the input would only be validated after the first “lost focus” or “property change”, or once the "Submit" button is clicked.
2) The other issue is validation at the end when the user clicks "Submit". There are certain things you want to validate right before submitting to the database, such as duplicates. I understand I can use UpdateSourceTrigger=Explicit and call the UpdateSource method on all controls. I'm wondering if there is an appropriate way to do this within the MVVM pattern. It seems like such code shouldn't be in the ViewModel since it's very View specific...
Any ideas would be great. I've searched a lot online but can't seem to find the right solution...

For number one your properties on the ViewModel should be initialized with a value before hand in the constructor
public double Property1 {get; set;}
public ViewModel()
{
Property1 = 0;
}
For number two the submit button should not be enabled until all fields pass validation. If you have a field that is unique in the database then validate it on property change and display and error if it doesn't pass. You can have a boolean property that is bound to the button's IsEnabled property and set it to true once all fields pass validation.

Related

Is it the right way that command parameter references a view control in MVVM pattern?

I'm making a page using MVVM pattern.
By the way, I had a question during implementing command.
It is 'Is it the right to send command parameter to UI control'?
For example, I have a TextBox control. When LostFocus event is fired, a validation command binded with TextBox will be raised and I will send an UI control as command parameter to display a validation failure message.
After a command is executed, I will change content value of UI control if validate is failed.
These are my scenario.
But I know a viewmodel should not reference any of views and its controls directly.
Please let me know how.
If your validation is evaluated in the code behind of your view/control then you are fine to send or pass a control.
I have to question what sort of validation is being done at the UI layer - it can be general input logic (like numbers only or only certain letters, etc), but it shouldn't involve any business logic. Your code behind could call a method on the viewmodel to perform business logic based validation, but it should avoid passing a control.
If you are simply looking for a way to make a generic validation method for multiple controls, then you should consider another way such as:
creating a custom usercontrol that has the validation
using an attached behavior
A view model should not have any references or any dependencies upon any UI control such as a TextBox.
Instead of passing a reference to the TextBox as a command parameter to the view model command, you should either pass the value of the Text property of the TextBox as a string argument or - even better - you should simply bind the Text property of the TextBox to a string source property of the view model:
<TextBox Text="{Binding YourViewModelProperty}" />
The source property will be set when the TextBox loses focus and you can easily kick off your validation logic in the setter:
private string _s;
public string YourViewModelProperty
{
get { return _s; }
set { _s = value; Validate(); }
}
This is how the MVVM pattern is supposed to be implemented.

Catching notifications from INotifyPropertyChanged before WPF windows close

In my current application, I have a WPF window with a large number of text fields for the user to fill in. To try and avoid loss of data, I've implemented a feature to notify users if they have unsaved changes.
The underlaying data object in the ViewModel implements INotifyPropertyChanged. So on each change I can set a boolean property on my BaseViewModel to indicate that something has changed. However, this notification only happens when the user moves off any given textbox and onto another part of the window.
The BaseViewModel also has an overridable SaveChanges method, in which the property is reset to indicate the data has been saved.
We're using MVVM, but in a nod to practicality, we do have one bit of code-behind. In the underlying view there's an assignment of a function to the Closing event. In the function, we check the boolean and give the user a messagebox to warn of unsaved changes.
This all works fine in the majority of situations. However, if someone alters text in a box and then goes straight for the close button on the WPF window, the property changed event gets fired after the window closes.
Is there any way I can catch this event and stop the window closing in this eventuality?
However, this notification only happens when the user moves off any given textbox and onto another part of the window.
You can control this using the UpdateSourceTrigger property.
<TextBox Text="{Binding Width, UpdateSourceTrigger=PropertyChanged}" Width="50" />
For a TextBox, the default value for this property is LostFocus, please refer to the Remarks section of the link.

WPF DataGrid - how to stop user proceeding if there are invalid cells? (MVVM)

I'm implementing edit functionality in my DataGrid. The CellEditingTemplate of a typical cell looks something like this:-
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Grid.Column="0"
Text="{Binding Concentration, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=LostFocus}"
Validation.ErrorTemplate="{StaticResource errorTemplate}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
In this example there is a TextBox bound to a property called "Concentration", which is a property of type double, with validation to ensure that it falls within a given range. The models that are being bound to the grid implement IDataErrorInfo by the way.
The problem is, although the error template highlights the cell when an invalid value is entered, there is nothing to stop me from moving focus away from that cell (or row). What's the preferred approach for handling validation with data grid editing? I wondered if I could prevent the user from leaving the cell until a valid value was entered, but that could be dangerous - they wouldn't be able to do anything (even close the app) until a valid value had been entered.
Another option might be to prevent the user from proceeding if the datagrid contains any invalid rows. But what's the best way to test this? IDataErrorInfo doesn't provide an "IsValid" property that I can examine on each of the rows' models.
Lastly I could just revert the model's property back to its previous "good" value (or a default value) if the user enters something invalid.
Any suggestions? I'm using MVVM by the way.
I use this to see if IDataerrorInfo has any errors for the object, a small snippet of the implementation:
protected Dictionary<string, string> _propertyErrors = new Dictionary<string, string>();
public bool HasErrors {
get { return (_propertyErrors.Count) > 0; }
}
Then I can handle the logic for what to do after evaluating this property. Do you want to prevent navigation, closing app, etc. Then you need to evaluate for errors from that code and then cancel that action.
I've used this method in the past to determine if a datagrid has errors:
private bool HasError(DataGrid dg,)
{
bool errors = (from c in
(from object i in dg.ItemsSource
select dg.ItemContainerGenerator.ContainerFromItem(i))
where c != null
select Validation.GetHasError(c)
).FirstOrDefault(x => x);
return errors;
}
Then it's only a matter of preventing the next action if the method returns true.

Invalid data in TextBox -- How to disable my command buttons?

Basically I have the following situation:
<TextBox Text="{Binding MyIntValue}" />
<Button prism:Click.Command={Binding MyCommand}" />
public Boolean CanDoCommand()
{
return (MyIntValue < 100);
}
public void DoCommand() { ... }
So here's the problem, if I type in the value of 25 the MyCommand becomes enabled. Afterwards, if I change it to 25A the Button is still enabled because the binding was not updated to reflect an error in my ViewModel. Instead, I only have an binding error on my View. This leaves the MyCommand button enabled and the MyIntValue still at 25.
How can I disable the button based on having any binding issues even if my ViewModel is proper?
Edit (What the poster is truly asking for):
How can I disable a button regardless
of what the CanExecute method returns
from the ViewModel based upon the View
having a BindingError?
<Button prism:Click.Command={Binding MyCommand,
UpdateSourceTrigger=PropertyChanged}" />
You must raise the command's can execute changed event when MyIntValue changes.
if your MyIntValue property is type of int your binding will never update when your input is 25A.
ony way to solve this is to use type of string and IDataErrorInfo on VM side.
another way is to use typeof Nullable int and a converter and set the value to null when its not what you expect.
EDIT:
How can I disable the button based on having any binding issues even if my ViewModel is proper?
your problem is that your VM and your UI is not in sync. if you type 25A your Vm seems right because it still has the 25, but your View has an BindingError. so your question should be how can i sync my view and viewmodel. (see my two suggestions)
EDIT:
another solution would be to prevent wrong input. so a Masked or RegexTextbox behavior should also work.

How to Validate binded WPF TextBoxes which is depend on each other's value and how to enable/disable Ok button according to Validation

I have two textboxes and two buttons on c# WPF windows application
Application have One variable that called Total Amount.
one text box is for Discount Percentage and other one is for Discount Amount.
if i changed discount amount then percentage should get reflected using DataBinding in WPF and viceversa(I Have that)
I want to validate both the textboxes
1). Discount should be in range MIN to MAX
2). Discount Amount should not be grater than Total Amount
and then Ok button will get Enable/disable according to Validation
I would recommend that you adopt the MVVM pattern (if you haven't already) and have your validation logic contained in the view model. Using this approach, you can either:
Expose an IsValid property on the view model and bind it to the button; the property getter would return the result of your validation logic
Expose an ICommand on the view model whose CanExecute method would return the result of your validation logic (recommended)
A quick example:
public class DiscountViewModel : ViewModel // Base class implements INotifyPropertyChanged
{
// Define all of your view model properties, i.e., DiscountAmount, DiscountPercent, etc.
...
// Define a command
public ICommand OKCommand { get; }
}
Then in your XAML view, you would add the following binding:
<Button Command={Binding Path=OkCommand} Content="OK" />
Again, this is just a brief example that should help point you in the right direction. There are tons of great resources on the MVVM pattern available as well as resources for the WPF command pattern. Here is a good introductory resource that covers both: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
For every Binding you define you can add a Converter. This can converter can be a single value converter, then you must implement the IValueConverter Interface.
But I think you can solve your problem with a IMultiValueConverter. This Converter can be given much values, which can also get from Bindings. The Converter is getting the values from the Bindings, processing them with your logic and then giving it back to your property where you defined it.
http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.aspx

Resources