I have a question about when to validate inputs on C# form applications.
When taking inputs from a form application, you can run all the validation as you parse in the data from the text fields in the main form class, to then use wherever. For example:
Method 1:
class Car
{
private string registration { set; get; } // NO VALIDATION HERE
}
// in the form class
private void add_Click(object sender, EventArgs e)
{
// get registration
int reg_valid = Validate.registration(txtReg.Text); // VALIDATION IS DONE HERE
if(reg_valid)
{
Car.registration = txtReg.Text;
} else {
// Registration invalid - throw error
}
}
There will be no validation in the actual Car object as it is all done in the form class.
The other way is to validate the input in the actual class it is stored in like:
Method 2:
class Car
{
// out registration
private string registration;
// we can set our registration here and return false if there is an error
bool set_registration(registration)
{
int reg_valid = Validate.registration(registration); // VALIDATION IS DONE HERE
if(reg_valid) {
this.registration = registration;
} else {
return false; // if error
}
return true; // if all goes well
}
}
// in the form class
private void add_Click(object sender, EventArgs e)
{
// get registration
string registration = txtReg.Text; // NO VALIDATION DONE HERE
// we can then store this in our object
if( ! Car.set_registration(registration))
{
// Registration invalid - throw error
}
}
Method 1 seems cleaner, but one of the reasons why we use private attributes is so that we can validate any parsing arguments which is why method 2 seems the better option. Also, using method 2, errors have to be carried up the stack possibly many times, which leads to repeated if statements.
* Please note, this code has not been ran so may contain errors.
Caution : Opinion-based answer !
It depends on the requirements of the Project, its scale, and the time you have (and many other factors of importance like "User Friendly Application" or of no importance like "my own taste would be..")
Due to the wide range of possible cases per individual, the answer cannot be expressed in a general guideline which would fulfill the needs of everyone.
Your question addressed two objects to manipulate :
one Form used as an User Interface where the user can input anything (assumption defined by "the user enter *any* value then click a Button")
a Class Property that is fed with the value (Setter) when it's valid.
You have no time, requirements just tells "Input a value", this is a small task in the Project of no critical importance on a small class used within this Button, and you're very lazy :
=> In Button Click, make a rigid validation with one or two forbidden checks that whip out any non allowed value, and shows a MessageBox depending on the nature of the Error. Only pass a Value to the class and go further when you have a valid value.
.
You have no time, simple requirements, small task of no critical importance but with a class largely used, but you're lazy :
=> Move the validation above in the class then Throw one, two or three generic Exceptions with custom messages. In any UI action using that class, use Try/Catch to capture the exception in the ButtonClick and display a MessageBox without going further in case of captured Exception.
.
You have 5 minutes, simple requirements, small non critical task, important Class role :
=> Like previously but, decide whether another way to show user is more recommended (depending of your target users) like changing the background color of a TextBox after you click a button, displaying the message in a Label, instead of annoying the user with a dialog box...
.
You have 10 minutes, simple requirements, repetitive task, important Class role :
=> Find a way to quickly test a given value and creates a public or friend (static ?) function in your Class that returns the result of a validation attempt, like an UICarValidationEnum Enumeration (* see Edit below) of possible Validation Errors :
a) UICarValidationEnum.Valid
b) .TooLong
c) .NullValue
d) .ForbiddenChars
e) ...
You can then use that validation Function inside and outside your Class. That way, you can handle invalid Setter attempts both before performing the Setter and while performing it, aswell as both using you class along with UI and without. The drawback is you can't avoid double validation if you check if the data is valid outside the class (In your Button for eg.)
.
Time matters, but requirements is efficient inputs, repetitive task of great importance, class largely used - you can't be lazy :
=> Handle both UI Validation and Class Validation. Why ? The UI Part addresses the "efficient inputs" part, and the Class Part addresses the "role of the Class". You can use the Validator Function above, or implement much more Exceptions upon Setter like in the other cases above. You're increasing the number of handled Exceptions/Invalid inputs by providing much more information to the user upon Button Click.
Remember : Class largely used. The move here is to implement the maximum coding to handle different cases of invalid inputs, to reduce the amount of code to write elsewhere in your project while still be able to handle all those cases.
.
Time matters, User Friendly Application, repetitive task, important Class :
=> Reconsider the UI Layout and behaviour, validation is mainly UI, but Class is still doing it by itself :
a) User hates Dialog Boxes (whatever DialogBox, message, openfile, etc.. avoid them whenever possible)
b) User hates aggressive colors
c) User hates validation
d) ...... User hates a lot of things..!
The role of the Function Validation above is of importance here : Capture the User Input actions. If it's a TextBox for example, capture the TextChanged Event, then call the Class validation Function. Update information in a Label notifying any encountered error, and change the background color of the TextBox to a friendly color, but related to an error standard, like light Pink. Don't use Color.Red, that's too agressive for most users. Seeing Red 200 times a day can lead to unexpected behaviours at the end of the day.
Only enable the Button when ALL Inputs are valid. Don't make a button per input, User hates endless validations.
.
Time doesn't really matter, User Friendly Application, repetitive task, important Class :
=> Along with the previous option, improve UI response.
Add graphic icons in your label, and/or consider the use of an ErrorProvider in your Form. Users like simple icons rather than long similar messages.
Consider the use friendly sounds to notify poping errors (not everyone is "kind of visual receptive")
.
Time doesn't matter at all, User Friendly Application, repetitive task, important Class :
=> Keep capturing user inputs live, but implement suggestive corrections related to. If it's a TextBox, use AutoCompleteCollection with predefined and fixed Inputs suggestions in the collection when user attempts to input invalid Datas (you'll need a Function for that).
Live disable other inputs when they are incompatible with user's choices/actions.
.
Time really matters, User Friendly recommended, repetitive task, important Class :
=> Don't overload your Form with validations routines.
Create a Custom Control for that specific Input. Overload your custom control with the validations. If your class cannot be used outside this UI, don't bother creating validations inside. Move everything in your custom Control and, even forbid invalid chars (if it's a textbox) whenever possible. In this specific case, you're going to use the UserControl as the Validator component of your Datas.
Otherwise, use the Class validating Function scheme upon Input and display the appropriate error in an User Friendly way whenever possible through that user Control (case where your Class may be used without the UserControl, which would generally be the case)
Etc. etc. etc.
I know I'm more considering the user who's going to use the Application than the one who's going to write the code. But have a look at Baltasarq's answer. In fact, best move is to do both. The three first options are there only for a simple Class of no real importance in your whole Project. We've all created that tiny Class to begin with, and we didn't bother to make strong validation controls. I think that most of us still use those kind of classes. However, as times goes by, some of those classes become more and more used.. then we were faced to the need of stronger validations schemes.
That's the cutting corners way. It's unsafe, but it works... until something happens...
=> Try to do both whenever possible. You don't know what may happen, who is going to use your Class, whether your boss will change his mind and ask you to create a specific UI for that Class to be user friendly for everyone...
Since a while, when I have time upon creating a Class, I'm in fact
creating at least two classes :
- MyClass_Class
- MyClass_Form
- (and perhaps MyClass_UserControl)
- (and perhaps MyChildClassWithUnsafeSetters - used to set value that has already been validated for performance needs)
The core class always provide live Validation capabilities...
EDIT :
Property Validator Function sample using enums...
[Flags]
public enum registrationValidation_Enum
{
Valid = 0x01,
TooLong = 0x02,
InvalidChars = 0x04,
NullEntry = 0x08
// ...
}
This enum can be encapsulated within the class. Enums are easier to handle/memorize/retrieve than Exceptions Classes.
This is the Property Getter/Setter.
class Car
{
private string registration = "Unknown";
public string Registration
{
get
{
return registration;
}
set
{
validate_registration(value, True);
// Setter for the Property.
// Throws an Exception upon invalid value.
}
}
}
This is a Validator Function which is Public :
public registrationValidation_Enum test_registration(
string newRegistration)
{
registrationValidation_Enum checkResult =
registrationValidation_Enum.Valid;
// Do the checks here
if (newRegistration.Length > 10)
{
checkResult = checkResult | registrationValidation_Enum.TooLong;
}
if (containsNonAlphNumericChars(newRegistration))
{
checkResult = checkResult | registrationValidation_Enum.InvalidChars;
}
// ...
return checkResult;
}
And here is the Public version of the Setter :
// this bypass the double check : attempts to set the value if Valid.
// otherwise, either returns a validation result,
// either throws an exception.
public registrationValidation_Enum validate_registration(
string newRegistration,
bool canThrowException)
{
bool isValid = test_registration(newRegistration);
if (isValid == registrationValidation_Enum.Valid)
{
registration = newRegistration;
return registrationValidation_Enum.Valid;
}
else
{
if (canThrowException)
{
string exceptionMessage = "";
if (isValid | registrationValidation_Enum.TooLong)
{
exceptionMessage += "Registration too long"
+ Environment.NewLine;
}
if (isValid | registrationValidation_Enum.InvalidChars)
{
exceptionMessage +=
"Registration contains invalid characters"
+ Environment.NewLine;
}
// ....
Throw New Exception(exceptionMessage);
}
else
{
return isValid;
}
}
}
The public validate_registration(string, false) is there in case :
you don't want to double the validation upon Button Click
you don't live monitor the user Inputs
you don't want to handle cascading exceptions everywhere
you want have control over custom error messages per UI context (you can't always have a label to write errors in)
while attempting to set registration value, all in a row with a button click.
Put the validation result in a variable in any UI-side and display the appropriate notifications/users choices depending on the UI components you can display... This wouldn't have been that simple with Exceptions : Imagine you have both TooLong and InvalidChars. Are you going to show a dialogbox "Too Long" then click button then show another "Invalid Chars" dialog ?
Note : To make the class Localizable, with custom Exception messages using Culture, I would define Class Level Messages (string) variables which values depend on the loaded Culture.
You should have a Core of classes and also those classes related to the user interface. First, create the Core classes (the bussiness logic) of your application. Of course, you should do validation here. Actually, code as if you had no knowledge about who is going to implement the user interface. Also, take into account that users interfaces can change, while the core classes should remain untouched.
When coding the user interface, you wil realise that if you don't do any validation, exceptions will be raised when the user inputs some data cases. This will make you create validation also in the user interface classes.
So, the answer is, you will finally have validation on both parts of the application.
You would use public properties with private members, where you do the checking inside the properties and then assign it to class members.
class Car
{
private string registration;
public string Registration
{
get { return registration;}
set {
if(Validate.registration(value))
registration = value;
else
throw new Exception("Your exception message here");
}
}
}
private void add_Click(object sender, EventArgs e)
{
Car.Registration = txtReg.Text;
}
There are 2 validations:
data type/property limits/parsing;
logical (values between properties);
Have a look at PropertyGrid. It's basic (and pretty sufficient) validation is if your property accept value or throw:
class Car
{
private int _someValue;
public int SomeValue
{
get { return _someValue; }
set
{
if(value > 100)
throw new OutOfRangeException(...);
_someValue = value;
}
}
}
This ensures what Car can validate own properties no matter how they are set (deserialization, PropertyGrid, direct property value, reflection, etc).
Another thing is some logical validation, which can't be performed at instance level, or you simply don't care about it (let instance to be created without exceptions). This one has to be put into editor code (form what is used to create instance).
As for myself, talking about TextBox, I prefer to make custom MyTextBox control which has all parsing related validation (methods GetDouble(), GetInt(), GetIntTime(), etc) and possibility to setup/query it (properties IsDouble, IsInt, etc.).
Related
Warning: Very long and detailed post.
Okay, validation in WPF when using MVVM. I’ve read many things now, looked at many SO questions, and tried many approaches, but everything feels somewhat hacky at some point and I’m really not sure how to do it the right way™.
Ideally, I want to have all validation happen in the view model using IDataErrorInfo; so that’s what I did. There are however different aspects that make this solution be not a complete solution for the whole validation topic.
The situation
Let’s take the following simple form. As you can see, it’s nothing fancy. We just have a two textboxes which bind to a string and int property in the view model each. Furthermore we have a button that is bound to an ICommand.
So for the validation we now have a two choices:
We can run the validation automatically whenever the value of a text box changes. As such the user gets an instant response when he entered something invalid.
We can take this one step further to disable the button when there are any errors.
Or we can run the validation only explicitly when the button is pressed, then showing all errors if applicable. Obviously we can’t disable the button on errors here.
Ideally, I want to implement choice 1. For normal data bindings with activated ValidatesOnDataErrors this is default behavior. So when the text changes, the binding updates the source and triggers the IDataErrorInfo validation for that property; errors are reported back the view. So far so good.
Validation status in the view model
The interesting bit is to let the view model, or the button in this case, know if there are any errors. The way IDataErrorInfo works, it is mainly there to report errors back to the view. So the view can easily see if there are any errors, display them and even show annotations using Validation.Errors. Furthermore, validation always happens looking at a single property.
So having the view model know when there are any errors, or if the validation succeeded, is tricky. A common solution is to simply trigger the IDataErrorInfo validation for all properties in the view model itself. This is often done using a separate IsValid property. The benefit is that this can also be easily used for disabling the command. The drawback is that this might run the validation on all properties a bit too often, but most validations should be simply enough to not hurt the performance. Another solution would be to remember which properties produced errors using the validation and only check those, but that seems a bit overcomplicated and unnecessary for most times.
The bottom line is that this could work fine. IDataErrorInfo provides the validation for all properties, and we can simply use that interface in the view model itself to run the validation there too for the whole object. Introducing the problem:
Binding exceptions
The view model uses actual types for its properties. So in our example, the integer property is an actual int. The text box used in the view however natively only supports text. So when binding to the int in the view model, the data binding engine will automatically perform type conversions—or at least it will try. If you can enter text in a text box meant for numbers, the chances are high that there won’t always be valid numbers inside: So the data binding engine will fail to convert and throw a FormatException.
On the view side, we can easily see that. Exceptions from the binding engine are automatically caught by WPF and are displayed as errors—there isn’t even a need to enable Binding.ValidatesOnExceptions which would be required for exceptions thrown in the setter. The error messages do have a generic text though, so that could be a problem. I have solved this for myself by using a Binding.UpdateSourceExceptionFilter handler, inspecting the exception being thrown and looking at the source property and then generating a less generic error message instead. All that capsulated away into my own Binding markup extension, so I can have all the defaults I need.
So the view is fine. The user makes an error, sees some error feedback and can correct it. The view model however is lost. As the binding engine threw the exception, the source was never updated. So the view model is still on the old value, which isn’t what’s being displayed to the user, and the IDataErrorInfo validation obviously doesn’t apply.
What’s worse, there is no good way for the view model to know this. At least, I haven’t found a good solution for this yet. What would be possible is to have the view report back to the view model that there was an error. This could be done by data binding the Validation.HasError property back to the view model (which isn’t possible directly), so the view model could check the view’s state first.
Another option would be to relay the exception handled in Binding.UpdateSourceExceptionFilter to the view model, so it would be notified of it as well. The view model could even provide some interface for the binding to report these things, allowing for custom error messages instead of generic per-type ones. But that would create a stronger coupling from the view to the view model, which I generally want to avoid.
Another “solution” would be to get rid of all typed properties, use plain string properties and do the conversion in the view model instead. This obviously would move all validation to the view model, but also mean an incredible amount of duplication of things the data binding engine usually takes care of. Furthermore it would change the semantics of the view model. For me, a view is built for the view model and not the reverse—of course the design of the view model depends on what we imagine the view to do, but there’s still general freedom how the view does that. So the view model defines an int property because there is a number; the view can now use a text box (allowing all these problems), or use something that natively works with numbers. So no, changing the types of the properties to string is not an option for me.
In the end, this is a problem of the view. The view (and its data binding engine) is responsible for giving the view model proper values to work with. But in this case, there seems to be no good way to tell the view model that it should invalidate the old property value.
BindingGroups
Binding groups are one way I tried to tackle this. Binding groups have the ability to group all validations, including IDataErrorInfo and thrown exceptions. If available to the view model, they even have a mean to check the validation status for all of those validation sources, for example using CommitEdit.
By default, binding groups implement choice 2 from above. They make the bindings update explicitly, essentially adding an additional uncommitted state. So when clicking the button, the command can commit those changes, trigger the source updates and all validations and get a single result if it succeeded. So the command’s action could be this:
if (bindingGroup.CommitEdit())
SaveEverything();
CommitEdit will only return true if all validations succeeded. It will take IDataErrorInfo into account and also check binding exceptions. This seems to be a perfect solution for choice 2. The only thing that is a bit of a hassle is managing the binding group with the bindings, but I’ve built myself something that mostly takes care of this (related).
If a binding group is present for a binding, the binding will default to an explicit UpdateSourceTrigger. To implement choice 1 from above using binding groups, we basically have to change the trigger. As I have a custom binding extension anyway, this is rather simple, I just set it to LostFocus for all.
So now, the bindings will still update whenever a text field changes. If the source could be updated (binding engine throws no exception) then IDataErrorInfo will run as usual. If it couldn’t be updated the view is still able to see it. And if we click our button, the underlying command can call CommitEdit (although nothing needs to be committed) and get the total validation result to see if it can continue.
We might not be able to disable the button easily this way. At least not from the view model. Checking the validation over and over is not really a good idea just to update the command status, and the view model isn’t notified when a binding engine exception is thrown anyway (which should disable the button then)—or when it goes away to enable the button again. We could still add a trigger to disable the button in the view using the Validation.HasError so it’s not impossible.
Solution?
So overall, this seems to be the perfect solution. What is my problem with it though? To be honest, I’m not entirely sure. Binding groups are a complex thing that seem to be usually used in smaller groups, possibly having multiple binding groups in a single view. By using one big binding group for the whole view just to ensure my validation, it feels as if I’m abusing it. And I just keep thinking, that there must be a better way to solve this whole situation, because surely I can’t be the only one having these problems. And so far I haven’t really seen many people use binding groups for validation with MVVM at all, so it just feels odd.
So, what exactly is the proper way to do validation in WPF with MVVM while being able to check for binding engine exceptions?
My solution (/hack)
First of all, thanks for your input! As I have written above, I’m using IDataErrorInfo already to do my data validation and I personally believe it’s the most comfortable utility to do the validation job. I’m using utilities similar to what Sheridan suggested in his answer below, so maintaining works fine too.
In the end, my problem boiled down to the binding exception issue, where the view model just wouldn’t know about when it happened. While I could handle this with binding groups as detailed above, I still decided against it, as I just didn’t feel all that comfortable with it. So what did I do instead?
As I mentioned above, I detect binding exceptions on the view-side by listening to a binding’s UpdateSourceExceptionFilter. In there, I can get a reference to the view model from the binding expression’s DataItem. I then have an interface IReceivesBindingErrorInformation which registers the view model as a possible receiver for information about binding errors. I then use that to pass the binding path and the exception to the view model:
object OnUpdateSourceExceptionFilter(object bindExpression, Exception exception)
{
BindingExpression expr = (bindExpression as BindingExpression);
if (expr.DataItem is IReceivesBindingErrorInformation)
{
((IReceivesBindingErrorInformation)expr.DataItem).ReceiveBindingErrorInformation(expr.ParentBinding.Path.Path, exception);
}
// check for FormatException and produce a nicer error
// ...
}
In the view model I then remember whenever I am notified about a path’s binding expression:
HashSet<string> bindingErrors = new HashSet<string>();
void IReceivesBindingErrorInformation.ReceiveBindingErrorInformation(string path, Exception exception)
{
bindingErrors.Add(path);
}
And whenever the IDataErrorInfo revalidates a property, I know that the binding worked, and I can clear the property from the hash set.
In the view model I then can check if the hash set contains any items and abort any action that requires the data to be validated completely. It might not be the nicest solution due to the coupling from the view to the view model, but using that interface it’s at least somewhat less a problem.
Warning: Long answer also
I use the IDataErrorInfo interface for validation, but I have customised it to my needs. I think that you'll find that it solves some of your problems too. One difference to your question is that I implement it in my base data type class.
As you pointed out, this interface just deals with one property at a time, but clearly in this day and age, that's no good. So I just added a collection property to use instead:
protected ObservableCollection<string> errors = new ObservableCollection<string>();
public virtual ObservableCollection<string> Errors
{
get { return errors; }
}
To address your problem of not being able to display external errors (in your case from the view, but in mine from the view model), I simply added another collection property:
protected ObservableCollection<string> externalErrors = new ObservableCollection<string>();
public ObservableCollection<string> ExternalErrors
{
get { return externalErrors; }
}
I have an HasError property which looks at my collection:
public virtual bool HasError
{
get { return Errors != null && Errors.Count > 0; }
}
This enables me to bind this to Grid.Visibility using a custom BoolToVisibilityConverter, eg. to show a Grid with a collection control inside that shows the errors when there are any. It also lets me change a Brush to Red to highlight an error (using another Converter), but I guess you get the idea.
Then in each data type, or model class, I override the Errors property and implement the Item indexer (simplified in this example):
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this["Name"]);
errors.AddUniqueIfNotEmpty(this["EmailAddresses"]);
errors.AddUniqueIfNotEmpty(this["SomeOtherProperty"]);
errors.AddRange(ExternalErrors);
return errors;
}
}
public override string this[string propertyName]
{
get
{
string error = string.Empty;
if (propertyName == "Name" && Name.IsNullOrEmpty()) error = "You must enter the Name field.";
else if (propertyName == "EmailAddresses" && EmailAddresses.Count == 0) error = "You must enter at least one e-mail address into the Email address(es) field.";
else if (propertyName == "SomeOtherProperty" && SomeOtherProperty.IsNullOrEmpty()) error = "You must enter the SomeOtherProperty field.";
return error;
}
}
The AddUniqueIfNotEmpty method is a custom extension method and 'does what is says on the tin'. Note how it will call each property that I want to validate in turn and compile a collection from them, ignoring duplicate errors.
Using the ExternalErrors collection, I can validate things that I can't validate in the data class:
private void ValidateUniqueName(Genre genre)
{
string errorMessage = "The genre name must be unique";
if (!IsGenreNameUnique(genre))
{
if (!genre.ExternalErrors.Contains(errorMessage)) genre.ExternalErrors.Add(errorMessage);
}
else genre.ExternalErrors.Remove(errorMessage);
}
To address your point regarding the situation where a user enters an alphabetical character into a int field, I tend to use a custom IsNumeric AttachedProperty for the TextBox, eg. I don't let them make these kinds of errors. I always feel that it's better to stop it, than to let it happen and then fix it.
Overall I'm really happy with my validation ability in WPF and am not left wanting at all.
To end with and for completeness, I felt that I should alert you to the fact that there is now an INotifyDataErrorInfo interface which includes some of this added functionality. You can find out more from the INotifyDataErrorInfo Interface page on MSDN.
UPDATE >>>
Yes, the ExternalErrors property just let's me add errors that relate to a data object from outside that object... sorry, my example wasn't complete... if I'd have shown you the IsGenreNameUnique method, you would have seen that it uses LinQ on all of the Genre data items in the collection to determine whether the object's name is unique or not:
private bool IsGenreNameUnique(Genre genre)
{
return Genres.Where(d => d.Name != string.Empty && d.Name == genre.Name).Count() == 1;
}
As for your int/string problem, the only way I can see you getting those errors in your data class is if you declare all your properties as object, but then you'd have an awful lot of casting to do. Perhaps you could double your properties like this:
public object FooObject { get; set; } // Implement INotifyPropertyChanged
public int Foo
{
get { return FooObject.GetType() == typeof(int) ? int.Parse(FooObject) : -1; }
}
Then if Foo was used in code and FooObject was used in the Binding, you could do this:
public override string this[string propertyName]
{
get
{
string error = string.Empty;
if (propertyName == "FooObject" && FooObject.GetType() != typeof(int))
error = "Please enter a whole number for the Foo field.";
...
return error;
}
}
That way you could fulfil your requirements, but you'll have a lot of extra code to add.
In my opinion, the problem lies in validation happening at too many places. I also wished to write all my validation login in ViewModel but all those number binding were making my ViewModel crazy.
I solved this problem by creating a binding that never fails. Obviously, if a binding is always successful then the type itself has to handle the error conditions gracefully.
Failable Value Type
I started by creating a generic type which would gracefully support the failed conversions:
public struct Failable<T>
{
public T Value { get; private set; }
public string Text { get; private set; }
public bool IsValid { get; private set; }
public Failable(T value)
{
Value = value;
try
{
var converter = TypeDescriptor.GetConverter(typeof(T));
Text = converter.ConvertToString(value);
IsValid = true;
}
catch
{
Text = String.Empty;
IsValid = false;
}
}
public Failable(string text)
{
Text = text;
try
{
var converter = TypeDescriptor.GetConverter(typeof(T));
Value = (T)converter.ConvertFromString(text);
IsValid = true;
}
catch
{
Value = default(T);
IsValid = false;
}
}
}
Note that even if the type fails to initialise because of invalid input string (second constructor), it quietly stores the invalid state along with invalid text also. This is required in order to support the round-trip of binding even in case of wrong input.
Generic Value Converter
A generic value converter could be written using above type:
public class StringToFailableConverter<T> : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(Failable<T>))
throw new InvalidOperationException("Invalid value type.");
if (targetType != typeof(string))
throw new InvalidOperationException("Invalid target type.");
var rawValue = (Failable<T>)value;
return rawValue.Text;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(string))
throw new InvalidOperationException("Invalid value type.");
if (targetType != typeof(Failable<T>))
throw new InvalidOperationException("Invalid target type.");
return new Failable<T>(value as string);
}
}
XAML Handy Converters
Since creating and using the instances of generics is pain in XAML, lets make static instances of common converters:
public static class Failable
{
public static StringToFailableConverter<Int32> Int32Converter { get; private set; }
public static StringToFailableConverter<double> DoubleConverter { get; private set; }
static Failable()
{
Int32Converter = new StringToFailableConverter<Int32>();
DoubleConverter = new StringToFailableConverter<Double>();
}
}
Other value types can be extended easily.
Usage
Usage is pretty simple, just need to change the type from int to Failable<int>:
ViewModel
public Failable<int> NumberValue
{
//Custom logic along with validation
//using IsValid property
}
XAML
<TextBox Text="{Binding NumberValue,Converter={x:Static local:Failable.Int32Converter}}"/>
This way, you can use the same validation mechanism (IDataErrorInfo or INotifyDataErrorInfo or anything else) in ViewModel by checking the IsValid property. If IsValid is true, you can directly use the Value.
Ok, I believe I have found the answer you were looking for...
It will not be easy to explain - but..
Very easy to understand once explained...
I do think It is most accurate/"certified" to MVVM viewed as "standard" or at the least attempted standard.
But before we begin.. you need to change a concept which you got used to regarding MVVM:
"Furthermore it would change the semantics of the view model. For me,
a view is built for the view model and not the reverse—of course the design
of the view model depends on what we imagine the view to do,
but there’s still general freedom how the view does that"
That paragraph is the source of your problem.. - why?
Because you are stating the View-Model has no role to adjust itself to the View..
That is wrong in many ways - as I'll prove to you very simply..
If you have a property such as:
public Visibility MyPresenter { get...
What is Visibility if not something that serves the View?
The type itself and the name that will be given to the property is definitely made up for the view.
There two distinguishable View-Models categories in MVVM according to my experience:
Presenter View Model - which is to be hooked to buttons, menus, Tab Items etc....
Entity View Model - which is to be hocked to controls that brings the entity data to screen.
These are two different - completely different concerns.
And now to the solution:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class VmSomeEntity : ViewModelBase, INotifyDataErrorInfo
{
//This one is part of INotifyDataErrorInfo interface which I will not use,
//perhaps in more complicated scenarios it could be used to let some other VM know validation changed.
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
//will hold the errors found in validation.
public Dictionary<string, string> ValidationErrors = new Dictionary<string, string>();
//the actual value - notice it is 'int' and not 'string'..
private int storageCapacityInBytes;
//this is just to keep things sane - otherwise the view will not be able to send whatever the user throw at it.
//we want to consume what the user throw at us and validate it - right? :)
private string storageCapacityInBytesWrapper;
//This is a property to be served by the View.. important to understand the tactic used inside!
public string StorageCapacityInBytes
{
get { return storageCapacityInBytesWrapper ?? storageCapacityInBytes.ToString(); }
set
{
int result;
var isValid = int.TryParse(value, out result);
if (isValid)
{
storageCapacityInBytes = result;
storageCapacityInBytesWrapper = null;
RaisePropertyChanged();
}
else
storageCapacityInBytesWrapper = value;
HandleValidationError(isValid, "StorageCapacityInBytes", "Not a number.");
}
}
//Manager for the dictionary
private void HandleValidationError(bool isValid, string propertyName, string validationErrorDescription)
{
if (!string.IsNullOrEmpty(propertyName))
{
if (isValid)
{
if (ValidationErrors.ContainsKey(propertyName))
ValidationErrors.Remove(propertyName);
}
else
{
if (!ValidationErrors.ContainsKey(propertyName))
ValidationErrors.Add(propertyName, validationErrorDescription);
else
ValidationErrors[propertyName] = validationErrorDescription;
}
}
}
// this is another part of the interface - will be called automatically
public IEnumerable GetErrors(string propertyName)
{
return ValidationErrors.ContainsKey(propertyName)
? ValidationErrors[propertyName]
: null;
}
// same here, another part of the interface - will be called automatically
public bool HasErrors
{
get
{
return ValidationErrors.Count > 0;
}
}
}
And now somewhere in your code - your button command 'CanExecute' method can add to its implementation a call to VmEntity.HasErrors.
And may peace be upon your code regarding validation from now on :)
The drawback is that this might run the validation on all properties a
bit too often, but most validations should be simply enough to not
hurt the performance. Another solution would be to remember which
properties produced errors using the validation and only check those,
but that seems a bit overcomplicated and unecessary for most times.
You don't need to track which properties have errors; you only need to know that errors exist. The view model can maintain a list of errors (also useful for displaying an error summary), and the IsValid property can simply be a reflection of whether the list has anything. You don't need to check everything each time IsValid is called, as long as you ensure that the error summary is current and that IsValid is refreshed each time it changes.
In the end, this is a problem of the view. The view (and its data
binding engine) is responsible for giving the view model proper values
to work with. But in this case, there seems to be no good way to tell
the view model that it should invalidate the old property value.
You can listen to errors within the container that is bound to the view model:
container.AddHandler(Validation.ErrorEvent, Container_Error);
...
void Container_Error(object sender, ValidationErrorEventArgs e) {
...
}
This notifies you when errors are added or removed, and you can identify binding exceptions by whether e.Error.Exception exists, so your view can maintain a list of binding exceptions and inform the view model of it.
But any solution to this problem will always be a hack, because the view is not filling its role properly, which is giving the user a means of reading and updating the view model structure. This should be seen as a temporary solution until you correctly present the user with some kind of "integer box" instead of a text box.
Here's an effort to simplify things if you don't want to implement tons of additional code...
The scenario is that you have an int property in your viewmodel (could be decimal or another non-string type) and you bind a textbox to it in your view.
You have validation in your viewmodel that fires in the setter of the property.
In the view a user enters 123abc and the view logic highlights the error in the view, but can't set the property because the value is the wrong type. The setter never gets called.
The simplest solution is to change your int property in the viewmodel to be a string property, and cast the values into and out of it from the model. This allows the bad text to hit the setter of your property, and your validation code can then check the data and reject it as appropriate.
IMHO validation in WPF is broken, as can be seen from the elaborate (and ingenious) ways people have tried to work around the problem given previously. For me I don't want to add a huge amount of extra code or implement my own type classes to enable a textbox to validate so basing these properties on strings is something I can live with, even if it does feel like a bit of a kludge.
Microsoft should look at fixing this so that the scenario of invalid user input in a textbox bound to an int or decimal property can somehow communicate this fact elegantly to the viewmodel. It should be possible, for instance for them to create a new bound property for a XAML control to communicate view logic validation errors to a property in the viewmodel.
Thanks and respect to the other guys that have provided detailed answers to this topic.
To give some background on what I am doing:
I have a program that allows a user to modify and create a general calibration. Inside this calibration includes groups. Groups differ by the type of analysis they perform. These groups also included spectral sets with each set containing data for only one molecule. Inside these sets also includes spectral data at varying concentrations. Each spectra data is a discrete set of data which is specified by its resolution (x-axis space between each point) and its spectral range (x axis range).
One of the main aspects of building these calibration files is to keep the resolution and spectral range consistent in all spectral data in each set. This means that spectral data cannot be added unless it matches the spectral data of the rest. Also, if the user deletes all spectral data the resolution and range is reset allowing Spectra data of any range or resolution to be added to the calibration.
The question is: How can I provided an effective way to prevent adding spectral data to the calibration that doesn't match the current resolution and spectral range???
Below is a general description of a calibration class. This is a very general description and contains the only info needed to explain what I am trying to do.
class calibration
{
List<Group> groups;
}
class Group
{
List<SpectralSet> sets;
}
class SpectralSet
{
List<SpectraData> spectras;
}
class SpectraData
{
double firstXPoint;
double lastXPoint;
double resolution;
double[] Ypoints;
}
I'm sure you could apply all sorts of fancy design patterns to enforce the rules you mention.
However, for the sake of simplicity, I would just encapsulate the logic in the calibration class with an AddGroup method which validates the added group conforms to the calibration's requirements. Similarly, I would create an AddSpectralSet method to the group class as a gate keeper into the sets collection.
At that point, depending on how often these things change, I would think about exposing the groups collection and sets collection as ReadOnlyCollection to ensure code doesn't try to add items without using the prescribed methods.
Hope this helps.
Your approach will probably vary a bit, but here's an outline of how you could achieve this. You need to do the following:
Only expose immutable public collection properties, along with an Add method, so that you can do your own validation.
For example, you don't want to do:
class Group
{
public List<SpectralSet> sets;
}
Because then anyone can just do myGroup.sets.Add(mySet), without you getting a chance to do any validation on the set. One common pattern to achieve this is as follows:
class Group
{
private List<SpectralSet> _sets;
public IEnumerable<SpectralSet> Sets { get { return _sets; } }
public void Add(SpectralSet set)
{
//Do validation here, throw an exception or whatever you want to do if the set isn't valid
_sets.Add(set);
}
//Have a similar Remove method
}
Store the criteria that the data must match
I'm not quite sure what a spectral range is, so I'll just use the resolution as an example. You can extend this to whatever the range criteria is simply. There's at least three possible ways you could do this:
When you construct a class, pass it the resolution that it's allowed in the constructor, and make this immutable.
When adding and removing, update the allowed resolution as necessary.
Don't store the resolution explicitly, calculate it every time you add or remove.
Out of those, option 1 is by far the simplest. Life is always much easier when you can keep things as immutable as possible. So you'd do something like:
class Group
{
private List<SpectralSet> _sets;
public IEnumerable<SpectralSet> Sets { get { return _sets; } }
public readonly double Resolution;
public Group(double resolution)
{
Resolution = resolution;
}
public void Add(SpectralSet set)
{
if(set.Resolution != resolution)
//Throw an Exception here, or however you want to handle invalid data
_sets.Add(set);
}
//Have a similar Remove method
}
Following this example, each of the classes you included would need a Resolution parameter with the same kind of logic, just checking the Resolution of any direct child you tried to add. Likewise with whatever you use for spectral range.
You also want to be able to change the resolution allowed if all data is cleared. The simplest way to do this is just create new objects whenever the data is cleared, rather than just clearing out the collections in existing objects.
Make SpectraData immutable
All this is useless if you can get a SpectraData out of one of the carefully gated collections and change it arbitrarily. Make anything that needs to be validated immutable, only allowing it to be set on construction. If you have a requirement not to do that, you need to think very carefully about how you will allow it to be changed.
Warning: Very long and detailed post.
Okay, validation in WPF when using MVVM. I’ve read many things now, looked at many SO questions, and tried many approaches, but everything feels somewhat hacky at some point and I’m really not sure how to do it the right way™.
Ideally, I want to have all validation happen in the view model using IDataErrorInfo; so that’s what I did. There are however different aspects that make this solution be not a complete solution for the whole validation topic.
The situation
Let’s take the following simple form. As you can see, it’s nothing fancy. We just have a two textboxes which bind to a string and int property in the view model each. Furthermore we have a button that is bound to an ICommand.
So for the validation we now have a two choices:
We can run the validation automatically whenever the value of a text box changes. As such the user gets an instant response when he entered something invalid.
We can take this one step further to disable the button when there are any errors.
Or we can run the validation only explicitly when the button is pressed, then showing all errors if applicable. Obviously we can’t disable the button on errors here.
Ideally, I want to implement choice 1. For normal data bindings with activated ValidatesOnDataErrors this is default behavior. So when the text changes, the binding updates the source and triggers the IDataErrorInfo validation for that property; errors are reported back the view. So far so good.
Validation status in the view model
The interesting bit is to let the view model, or the button in this case, know if there are any errors. The way IDataErrorInfo works, it is mainly there to report errors back to the view. So the view can easily see if there are any errors, display them and even show annotations using Validation.Errors. Furthermore, validation always happens looking at a single property.
So having the view model know when there are any errors, or if the validation succeeded, is tricky. A common solution is to simply trigger the IDataErrorInfo validation for all properties in the view model itself. This is often done using a separate IsValid property. The benefit is that this can also be easily used for disabling the command. The drawback is that this might run the validation on all properties a bit too often, but most validations should be simply enough to not hurt the performance. Another solution would be to remember which properties produced errors using the validation and only check those, but that seems a bit overcomplicated and unnecessary for most times.
The bottom line is that this could work fine. IDataErrorInfo provides the validation for all properties, and we can simply use that interface in the view model itself to run the validation there too for the whole object. Introducing the problem:
Binding exceptions
The view model uses actual types for its properties. So in our example, the integer property is an actual int. The text box used in the view however natively only supports text. So when binding to the int in the view model, the data binding engine will automatically perform type conversions—or at least it will try. If you can enter text in a text box meant for numbers, the chances are high that there won’t always be valid numbers inside: So the data binding engine will fail to convert and throw a FormatException.
On the view side, we can easily see that. Exceptions from the binding engine are automatically caught by WPF and are displayed as errors—there isn’t even a need to enable Binding.ValidatesOnExceptions which would be required for exceptions thrown in the setter. The error messages do have a generic text though, so that could be a problem. I have solved this for myself by using a Binding.UpdateSourceExceptionFilter handler, inspecting the exception being thrown and looking at the source property and then generating a less generic error message instead. All that capsulated away into my own Binding markup extension, so I can have all the defaults I need.
So the view is fine. The user makes an error, sees some error feedback and can correct it. The view model however is lost. As the binding engine threw the exception, the source was never updated. So the view model is still on the old value, which isn’t what’s being displayed to the user, and the IDataErrorInfo validation obviously doesn’t apply.
What’s worse, there is no good way for the view model to know this. At least, I haven’t found a good solution for this yet. What would be possible is to have the view report back to the view model that there was an error. This could be done by data binding the Validation.HasError property back to the view model (which isn’t possible directly), so the view model could check the view’s state first.
Another option would be to relay the exception handled in Binding.UpdateSourceExceptionFilter to the view model, so it would be notified of it as well. The view model could even provide some interface for the binding to report these things, allowing for custom error messages instead of generic per-type ones. But that would create a stronger coupling from the view to the view model, which I generally want to avoid.
Another “solution” would be to get rid of all typed properties, use plain string properties and do the conversion in the view model instead. This obviously would move all validation to the view model, but also mean an incredible amount of duplication of things the data binding engine usually takes care of. Furthermore it would change the semantics of the view model. For me, a view is built for the view model and not the reverse—of course the design of the view model depends on what we imagine the view to do, but there’s still general freedom how the view does that. So the view model defines an int property because there is a number; the view can now use a text box (allowing all these problems), or use something that natively works with numbers. So no, changing the types of the properties to string is not an option for me.
In the end, this is a problem of the view. The view (and its data binding engine) is responsible for giving the view model proper values to work with. But in this case, there seems to be no good way to tell the view model that it should invalidate the old property value.
BindingGroups
Binding groups are one way I tried to tackle this. Binding groups have the ability to group all validations, including IDataErrorInfo and thrown exceptions. If available to the view model, they even have a mean to check the validation status for all of those validation sources, for example using CommitEdit.
By default, binding groups implement choice 2 from above. They make the bindings update explicitly, essentially adding an additional uncommitted state. So when clicking the button, the command can commit those changes, trigger the source updates and all validations and get a single result if it succeeded. So the command’s action could be this:
if (bindingGroup.CommitEdit())
SaveEverything();
CommitEdit will only return true if all validations succeeded. It will take IDataErrorInfo into account and also check binding exceptions. This seems to be a perfect solution for choice 2. The only thing that is a bit of a hassle is managing the binding group with the bindings, but I’ve built myself something that mostly takes care of this (related).
If a binding group is present for a binding, the binding will default to an explicit UpdateSourceTrigger. To implement choice 1 from above using binding groups, we basically have to change the trigger. As I have a custom binding extension anyway, this is rather simple, I just set it to LostFocus for all.
So now, the bindings will still update whenever a text field changes. If the source could be updated (binding engine throws no exception) then IDataErrorInfo will run as usual. If it couldn’t be updated the view is still able to see it. And if we click our button, the underlying command can call CommitEdit (although nothing needs to be committed) and get the total validation result to see if it can continue.
We might not be able to disable the button easily this way. At least not from the view model. Checking the validation over and over is not really a good idea just to update the command status, and the view model isn’t notified when a binding engine exception is thrown anyway (which should disable the button then)—or when it goes away to enable the button again. We could still add a trigger to disable the button in the view using the Validation.HasError so it’s not impossible.
Solution?
So overall, this seems to be the perfect solution. What is my problem with it though? To be honest, I’m not entirely sure. Binding groups are a complex thing that seem to be usually used in smaller groups, possibly having multiple binding groups in a single view. By using one big binding group for the whole view just to ensure my validation, it feels as if I’m abusing it. And I just keep thinking, that there must be a better way to solve this whole situation, because surely I can’t be the only one having these problems. And so far I haven’t really seen many people use binding groups for validation with MVVM at all, so it just feels odd.
So, what exactly is the proper way to do validation in WPF with MVVM while being able to check for binding engine exceptions?
My solution (/hack)
First of all, thanks for your input! As I have written above, I’m using IDataErrorInfo already to do my data validation and I personally believe it’s the most comfortable utility to do the validation job. I’m using utilities similar to what Sheridan suggested in his answer below, so maintaining works fine too.
In the end, my problem boiled down to the binding exception issue, where the view model just wouldn’t know about when it happened. While I could handle this with binding groups as detailed above, I still decided against it, as I just didn’t feel all that comfortable with it. So what did I do instead?
As I mentioned above, I detect binding exceptions on the view-side by listening to a binding’s UpdateSourceExceptionFilter. In there, I can get a reference to the view model from the binding expression’s DataItem. I then have an interface IReceivesBindingErrorInformation which registers the view model as a possible receiver for information about binding errors. I then use that to pass the binding path and the exception to the view model:
object OnUpdateSourceExceptionFilter(object bindExpression, Exception exception)
{
BindingExpression expr = (bindExpression as BindingExpression);
if (expr.DataItem is IReceivesBindingErrorInformation)
{
((IReceivesBindingErrorInformation)expr.DataItem).ReceiveBindingErrorInformation(expr.ParentBinding.Path.Path, exception);
}
// check for FormatException and produce a nicer error
// ...
}
In the view model I then remember whenever I am notified about a path’s binding expression:
HashSet<string> bindingErrors = new HashSet<string>();
void IReceivesBindingErrorInformation.ReceiveBindingErrorInformation(string path, Exception exception)
{
bindingErrors.Add(path);
}
And whenever the IDataErrorInfo revalidates a property, I know that the binding worked, and I can clear the property from the hash set.
In the view model I then can check if the hash set contains any items and abort any action that requires the data to be validated completely. It might not be the nicest solution due to the coupling from the view to the view model, but using that interface it’s at least somewhat less a problem.
Warning: Long answer also
I use the IDataErrorInfo interface for validation, but I have customised it to my needs. I think that you'll find that it solves some of your problems too. One difference to your question is that I implement it in my base data type class.
As you pointed out, this interface just deals with one property at a time, but clearly in this day and age, that's no good. So I just added a collection property to use instead:
protected ObservableCollection<string> errors = new ObservableCollection<string>();
public virtual ObservableCollection<string> Errors
{
get { return errors; }
}
To address your problem of not being able to display external errors (in your case from the view, but in mine from the view model), I simply added another collection property:
protected ObservableCollection<string> externalErrors = new ObservableCollection<string>();
public ObservableCollection<string> ExternalErrors
{
get { return externalErrors; }
}
I have an HasError property which looks at my collection:
public virtual bool HasError
{
get { return Errors != null && Errors.Count > 0; }
}
This enables me to bind this to Grid.Visibility using a custom BoolToVisibilityConverter, eg. to show a Grid with a collection control inside that shows the errors when there are any. It also lets me change a Brush to Red to highlight an error (using another Converter), but I guess you get the idea.
Then in each data type, or model class, I override the Errors property and implement the Item indexer (simplified in this example):
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this["Name"]);
errors.AddUniqueIfNotEmpty(this["EmailAddresses"]);
errors.AddUniqueIfNotEmpty(this["SomeOtherProperty"]);
errors.AddRange(ExternalErrors);
return errors;
}
}
public override string this[string propertyName]
{
get
{
string error = string.Empty;
if (propertyName == "Name" && Name.IsNullOrEmpty()) error = "You must enter the Name field.";
else if (propertyName == "EmailAddresses" && EmailAddresses.Count == 0) error = "You must enter at least one e-mail address into the Email address(es) field.";
else if (propertyName == "SomeOtherProperty" && SomeOtherProperty.IsNullOrEmpty()) error = "You must enter the SomeOtherProperty field.";
return error;
}
}
The AddUniqueIfNotEmpty method is a custom extension method and 'does what is says on the tin'. Note how it will call each property that I want to validate in turn and compile a collection from them, ignoring duplicate errors.
Using the ExternalErrors collection, I can validate things that I can't validate in the data class:
private void ValidateUniqueName(Genre genre)
{
string errorMessage = "The genre name must be unique";
if (!IsGenreNameUnique(genre))
{
if (!genre.ExternalErrors.Contains(errorMessage)) genre.ExternalErrors.Add(errorMessage);
}
else genre.ExternalErrors.Remove(errorMessage);
}
To address your point regarding the situation where a user enters an alphabetical character into a int field, I tend to use a custom IsNumeric AttachedProperty for the TextBox, eg. I don't let them make these kinds of errors. I always feel that it's better to stop it, than to let it happen and then fix it.
Overall I'm really happy with my validation ability in WPF and am not left wanting at all.
To end with and for completeness, I felt that I should alert you to the fact that there is now an INotifyDataErrorInfo interface which includes some of this added functionality. You can find out more from the INotifyDataErrorInfo Interface page on MSDN.
UPDATE >>>
Yes, the ExternalErrors property just let's me add errors that relate to a data object from outside that object... sorry, my example wasn't complete... if I'd have shown you the IsGenreNameUnique method, you would have seen that it uses LinQ on all of the Genre data items in the collection to determine whether the object's name is unique or not:
private bool IsGenreNameUnique(Genre genre)
{
return Genres.Where(d => d.Name != string.Empty && d.Name == genre.Name).Count() == 1;
}
As for your int/string problem, the only way I can see you getting those errors in your data class is if you declare all your properties as object, but then you'd have an awful lot of casting to do. Perhaps you could double your properties like this:
public object FooObject { get; set; } // Implement INotifyPropertyChanged
public int Foo
{
get { return FooObject.GetType() == typeof(int) ? int.Parse(FooObject) : -1; }
}
Then if Foo was used in code and FooObject was used in the Binding, you could do this:
public override string this[string propertyName]
{
get
{
string error = string.Empty;
if (propertyName == "FooObject" && FooObject.GetType() != typeof(int))
error = "Please enter a whole number for the Foo field.";
...
return error;
}
}
That way you could fulfil your requirements, but you'll have a lot of extra code to add.
In my opinion, the problem lies in validation happening at too many places. I also wished to write all my validation login in ViewModel but all those number binding were making my ViewModel crazy.
I solved this problem by creating a binding that never fails. Obviously, if a binding is always successful then the type itself has to handle the error conditions gracefully.
Failable Value Type
I started by creating a generic type which would gracefully support the failed conversions:
public struct Failable<T>
{
public T Value { get; private set; }
public string Text { get; private set; }
public bool IsValid { get; private set; }
public Failable(T value)
{
Value = value;
try
{
var converter = TypeDescriptor.GetConverter(typeof(T));
Text = converter.ConvertToString(value);
IsValid = true;
}
catch
{
Text = String.Empty;
IsValid = false;
}
}
public Failable(string text)
{
Text = text;
try
{
var converter = TypeDescriptor.GetConverter(typeof(T));
Value = (T)converter.ConvertFromString(text);
IsValid = true;
}
catch
{
Value = default(T);
IsValid = false;
}
}
}
Note that even if the type fails to initialise because of invalid input string (second constructor), it quietly stores the invalid state along with invalid text also. This is required in order to support the round-trip of binding even in case of wrong input.
Generic Value Converter
A generic value converter could be written using above type:
public class StringToFailableConverter<T> : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(Failable<T>))
throw new InvalidOperationException("Invalid value type.");
if (targetType != typeof(string))
throw new InvalidOperationException("Invalid target type.");
var rawValue = (Failable<T>)value;
return rawValue.Text;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(string))
throw new InvalidOperationException("Invalid value type.");
if (targetType != typeof(Failable<T>))
throw new InvalidOperationException("Invalid target type.");
return new Failable<T>(value as string);
}
}
XAML Handy Converters
Since creating and using the instances of generics is pain in XAML, lets make static instances of common converters:
public static class Failable
{
public static StringToFailableConverter<Int32> Int32Converter { get; private set; }
public static StringToFailableConverter<double> DoubleConverter { get; private set; }
static Failable()
{
Int32Converter = new StringToFailableConverter<Int32>();
DoubleConverter = new StringToFailableConverter<Double>();
}
}
Other value types can be extended easily.
Usage
Usage is pretty simple, just need to change the type from int to Failable<int>:
ViewModel
public Failable<int> NumberValue
{
//Custom logic along with validation
//using IsValid property
}
XAML
<TextBox Text="{Binding NumberValue,Converter={x:Static local:Failable.Int32Converter}}"/>
This way, you can use the same validation mechanism (IDataErrorInfo or INotifyDataErrorInfo or anything else) in ViewModel by checking the IsValid property. If IsValid is true, you can directly use the Value.
Ok, I believe I have found the answer you were looking for...
It will not be easy to explain - but..
Very easy to understand once explained...
I do think It is most accurate/"certified" to MVVM viewed as "standard" or at the least attempted standard.
But before we begin.. you need to change a concept which you got used to regarding MVVM:
"Furthermore it would change the semantics of the view model. For me,
a view is built for the view model and not the reverse—of course the design
of the view model depends on what we imagine the view to do,
but there’s still general freedom how the view does that"
That paragraph is the source of your problem.. - why?
Because you are stating the View-Model has no role to adjust itself to the View..
That is wrong in many ways - as I'll prove to you very simply..
If you have a property such as:
public Visibility MyPresenter { get...
What is Visibility if not something that serves the View?
The type itself and the name that will be given to the property is definitely made up for the view.
There two distinguishable View-Models categories in MVVM according to my experience:
Presenter View Model - which is to be hooked to buttons, menus, Tab Items etc....
Entity View Model - which is to be hocked to controls that brings the entity data to screen.
These are two different - completely different concerns.
And now to the solution:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class VmSomeEntity : ViewModelBase, INotifyDataErrorInfo
{
//This one is part of INotifyDataErrorInfo interface which I will not use,
//perhaps in more complicated scenarios it could be used to let some other VM know validation changed.
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
//will hold the errors found in validation.
public Dictionary<string, string> ValidationErrors = new Dictionary<string, string>();
//the actual value - notice it is 'int' and not 'string'..
private int storageCapacityInBytes;
//this is just to keep things sane - otherwise the view will not be able to send whatever the user throw at it.
//we want to consume what the user throw at us and validate it - right? :)
private string storageCapacityInBytesWrapper;
//This is a property to be served by the View.. important to understand the tactic used inside!
public string StorageCapacityInBytes
{
get { return storageCapacityInBytesWrapper ?? storageCapacityInBytes.ToString(); }
set
{
int result;
var isValid = int.TryParse(value, out result);
if (isValid)
{
storageCapacityInBytes = result;
storageCapacityInBytesWrapper = null;
RaisePropertyChanged();
}
else
storageCapacityInBytesWrapper = value;
HandleValidationError(isValid, "StorageCapacityInBytes", "Not a number.");
}
}
//Manager for the dictionary
private void HandleValidationError(bool isValid, string propertyName, string validationErrorDescription)
{
if (!string.IsNullOrEmpty(propertyName))
{
if (isValid)
{
if (ValidationErrors.ContainsKey(propertyName))
ValidationErrors.Remove(propertyName);
}
else
{
if (!ValidationErrors.ContainsKey(propertyName))
ValidationErrors.Add(propertyName, validationErrorDescription);
else
ValidationErrors[propertyName] = validationErrorDescription;
}
}
}
// this is another part of the interface - will be called automatically
public IEnumerable GetErrors(string propertyName)
{
return ValidationErrors.ContainsKey(propertyName)
? ValidationErrors[propertyName]
: null;
}
// same here, another part of the interface - will be called automatically
public bool HasErrors
{
get
{
return ValidationErrors.Count > 0;
}
}
}
And now somewhere in your code - your button command 'CanExecute' method can add to its implementation a call to VmEntity.HasErrors.
And may peace be upon your code regarding validation from now on :)
The drawback is that this might run the validation on all properties a
bit too often, but most validations should be simply enough to not
hurt the performance. Another solution would be to remember which
properties produced errors using the validation and only check those,
but that seems a bit overcomplicated and unecessary for most times.
You don't need to track which properties have errors; you only need to know that errors exist. The view model can maintain a list of errors (also useful for displaying an error summary), and the IsValid property can simply be a reflection of whether the list has anything. You don't need to check everything each time IsValid is called, as long as you ensure that the error summary is current and that IsValid is refreshed each time it changes.
In the end, this is a problem of the view. The view (and its data
binding engine) is responsible for giving the view model proper values
to work with. But in this case, there seems to be no good way to tell
the view model that it should invalidate the old property value.
You can listen to errors within the container that is bound to the view model:
container.AddHandler(Validation.ErrorEvent, Container_Error);
...
void Container_Error(object sender, ValidationErrorEventArgs e) {
...
}
This notifies you when errors are added or removed, and you can identify binding exceptions by whether e.Error.Exception exists, so your view can maintain a list of binding exceptions and inform the view model of it.
But any solution to this problem will always be a hack, because the view is not filling its role properly, which is giving the user a means of reading and updating the view model structure. This should be seen as a temporary solution until you correctly present the user with some kind of "integer box" instead of a text box.
Here's an effort to simplify things if you don't want to implement tons of additional code...
The scenario is that you have an int property in your viewmodel (could be decimal or another non-string type) and you bind a textbox to it in your view.
You have validation in your viewmodel that fires in the setter of the property.
In the view a user enters 123abc and the view logic highlights the error in the view, but can't set the property because the value is the wrong type. The setter never gets called.
The simplest solution is to change your int property in the viewmodel to be a string property, and cast the values into and out of it from the model. This allows the bad text to hit the setter of your property, and your validation code can then check the data and reject it as appropriate.
IMHO validation in WPF is broken, as can be seen from the elaborate (and ingenious) ways people have tried to work around the problem given previously. For me I don't want to add a huge amount of extra code or implement my own type classes to enable a textbox to validate so basing these properties on strings is something I can live with, even if it does feel like a bit of a kludge.
Microsoft should look at fixing this so that the scenario of invalid user input in a textbox bound to an int or decimal property can somehow communicate this fact elegantly to the viewmodel. It should be possible, for instance for them to create a new bound property for a XAML control to communicate view logic validation errors to a property in the viewmodel.
Thanks and respect to the other guys that have provided detailed answers to this topic.
Currently in my app I use the func/lambda method of showing message boxes as explained in the url below:
http://www.deanchalk.me.uk/post/WPF-MVVM-e28093-Simple-e28098MessageBoxShowe28099-With-Action-Func.aspx
To pass the message box text and caption is not a problem, however I also want to pass the image box image and image box type (yes/no etc). These are WPF enumerations. Currently I wrote a few methods to convert those enums into non WPF (own made) enums but it feels a bit tedious to copy every value.
Is it acceptable to use WPF enumerations in ViewModel? (I guess not). And if not, how can I prevent them to be used and still select them in the ViewModel?
I'm slightly confused with your terms ModelView, and ViewModel. With MVVM, there is just the model, the view, and the view model.
That article is talking about abstracting the message box so that you can run unit tests without blocking the build server while it waits for user interaction.
The implementation uses the Func delegate, but you could do this just as easily using an interface. An approach then would be to create your own enumerations, and then convert them for the MessageBox implementation of the interface.
E.g.
public enum ConfirmationResult
{
Yes,
No,
Cancel
..etc
}
public enum ConfirmationType
{
YesNo,
OkCancel
..etc
}
public interface IConfirmation
{
ConfirmationResult ShowConfirmation(string message, ConfirmationType confirmationType)
}
public class MessageBoxConfirmation : IConfirmation
{
ConfirmationResult ShowConfirmation(string message, ConfirmationType confirmationType)
{
// convert ConfirmationType into MessageBox type here
// MessageBox.Show(...)
// convert result to ConfirmationResult type
}
}
Your view models would then take an IConfirmation as a dependency (in their constructor for example), and in unit tests, you can stub the IConfirmation interface to always return a particular result from the ShowConfirmation method.
You could also overload the ShowConfirmation method to provide options for images, window titles etc.
As mentioned in a previous post, I am currently learning C#, and for my first (well second if you include hello world and some messing about) real project, I'm going to create a basic EPOS system and then expand upon it..
My intention is to have all pricing, Cashier details, sales transactions, etc.. stored within a MySQL Database (I can move from PHP to C#, but I can't bring myself to moving away from MySQL!)
What I would like to check is the following:
I have 2 forms(at present)
A login screen (where the user enters their PIN number)
The main Sales screen.
When the user logs in, I am going to authenticate them against the details held within my database, and if successful, then I will load the main sales screen.
I need to somehow transfer information around between forms, so what I have done is created a new class file called variableStore.cs in here, I am going to be storing the userID, userName (so I can have "Currently Logged in: John Doe" on each form status bar.
But I will also be using the same file for transferring "transaction total", etc.. around between forms.
Is this the correct (ok, not correct, but is this how you would do it?) way of doing things?
I would not do this way you said. Is this a web application?
You should keep the user information and the business layer information separately. This will give you enough freedom to change the user information in the future if required.
For passing the transaction total, you should query the information from the database and show it to the web page. Can you do this?
You can also pass data using the following:-
1. constructor
2. objects
3. properties
4. Delegates
When talking about authentication and authorization, you should create your own implementation of IPrincipal and IIdentity, which are then set within the Thread (Thread.CurrentThread).
The IPrincipal goes on the Thread and it holds onto your IIdentity. Your implementation can be queried to determine if the user is authenticated and what the user is allowed to do (see IsInRole).
As for using global variables to store information, that is not a good idea. However, you're starting out, so it might be good for you.
Alternatively, you could create public properties on your Forms where you set and, after the form is closed, get the results of user action on the form (don't forget, a form is an object just like anything else). So if form A instantiates form B, shows B modally, then gets control back after form B is closed, form A could then get the results (whatever they are) of user interaction from the properties on B.
public class B : Form
{
// Set when the user completes a sale and closes the form with the OK button.
public SaleItem Sale{ get; private set;}
}
This is much better than using a global store for variables. If I were doing this, I'd use a dependency injection framework like Unity to get instances of classes that provide for functions such as sales within my application.
No, that is not how I would handle things.
A much better way of handling is passing the values between forms either through the constructor, properties, or methods.
Constructor
public class SalesForm : Form
{
public SalesForm(string userName)
{
Text = "Currently Logged In: " + userName;
}
}
Now, on the Login Form button click handler (after authenticating the user):
var mainForm = new SalesForm(userName);
mainForm.Show();
Property
public class SalesForm : Form
{
private string _userName = null;
public string UserName
{
get
{
return _userName;
}
set
{
_userName = value;
if(!String.IsNullOrEmpty(_userName))
Text = "Currently Logged In: " + _userName;
}
}
}
And again, on the Login Form button click handler:
var mainForm = new SalesForm();
mainForm.UserName = userName;
mainForm.Show();
Method
public class SalesForm : Form
{
public void SetTitle(string userName)
{
Text = "Currently Logged In: " + userName;
}
}
And:
var mainForm = new SalesForm();
mainForm.SetTitle(userName);
mainForm.Show();
It is normally preferred to use the IServiceProvider pattern, but in small and trivial cases, static would be OK.
For a small, simple application when you're just getting started, static variables can help get things running, but it's not a habit you want to get into. You might start by writing down all of your state variables in one place, but then step back and take the time to think about where you actually use them. If some of the variables are only used in one small part of the program, try to think of ways to hide them from the rest of the program, such as by putting them in a class that only that part of the program is given an instance of. As you write your program, take breaks periodically, look at your classes and think about the following:
Encapsulation : Is my class doing a well-defined job or is it trying to do all sorts of different things? If you can't immediately figure out what the job-description of the class is, then it's probably doing too much. Consider breaking the functionality off into smaller pieces that you can work on individually.
Insulation: Is my class exposing information that other classes shouldn't need to know about it? I should only tell other classes what they need in order for them to use me for my job. I also need to prevent other classes from interfering with my job as best I can. If other classes use me incorrectly, I should complain loudly (throw exceptions) so that the developer realizes that something is wrong.
Non-repetition: Do I have pieces of code that look very similar, with only slight modifications between them? Try to find a way to consolidate these pieces of code into a single method and call that method from each location. If you ever find yourself fixing a bug and having to go to multiple locations in the code to apply the fix, this is a sign that you're repeating yourself. Invest the time to fix this and it will be beneficial in so many ways in the future.
These principles are all designed to help manage complexity, by helping you to understand your code and by limiting the possibility for bugs by minimizing the number of possible interactions between portions of code. Enjoy learning. :)