How to trap view-only validation errors in Catel? - wpf

Trying to figure out how to capture view-only validation errors such as entering non-numeric characters in a text box bound to an integer property. I would like the Catel DataWindow to behave consistently.
Description:
I have a Catel MVVM window (implemented using DataWindow and a view model with a model property.)
The model property is an integer:
public Foo { get { GetValue .......... } }
The view model property is also an integer, and bound to the model:
[ViewModelToModel(...)]
public Foo { get { GetValue .......... } }
In the view, there is a text box that is bound to Foo. When the user enters a non-integer value in the text box, there is naturally an error in the binding process, and because the text box has ValidatesOnExceptions set to true, the following appears in the Catel info message bar:
Two problems that I must fix:
I need a custom error message ("Value 117.228 could not be converted" is not going to fly here.)
The WarningAndErrorValidator does pick up the error, but the DataWindow OK button is still enabled, and I am able to "save" the view model. I need OK to be disabled when there are any errors, even if they don't make it to the view model.
A web search has provided a couple possible solutions:
Bind to a view model property that's a string, and handle mapping/conversion between the view model and the model
Build support in the MVVM framework to trap UI validation errors and communicate them to the view model
Solution #1 is definitely the "workaround" solution because it means I need something like this in the view model (excuse the pseudo-code...):
[ViewToViewModel(...)]
public int Foo { ...... }
// Also a Catel property
public string Foo_Raw { ...... }
// Property changed handlers for both the above properties, keeping them in sync with one another when possible...
protected override void ValidateBusinessRules(List<.......> validationResults)
{
if (this.Foo_Raw != this.Foo.ToString())
{
validationResults.AddError("Foo must be an integer.");
}
}
I am not pleased at the prospect of creating this kind of rickety structure.
I'd much rather go with something like #2, but I didn't see anything in Catel's documentation that suggests that approach is supported. Did I miss an easy solution?
UPDATE: I just learned about the numeric text box behavior which might be another way to solve my specific problem, but I am really looking for a more general solution for capturing binding/UI errors in the view model validation.

The issue is that the exceptions you are trying to receive are not yet bound (since binding them goes wrong). There is no way for the vm to be aware of this issue. Since this is a view-related issue, you can only handle this error in the view.
One solution might be to forward the messages caught by the WarningAndErrorValidator onto the view model. You can define your own WarningAndErrorValidator on the view and subscribe to the Validated event. Then you can pass this onto your vm. This will require you to write a custom base class for your views if you want this shared among all controls in your app.

Geert van Horrik's answer was not quite correct (unless I missed something, Geert). The WarningAndErrorValidator only traps view model errors, not errors from the visual tree itself, or binding errors. It turns out that is something the InfoBarMessageControl does without help from the WarningAndErrorValidator.
What I did was, in my subclass of DataWindow, I duplicated the logic from InfoBarMessageControl that traps and analyzes visual tree validation errors, and I maintained a similar error message data structure.
I then overrode DataWindow::ValidateData like so:
protected override bool ValidateData()
{
// In addition to base class logic, make sure there are no errors of any kind including view-only errors (like binding exceptions or ValidationRule errors).
return base.ValidateData() && this.ViewErrorCount == 0;
}
ViewErrorCount is a simple int that I update when I trap errors as described above.

Related

MVVM detect Validation.HasError in View Model

I'm using MVVM and have most of my validation done using IDataErrorInfo and my ViewModel has an IsValid property which checks the validity of each member that needs to be validated. However I have a couple of textboxes bound to ints that can't be null, so I'm using a ValidationRule to alert the user (with a more friendly message than the "value could not be converted" one) if they blank that field out as obviously the property setter never gets called so the IDataErrorInfo code isn't called.
The problem is that I have a Save button (which is a RelayCommand) which I want disabled if there is any validation error. So the CanExecute of that command checks the VM's IsValid property. But obviously if the user blanks my int field the IDataErrorInfo knows nothing about it and currently the button won't disabled. Is there a way that the ViewModel can detect that error?
I thought I'd found a solution here
http://wpfglue.wordpress.com/2009/12/03/forwarding-the-result-of-wpf-validation-in-mvvm/
but having translated it to C# I can't get it working (the Coerce callback is never called). I don't understand dependency properties and objects very well yet (very new to WPF) and this solution looks complicated to me.
The only thing I can think to do is to get rid of the validation rule and make a nullable int wrapper, put TargetNullValue='' in the binding and then I can check them for null in the IDataErrorInfo code. I would prefer not to do this if there's a better way.
why not use string properties instead of int with IDataErrorInfo validation in your viewmodel? in your savecommand you can safely convert your string to your int values, if IDataErrorInfo has no errors of course. Using string properties with IDataErrorInfo is the most easy way.
edit: one more think, there is another problem if you not use string properties. say you have an int Property, and the user set a 10 in your textbox. so in your viewmodel you have the 10. now the user delete the 10 and set a abc in your textbox. your viewmodel still got the 10., because of the bindingconversationexception. thats why i almost use string properties. to be fair you can use behaviors for textbox to set a mask, so the user can not enter invalid data.
I can think of two strong options right away. One is to bind to a string property in your ViewModel, which in turn is programmed to only parse and store the underlying 'int' value if the string is determined to be valid. This ensures that your TextBox will always successfully store its databound value.
The second is to intercept the ValidationExceptions that occur in your View, storing them in your ViewModel via a custom Behavior. This article will essentially do exactly as you described in your question.
What you can try is BindingGroups and have a validation over the whole element, not just single properties. I used this for our modal dialogs to create a project for example, where certain settings must be set before finishing the dialog. This link explained it in good detail. This one is also quite detailed.

