Complex conditional ValidationRule in XAML - wpf

Is there a way to create a validation rule which has bindable boundaries?
Creating dependencyproperties requires the class in question to be a dependencyobject, which neither ValidationRule nor ValidationAttributes are.
What I want is something similar to this:
<Binding.ValidationRules>
<c:AgeRangeRule
Min="{Binding CalculatedMinAge}"
Max="{Binding CalculatedMaxAge}"/>
</Binding.ValidationRules>
Where both CalculatedMinAge and CalculatedMaxAge are properties on my ViewModel which gets calculated base on some other user input in the same dialog (and thus may change, rendering the age, which the user already entered, invalid).
Is this possible to do?
Is there any other way to obtain the result which would come from this?
I need the solution to be awailable in WPF, C#4.
PS: The example is a thought up example. My real world example requires knowledge of domain. For simplicity, lets say that CalculatedMinAge and CalculatedMaxAge depends on which product the user wants to buy.

Related

Is there a concise way of applying Binding.ValidationRules in XAML

I'm working on the validation rules for some data input forms in WPF/XAML. I've been able to get the user experience I would like, by explicitly applying validation rules to the field bindings:
<Binding
Path="qtyoffset"
NotifyOnValidationError="True"
ValidatesOnDataErrors="True"
UpdateSourceTrigger="PropertyChanged"
>
<Binding.ValidationRules>
<utility:DecimalValidationRule precision="1" />
</Binding.ValidationRules>
</Binding>
With the above, the field is validated on every key press. If the user enters a 'X', the field is immediately flagged as invalid, the error message shows up in the appropriate place, the "commit" button is immediately disabled, etc. All nice and slick. Same thing happens if the user enters two digits after the decimal point, enters two decimal points, etc.
My only problem is the verbosity. The above binding code needs to be provided for every field, and if it's not quite right on one field, that one field will work not quite right. I'd much rather specify only the binding path, and have the rest of it added automatically. Set NotifyOnValidationError, ValidatesOnDataErrors, and UpdateSourceTrigger on every binding, set whichever validation rules are appropriate for the specific data type, depending on the type it is bound to. Or, at least, according to the type I specify in XAML.
I'm thinking about the way I would do validation in JQuery. Rather than statically listing all the validation rules on each input element, I'd set a number of classes. And then, on load, I'd use JQuery's DOM search capabilities to find every input element with a specific class set, and dynamically add the appropriate validation functionality.
XAML provides a very nice way of providing this sort of concise configuration for display elements, using Styles and Setters. But that doesn't work for Bindings.
Is there a reasonable alternative?
Short answer: No, not really.
However you can do 2 things:
You can put
NotifyOnValidationError="True"
ValidatesOnDataErrors="True"
UpdateSourceTrigger="PropertyChanged"
in resources and access them as StaticResource, to factor out redundant information, and then you could change all of those properties in one place.
You can make your own markup extension based on Binding. That would make a much shorter XAML declaration. And you could use it like: <TextBox Text={local:DecimalBinding Path=qtyoffset} />
HTH,
Bab.
This would probably be overkill for your needs, but one thing you could do is to subclass the WPF UserControl, and then write a function when the UserControl is loaded and also when new bindings are applied, that walks down the visual tree looking for Bindings of the relevant types and applies the validation to them.

Attention MVVM Purists: Should a ViewModel access localized resources?

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.

WPF DataGrid with textbox in template column not firing property changed

