This is somewhat of a followup to my previous question, where people pointed me in the direction of MVVM.
I'm trying to understand exactly where the work is supposed to go in this framework. My view contains a textbox into which the user is meant to input a URI.
As far as I see, I have two choices:
Bind to a Uri object in my ViewModel, using a converter and validator to check if the URI is valid and convert it if it is. The ViewModel then ends up with either a valid Uri, or DependencyProperty.UnsetValue. (I am using something like this as a combined converter/validator; is this good MVVM practices?)
Bind to a string in my ViewModel, and do the conversion/validation as necessary for the ViewModel's code. I'm not entirely sure what the code is for having the ViewModel tell the view that the URI-string is invalid, though, and displaying appropriate validation errors.
I guess generally the question is about how and where to handle potentially invalid data in the MVVM framework. This doesn't seem to be covered in any of the basic introductions to MVVM that I've been browsing through. Thanks for your help in getting this all straight in my mind :).
In my opinion, you should have your validation framework, validate the input from the user, once it's confirmed as valid, is should be bound by a converter to a Uri property on the ViewModel.
It all depends how you setup your validation, but I would suggest that your validation should come before properties being set on the ViewModel.
Hope that helps!
Related
I have a DevExpress DxGrid bound to an ObservableCollection of viewmodels (based on SimpleMvvmToolkit).
The viewmodel has 2 properties exposed by itself (a string and a boolean) and a few other properties are exposed by its base class (ViewModelDetailBase), one of them is the model behind the viewmodel.
Everytime I use the grid to modify the contents of one of the properties (e.g. the boolean value), I get an error saying "The type xxx cannot be serialized.." (xxx is the type of the Model) followed by the suggestion to use DataContractAttribute to circomvent the issue.
I am not sure how and where to look for a solution. Maybe I should read up on it, but why is serialization needed here??
Anyway, I hope you can shed a light on this. I'd appreciate some pointers to get me looking in the right direction.
edit: Since the situation is too intricate to post the relevant code here, I made a sandbox project that reproduces the error. You can find it via this WeTransfer link.
Best regards,
~Rob
Thanks to great help of the Simple MVVM Toolkit community I found out that the solution was simple.
In Simple MVVM Toolkit, A viewmodel needs to be serializable because it gets cloned. This is to easily roll-back data when an action is canceled. The reason my viewmodel could not be cloned whas because its "model" property was missing a default (parameter-less) constructor.
There's no need to decorate the viewmodel and its properties with [DataContract] or [DataMember].
I hope this helps others.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
So I'm fairly new to MVVM and I have been struggling with a few issues regarding user input validation. WPF has some built-in features that seem to work like "magic", and generally, I know "magic" is not good.
For Example: If you bind a TextBox to a Property that has a type of double and the user enters "hello" into that TextBox, WPF automatically displays a red border around the TextBox notifying the user that the input is invalid.
This is all well and good, but it does seem like "magic". I was told by an experienced developer that WPF and app builders that are similar want to have too much control. He said that in web development the View would not know what type the Property is. Which makes sense to me. So this leads me to my general question - Should a WPF View understand property types? - If instead I declared the Property type as a string I could then have complete control over the view. Instead of having to work around WPF's "smart" TextBox "magic".
An alternative way to phrase my question is - Should Property types be declared in the Model or ViewModel?
I understand that if you declare the Property type in the Model as a double and as a string in the ViewModel it must be parsed in the Model. In most examples of MVVM applications I have looked at, the Property types are similar across the entire application, but I think that a "dumb" view that doesn't understand what it is working with would be much better.
Back to my example: If the Property were declared as a String you could completely control the format necessary for input and prevent invalid input all-together. This seems like a much better solution than trusting the WPF TextBox.
Yes, I think that feature of WPF is "that nice" :)
WPF has two layers: a data layer and a UI layer.
The data layer contains your data. If you have a number in your data, it should be of a numeric data type, not a string data type.
The UI layer (XAML) is only meant to provide a user-friendly interface for the data so that users can easily interact with the data layer. For example, if you have a data layer containing a numeric value and you want the user to be able to edit that value, you might choose to display your number using a TextBox.
If you force your data layer to use a string instead of a number just because the UI layer is using a TextBox to display the data, then you are letting the UI control your application, which is not how WPF should work. In addition, blending the two layers together like this makes it much harder to maintain in the future. For example, what happens if you decide to change the TextBox to a NumericUpDown UI control? Now you have to go modify your data layer to make a UI change.
In regards to your particular example, when you bind a TextBox to a double and type in a string like "hello", what actually occurs is that WPF tries to set your double value to a string value, and that throws an exception.
That exception is what causes the red border to appear around the TextBox and an error message to display, not any special handling. You could just as easily throw an exception in the setter for your double property, and the same thing would occur.
I wouldn't really call that "magic", just exception handling :)
But to avoid having to throw exceptions anytime you want to validate input, WPF provides the IDataErrorInfo interface which you can use to validate your property without throwing exceptions. The UI will react to errors raised with this interface the same way it would react to Exceptions.
Yes it should.
Karl Shifflett has a great article about it. Input Validation – UI Exceptions & Model Validation Errors
The solution is to detect invalid input. Data binding pipeline will throw exception when user input invalid data type. When it happens handle it on View then add error messages on ViewModel so that you could use it when needed.
WPF by default swallow that data binding exception, but you could add a handler on Loaded event in your View class.
_errorEventRoutedEventHandler = new RoutedEventHandler(ExceptionValidationErrorHandler);
this.AddHandler(System.Windows.Controls.Validation.ErrorEvent, _errorEventRoutedEventHandler, true);
Implement the handler
private void ExceptionValidationErrorHandler(object sender, RoutedEventArgs e)
{
// Add logic to handle this invalid data type exception.
// Add error messages to viewmodel, show notification dialog, etc
...
}
Set these properties on XAML binding NotifyOnValidationError, ValidatesOnDataErrors, ValidatesOnExceptions to true.
Text="{Binding UnitPrice, StringFormat=c, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"
That's it.
Since you´re using a strong typed language, you're going to need validation sooner or later. I'm not sure what you would want to achieve by not using a form of validation that is already at hand. Don't forget that you can modify your view to do what ever you want when an invalid input is given. If you don't want any exceptions thrown (as explained by #Rachel), using a string property will likely prevent them.
Declaration
Properties connected to your database are declared in the Model. Properties you need to transform the Model into something userfriendly, are declared in the ViewModel. For example, we have a value (valueA) that is stored in the database. valueA is calculated by using two inputfields the user has available (valueB and valueC). In this case valueA is declared in your Model, but valueB and valueC are only declared in your ViewModel, since they don't need to be stored in the database. (Technically all three are available in your ViewModel but only valueA is declared in your Model)
The way I understand it:
Model Has properties that are stored in the database
ViewModel Converts the Model to something a user can handle (and vice versa).
View Is more or less the 'input area for your ViewModel' in which the user is aided by the use of graphics.
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.
I struggle to understand the purpose of the ViewModel sometimes, especially with respect to what it should and shouldn't do.
So... I find myself wanting to expose a display string from my ViewModel. Ok, actually it's a Date string, where I want to display something like "Unknown" if the date isn't set. Really I want a solution to cover the general case, so I don't want a discussion about using null and fallback values. It could equally be a status message like "Awaiting Dispatch" or "Launch Aborted".
So the quesion is, how should the ViewModel expose display strings to the View. The App will need to be localized, so we can't have hard coded strings in the ViewModel.
Is it ok to access the app's resources from the ViewModel, and return the display string?
As an aside, should I be using a resx string resource, or a Xaml resource dictionary? I'm leaning toward the Xaml. Advantages/Disadvantages?
Thanks,
Mark
As mentioned in the other answers, this is clearly the view's responsibility. The view model should expose the data property (a status enum or something similar) for the view to inspect in order to determine the particulars of the display message. From a purist's perspective, generating display properties from the VM or code behind is right out.
I'm surprised to hear no suggestions of datatriggers, though. Use them in your TextBlock to bind to the exposed status enum value and update the text appropriately. This question compares their performance against binding to a converter and suggests that triggers will perform better in this scenario. I haven't done any testing myself, so I'm not sure, but it seems intuitively reasonable to me, provided you aren't listening for an outrageous number of different enum values. I also think it's the more appropriate method in this case, and I'll cite this question asking for a comparison of VC vs. DT use cases, though I don't think it's terribly contentious. To put it another way, if one holds that a VM is a value converter on steroids (a statement which I'm very leery of and will not agree or disagree with at the moment), then this argument holds: a VM shouldn't be exposing display properties; a VM's purpose and abilities are a super-set of those of a VC; therefore, a VC should not be exposing display properties.
I'm doing my best to give the purist's answer (or what I think it is, at least), but there are certainly easier ways to code it than writing a lot of datatriggers, especially if you have many different values to address.
Regarding your resource question, have a look at this MSDN article on resource files, which is, in certain ways, easier to work with than a resx file. Which approach you should use depends on your use cases.
What you are doing is taking data that is inside the system and converting it to a format that makes it more useful and clear to the user. This is the responsibility of the view. It would be the responsibility of the view to format a valid date into the correct culture and it has the same responsibility for any text that you wish to display.
In the same respect the status examples you give would probably be stored as an enum in the viewmodel (since that would make it easier to apply business logic to them, which is the role of the viewmodel) and it would be the responsibility of the view to display the values in a way that works for the user in all respects. This would include size, colour, font, position and culture.
That being said though I have been known to put display logic in my viewmodel (concatenating firstname and surname for example) simply because it is easier. I've even been known to use code-behind (shock, horror!) where it suits my purpose. There is a sliding scale between purity and pragmatism, and it is up to you where you sit on that line.
To display the string Unknown when the date is not set is a display issue and therefore has to be solved in the view. Even "Awaiting dispatch" is a presentation of a value.
So the answer is: The view model should never expose display strings. The view model has to expose a value that leads the view/a converter/whatever object from the presentation layer to choose a display string.
Even if this answer is not the answer you want to read, it is the answer an MVVM purist has to give. And as you asked for MMVM purists, here it is.
For the date, I would have the ViewModel export a nullable DateTimeOffset, where the null value means "not set". The in the View (via a converter or similar), the null value is displayed as whatever you need.
As to the aside: I would use resx. The reason is that there is a defined fallback mechanism when strings aren't available in the language.
An approach I have taken was creating Culture specific Resources and adding instances (singletons?) of these to the ViewModel.
Then the View can simply Bind to the ViewModel.Resource.DisplayString
See this article for Windows Phone 7 which easily translates to WPF.
If you dislike this because you do not want to tie the culture specific resource to the ViewModel and want the View to solve it you could use/write a ValueConverter that turns a value from a VM property into a display string.
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.