Advice needed regarding validation in MVVM

I'm converting one of my existing applications to the MVVM pattern to improve its structure and I'm a bit torn on what would be the best way to do the data validation.
Currently the application uses data binding to link the UI and code and uses a few validation rules and value converters which can be reused on similar values (one of each for dates, etc.).
Whilst reading up on MVVM I've come across IDataErrorInfo, which I find attractive because it would keep validation out of the view, thus slightly reducing repetitive code when setting bindings, etc. and allow for more specific error messages.
ValidationRules on the other hand block the transfer of binding data if the validation fails, which I need because I only want the model values to change a new, valid value is supplied.
My major concern is that if I restrict things too much in the viewmodel that this will make things difficult in the view - is it a good idea to restrict things to a comfortable level in the general case and then remedy specific cases that need more flexibility in the view?
So my main question is, would it be better to put validation and conversion in the properties of the viewmodel or stick with my validationrules and valueconverters (or some compromise in between)?
I implement all validation in the view model, using IDataErrorInfo, and let the view model decide whether or not it should pass property changes to the model based on whether the property is valid. So a typical setter looks something like:
public string Value
{
set
{
if (value == _Value)
{
return;
}
_Value = value;
Validate("Value");
if (Error["Value"] == null)
{
Model.Value = value;
}
OnPropertyChanged("Value");
}
}
I never, ever implement validation or value conversion in the view. That just seems like begging for trouble.
I would use a combination.
I use Idataerrorinfo in my entities (validation is not in the viewmodel) for core re-usable business rules. My entities can also validate themselves this way.
I then use view ValidationRules for places where a binding error will not make it to my entity such as when a string is used as input in a integer textbox.

Where do I catch Exceptions in MVVM?

My view model class has a method (not sure if that is good practice or if view models are supposed to be strictly property and property changing mechanisms) that connects to a service. Of course I want to handle any possible WCF exceptions when connecting or disconnecting.
Let's use endpoint not found as an example considering that is an exception that I would want to bring to the user's attention. Consider the rough code example:
public void Connect()
{
ServiceClient proxy = null;
try
{
proxy = new ServiceClient();
proxy.Subscribe();
// ...
}
catch(EndpointNotFoundException)
{
// should I do something here?
}
// .. other WCF related exception catches and a finally
}
Is it considered good practice to maybe invoke System.Windows.MessageBox.Show() directly within the catch or should I maybe rethrow the exception so another layer of my WPF application catches it? If so, where is the ideal place to catch such an exception?
I've been handling exceptions in my MVVM client by catching them and wrapping them in an ErrorViewModel property of whatever ViewModel caught the exception.
Let's say a ViewModel A catches the EndpointNotFoundException. To present this error, I wrap the Exception in an ErrorViewModel and assign that to A's Error property.
The View associated with A contains a ContentControl bound to A's Error property. Meanwhile, I use a DataTemplate to associate an Error View to the ErrorViewModel. In that View, Visibility is determined by whether or not A's Error property contains an exception.
So A's View contains an error-message View that will only appear when an exception is caught, and can be dismissed by the user (an OK button on the error-message View invokes a command on A that clears A's Error property, thereby changing the error-message View's visibility to Collapsed).
Thus far, this seems to be a good approach that preserves proper MVVM decoupling.
Hope that helps. One way or another, honestly, I'd consider System.Windows.MessageBox.Show() in a WPF app as purely a last resort. Why give up rich control over the UI in favor of that old thing? Speaking of which, here's another popup-implementation approach.

Is it just me, or is WPF a mess of databinding and custom IValueConverters?

