Reputation: 944
I am using MVC\MVVM and WPF. I have a form that is bound to a model and a controller that is catching the PropertyChanged events and evaluating business rules and validations. Well, I want to pop-up a message when a validation is false telling the user what is wrong and how to fix it. But I'm not sure the "correct" way from the controller. I'd like to throw an exception that can be caught by the view but I can't figure out how. I've tried Dispatcher.Invoke() but that just got me an unhandled exception at the application level.
How do I trap exceptions that are generated from the PropertyChanged event handler in the Controller?
EDIT: Specifically I have a combobox that has a list of discounts in it. I can't allow an inappropriate selection, but I have to notify the user why the selection is inappropriate. This is not as obvious as a textbox that has integers in it. I need to tell the user the date the customer filled out a survey. They can't use that discount twice. I don't want to exclude the survey discount from the list since that would just look like an error to the user. I need to show them the discount and tell them the customer has used that discount and cannot use it again.
EDIT 2: I have looked over the ValidationRule class and since I have to use a database lookup I don't see how I'm going to keep everything in the Model and still have the business rules in the Controller. I've looked at IDataErrorInfo but that requires me to wrap my Model in my Controller and bind to the Controller instead, but only for one field. I think the best course of action in this case is to have the Controller call a method on the View and pop-up a message.
Upvotes: 0
Views: 1488
Reputation: 437904
You are walking down the wrong path.
A good way to handle validation in MVVM is by implementing IDataErrorInfo
and setting ValidatesOnDataErrors
to true
on your bindings. You will also almost certainly want to enable ValidatesOnExceptions
for completeness, and NotifyOnValidationError
so that the binding engine triggers the Validation.Error
attached event on the control that you have bound the property to.
For more details, see the Validation section on the MSDN documentation for data binding in WPF.
Some tips:
INotifyDataErrorInfo
and the corresponding ValidatesOnNotifyDataErrors
binding property that provide beefed-up validation functionality in comparison to IDataErrorInfo
; you might want to look into that.IDataErrorInfo.Error
because it is used by the Windows Forms infrastructure and is ignored in WPF. You can even have the getter throw NotImplementedException
.Update: clarification and example code
This validation model does not involve implementing a ValidationRule
yourself at all; your models (i.e. the binding sources) simply need to implement one of the two interfaces. How to implement the interface is entirely up to you; in a past project I implemented basic async validation with
public interface IDelegatedValidation : IDataErrorInfo
{
/// <summary>
/// Occurs when validation is to be performed.
/// </summary>
event EventHandler<DelegatedValidationEventArgs> ValidationRequested;
}
public class DelegatedValidationEventArgs : EventArgs
{
public DelegatedValidationEventArgs(string propertyName)
{
this.PropertyName = propertyName;
}
public string PropertyName { get; private set; }
public bool Handled { get; set; }
public string ValidationError { get; set; }
}
The model implements IDelegatedValidation
by exposing an event and with
string IDataErrorInfo.this[string columnName]
{
get { return this.GetValidationError(columnName); }
}
private string GetValidationError(string propertyName)
{
var args = new DelegatedValidationEventArgs(propertyName);
this.OnValidationRequested(args);
return args.ValidationError;
}
protected virtual void OnValidationRequested(DelegatedValidationEventArgs args)
{
var handler = this.ValidationRequested;
if (handler == null) {
return;
}
foreach (EventHandler<DelegatedValidationEventArgs> target in handler.GetInvocationList()) {
target.Invoke(this, args);
if (args.Handled) {
break;
}
}
}
So the workflow goes like this:
ValidationRequested
event.GetValidationError
.args.ValidationError
and args.Handled
to true
so that the chain is stopped.There is an added twist in that if the viewmodel needs to know whether its model is valid or not (e.g. to enable/disable a "save" command) it needs to also tap into this process.
There's really nothing that you cannot do with IDataErrorInfo
/ INotifyDataErrorInfo
because how you implement them is entirely up to you. Definitely have a look at the Silverlight version of the latter's documentation for more examples; there is also a lot of helpful material like this on the internet.
Upvotes: 2