I have a datagrid with a template column, and in that template is a textbox. It's bound to the 'Quantity' property of the object in the collection that makes up itemssource. Quantity is an integer. When I add an item to the datagrid, I am adding an event handler to the PropertyChanged event of the item:
EnteredPart.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(p_PropertyChanged);
This works fine when the user enters an integer in the textbox... the p_PropertyChanged handler fires and I can disable buttons and alter quantities as necessary. When the user enters a non-integer, the handler doesn't get called, I just get a red outline around the textbox. Because of this, I can't disable the necessary buttons when I need to (they should be disabled with the quantity is not legit.) Is there any way that I can do something about this?
EDIT: I tried changing the Quantity property to a string, and this caused the property changed handler to be called when non-integral values are entered. However, I then added validation to the textbox to check for this, and if the Validate method returns false, the property changed handler once again ceases to be hit. Is there any way at all to get both validation and property changed notifications??
EDIT 2: Here another instance of this problem I'm having, in another location. I have a form for adding/editing phone numbers. The phone number textbox looks like this:
<TextBox >
<TextBox.Text>
<Binding Path="Phone.Number">
<Binding.ValidationRules>
<local:PhoneValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Upon clicking a save button, I need to check in my viewmodel if the number is valid, because if it's not, I don't want to run the save command. However, it doesn't seem as if there's any way to do that, because if the validation fails, then the Phone.Number property has a null value, and I have no way to check to see if I should run the save command. I either need access to the error state (which I thought would work by Validation.GetErrors, but doesn't), or to the actual text within the textbox, which isn't available in the viewmodel.
Just for the record, validation in the View part has many drawbacks if you are in an MVVM architecture.
You'd try here to check your model's content, in the view: you'd therefore break MVVM's architecture by calling your model in the view.
Using IDataErrorInfo will help you to fulfill MVVM's main objective (ie. clearly separate the three parts).
Just an example:
I think here you are just performing a very small validation (just check if it is an int or not).
But in a different environment, assume that your model is much more complicated and needs a deeper validation. Using IDataErrorInfo will help you to check deeply in your model without calling it from the view.
As a matter of fact, in my personal experience, as I regularly work with large and highly correlated datasets, I cannot even picture using validation without IDataErrorInfo because it'd cost me too much to investigate in all the data presented and find potential errors

Pros and cons of having a WPF specifics in the view model

I'm having trouble deciding what to think about this piece of code:
public SolidColorBrush Brush
{
get { return IsValid ? _validItemBrush : _invalidItemBrush; }
}
It is part of a view model in my current project and as you can imagine, the Brush will be bound to some text elements in the UI, to indicate (in-)validity of other pieces of data, in an otherwise fairly simple and straightforward dialog.
The proponents of this piece of code say that since we're using WPF, we might as well allow for some simple WPF specific constructs in the view model.
The opponents say that this violates Separation of Concerns, as it clearly dictates style which should be taken care of solely by the view.
Please share your arguments, and if you're not happy with the code above, please share your ideas around alternative solutions. (I'm particularly interested in what you have to say about using DataTemplates).
Is it possible that there is one solution that could be considered best practice?
Personally, I would have the two brushes be defined in XAML, and have the controls that use them switch brushes (in xaml) based on the IsValid property. This could be done very easily with DataTriggers, or even a single IValueConverter - the converter could take 2 brushes and a boolean and swap between them fairly easily.
This keeps the business logic presentation-neutral - a "Brush" is very specific to a specific form of presentation, and a pure View choice. Hard-coding this into the ViewModel violates the single responsibility principle as well as is not a clean separation of concerns.
I would very much keep this in the View, and switch based on the IsValid (bound) property that is ViewModel specific.
While there are circumstances where I might use WPF constructs in the view model, this isn't one of them. Here's why:
It's harder to change. If you define brushes as resources and use them in styles, changing your application's color scheme can simply be a matter of loading a different resource dictionary. If you hard-code color values in your view models, you have a lot of different things to change if it turns out your end users need different colors.
It's harder to test. If you want to write a unit test that checks to see if a property is returning the right brush, you have to create a brush in your unit test and compare the values of the two, since it's a reference type.
In many, maybe even most cases, it doesn't make the code simpler or easier to maintain. You're pretty likely to already be using a style (assuming that you are conversant with styles), since they make just about everything in WPF easier. Binding IsValid to brush colors is just a matter of adding a DataTrigger to a style. Which is where anyone maintaining this code would expect to find it.
There are certainly times when I do use WPF constructs in the view model - for instance, long ago stopped wondering if it was problem if a view model exposed a property of type Visibility. Note that none of the above concerns apply to that case.
In cases like yours where it's purely aesthetic I use Triggers or the Visual State Manager to change colors.
Sometimes I do use colors in my ViewModels, but only if its part of my software spec (e.g., the color of the chart displaying a patient's CO2 depends on localization). In that case, I use a Color struct bound property, allowing the View to use the Color for a SolidColorBrush, a GradientStop, or whatever it wants. I initially used a string in #AARRGGBB format to completely remove the WPF dependency but my more seasoned co-workers didn't like that.

Switching between view mode and edit mode in MVVM?

I am new to MVVM and I've decided to move on and start adopting it in my upcoming projects.
I have read this related question and answer, but I don't know how this would be implemented with MVVM.
I want all the views in my project to have 2 modes, Edit Mode and View Mode.
I don't want the user by default to see TextBoxes for all the fields, I rather want them to see TextBlocks (or set all the TextBoxes' as IsReadOnly property to true (via style etc. you tell me..).
When the user opens up the entity it should usually be TextBlocks, Labels (or readonly TextBoxes) etc., and if he clicks "Edit" (if he has permission to), it should go Edit Mode, and all the fields' labels should be inverted to TextBoxes (RichTextBoxes etc., ComboBoxes or any other editable fields that are not just labels).
I am pretty sure I am not the only one having this issue, I would like to hear from the experts what is the most efficient way to switch between these modes in pure MVVM, and whether it's is common to declare two separate views for it.
Please refer me to a good article that explains how to do it (maybe it is done by Visual State?? IDK).
UPDATE
I want to know WHAT rather than HOW, my question is about the pattern, and is should I separate Edit Mode
from View Mode at either the V or the VM?
So please emphasize this detail in your answer.
Thanks in advance.
Use the IsReadOnly property for your text boxes and bind that to the "edit mode" property:
<TextBox .... IsReadOnly={Binding IsViewMode} ... />
Then in your view model:
public bool IsViewMode
{
get { return _IsViewMode; }
set
{
_IsViewMode= value;
// Call NotifyPropertyChanged when the source property is updated.
NotifyPropertyChanged("IsViewMode");
}
}
IsViewMode defaults to true and is switched to false when the user clicks "edit". The binding will instantly make all the text boxes editable.
You could do the same for the other controls - though it will be the IsEnabled property you need to bind to in these cases - though you'd have greyed out controls.
To swap out text blocks and your controls you'll need to have both controls sharing the same location in a grid and their visibility controlled by the IsViewMode property via a pair of converters:
<TextBlock Grid.Row="1" Grid.Column="2" ...
Visiblity={Binding IsViewMode, Converter=DirectConverter} ... />
<ComboBox Grid.Row="1" Grid.Column="2" ...
Visiblity={Binding IsViewMode, Converter=InvertedConverter} ... />
The direct converter is:
return IsViewMode ? Visibility.Visible : Visibility.Collapsed;
The inverted converter is:
return IsViewMode ? Visibility.Collapsed : Visibility.Visible;
I think about it this way: the View is what it looks like, and the ViewModel is how it interacts with the user. Since a readonly interface has substantially different behavior than a read/write interface, then there should be two different ViewModels.
Now I did created an edit ViewModel that inherited from a display ViewModel because I considered the editing functionality to be an extension of the display functionality. This works for simple CRUD type applications where the user is directly editing fields without a lot of business logic.
On the other hand, if you have a more complicated business process (or workflow) that you're modelling, then typically the way you manipulate information is very different from the way you want to view it. Therefore, I would generally separate the two ViewModels unless it was just CRUD.
ChrisF's answer is fine if you want to go the IsReadOnly route. If you want to go the TextBlock-to-TextBox route, though, the most efficient way is have a Control which switches its Template, via triggers, based on the value of an IsInEditMode or IsInViewModel property.
Viewmodel: I would definitely keep just one viewmodel with a ViewMode property much as described in ChrisF's answer. Separate ViewModels would just be inelegant.
View: As I see it, you have at least three options, with various pros and cons.
Just readonly-ing all the controls, as suggested in the ChrisF's answer. Pros: Simplest thing to do. Cons: That is an ugly UI in my humble opinion.
Create seaparate display and edit controls in separate containers. Bind visibility of the containers to ViewMode. Pros: A more pleasing ui experience can be afforded here. You can even animate the transitions from one to the other. Cons: Doubles the number of controls (could hurt performance for very large windows). Positioning the controls inside the two containers at exactly the same pixel positions can become a bit non-trivial in a fluid ui.
For every edit control in the xaml, position a display control right on top of it. Bind visibility to the ViewMode property. Pros: No duplication of label controls at least, so slightly faster. Cons: Harder to get animation stuff and other view tweaks right.
Edit: In view of the clarification provided, I chose to replace the previous answer as it pretty much largely dealt with the how and not the what.
First, I'd implement an abstract base class for my view models that implemented IEditableObject and exposed appropriate commands for BeginEdit, EndEdit, and CancelEdit. It might be that the actual implementations for those three methods would have to be up to the derived classes, but the commands could live in the base class.
In this approach, EndEdit updates the model with the current values of properties in the view model.
I'd also implement a boolean IsEditing property in the base class, for use in data triggers, so that if I want to switch between modes without (say) opening a modal dialog, I can just do it in a style.
As far as the visual design of the front-end goes, I find the idea that a read-only view is just an edit view with read-only controls is one that appeals primarily to programmers. Generally speaking, if you're simply presenting an object to a user, the goal of that presentation is to provide the user with an informative, clear, and intuitive representation of the information in that object. If you're letting the user edit an object, the goal of that presentation is to make the task of modifying all of the editable properties of the object as easy and clear as possible.
Those are two very different sets of goals. For instance, while a person's sex, height, and weight might be important pieces of information for an application to collect, it's probably not all that important for the system to present that information to the user in most contexts. It seems like a very natural thing to do if what you have in your head is that edit mode is just like display mode. But if you're placing the needs of the user, and not the programmer, front and center, it may not be the right thing to do at all.

Resources