Seriously, it seems like every time I want to make my UI elements talk to each other, I end up coding a new, custom, IValueConverter :(. Someone tell me that I'm doing it wrong, please!
Examples:
I wanted a button to be enabled only if my textbox contained a valid URI. Great, time to code up a UriIsValidConverter!
Oh oops, I also wanted to disable it while I'm processing something. I guess now I need to code up a UriIsValidAndBoolIsFalseMultiConverter!
I want to display a list of files in a certain directory (specified by a textbox) inside a listbox. I guess I need a DirectoryPathToFileList converter!
Oh hey, I want icons for each of those files in the listview. Time for a FileInfoToBitmap converter!
I want my status to be red if my status-string contains "Error", and green otherwise. Yay, I get to code up a StatusStringToSolidColorBrushConverter!
I'm really thinking this isn't that much better than the old Windows Forms method of just wiring up everything manually using TextChanged events (or whatever). Which I guess is still an option. Is that what people actually do, perhaps, and I'm trying too hard to make everything fit into the databinding paradigm?
So yeah, please tell me if this is really how WPF coding is---or if I'm doing it wrong, and if so, what I should be doing.
Your approach is perfectly valid (though I would use a multibinding for the second example, rather than a such a specialised converter), though by placing all your logic into the XAML you are producing very high coupling between the way the application looks and the way that it behaves, because of this you may want to look into the MVVM pattern to separate those things out.
Under the MVVM pattern your XAML (the view) just contains very simple data bindings into a ViewModel which handles all the logic and updates the view through the INotifyPropertyChanged interface. The code for your third example may look something like:
public class DirectoryManagerViewModel : INotifyPropertyChanged
{
private string _directory;
public string Directory
{
get { reutrn _directory; }
set
{
if (_directory != value)
{
_directory = value;
OnPropertyChanged("Directory");
if (IsValidDirectory(value))
{
PopulateFiles();
}
}
}
}
public ObservableCollection<FileViewModel> Files { get; private set; }
private bool IsValidDirectory(string directory)
{
//return if the directory exists etc.
}
private bool PopulateFiles()
{
//Populate Files with files in directory
}
}
Where FileViewModel is another view model which contains the name and the icon for a file.
The advantage of this approach is that the ViewModels can be reused with other views and other technologies such as ASP.NET or Winforms so you are not locked into the WPF stack. (alos if you work in an environment where there are designers responsible for the look and developers responsible for the behaviour, this helps define those boundaries)
At the end of the day though this logic does need to go somewhere and while there are better and worse ways to architect your application you are still going to be writing code that takes a string and converts it into a series of filenames and icons somewhere.
First, you might want to start by reading about the Model-View-ViewModel pattern (MVVM). Josh Smith had a fantastic article in MSDN Magazine recently. MVVM and WPF go perfectly together. Done right, you won't need IValueConverters so much. The way that you are going about it now is causing a very tight coupling between your visualization and your application actions. MVVM is designed to decouple these elements.
In this context, your view model will track state for you. Your button will be enabled if the CanExecute method on a certain ICommand in your view model returns true. This same concept can handle disabling the button when processing something.
You want to display a list of files in a certain directory that is specified inside a listbox? Have a DirectoryViewModel view model that will handle providing the list of files to the view by binding to the view model. The display of the files can be specified with a DataTemplate specified in XAML with no code behind. This same concept can handle providing the icons to the view whose display can be specified in the template.
You want your status to be red if a status message contains "Error" and green otherwise? Let a view model handle determining the state and let the view bind to that state and now you only need an IStateConverter to convert the state to red or green appropriately (this is one of many ways to handle this problem in the MVVM context).
Get in the habit of keep data and state separate from your view and your applications will be loosely coupled, easier to design and maintain, and easier to test.
Don't know if you are wrong, just making it a lot harder than it has to be!
I use MVVM, so where you are writing customer converters, I provide a bindable property on the view model that tells the view what to do. For example:
To display a list of files, I provide a collection that contains that list.
If I want icons the object in that collection has a icon property
If I want a status to be red or green I provide a StatusColorbrush property.
By moving this logic into the view model, I get:
much simpler Xaml.
can test my view logic without the view.
This approach uses one of the strong points of WPF, it's binding capabilities.

Event on validation - WPF

I'm looking at developing a simple validation framework for WPF (the IDataErrorInfo method doesn't provide enough info for my needs), and am wondering if there is a way to be notified when a Property is going to validate? Please note that I need to know any time it is going to attempt validation rather than only when there is an error (so NotifyOnValidationError doesn't cut it)
Alternatively, my ultimate goal is simply to package more information than just an error string into my validations (How critical is the error, links for more info, etc.) while still allowing the validation to be driven by the data object (IDataErrorInfo style). If anyone can point me to a method for doing that then I'll be perfectly happy as well. :)
The problem you are going to run into is that WPF databinding and validation are tied to the IDataErrorInfo interface. The bindings check for validation based on the UpdateSourceTrigger property of the binding. So if your binding has "UpdateSourceTrigger=PropertyChanged" then everytime the property changes it calls the item["MyProperty"] which is where you would return information as to whether of not your property is valid. If it's set to "LostFocus" then it checks whenever the control loses focus. The binding also requires the "ValidatesOnDataErrors=True" in order for it to force validation on your bound entity.
I think your best bet would be to create a class that implements IDataErrorInfo and then supply more detailed information based on the severity of the error.
You need to look into inheriting from ValidationRule and then adding the new rule to all you binding objects.

Resources