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.
Related
EDIT: TL/DR
You can use custom markup extensions in PriorityBinding. It likely gets highlighted with an error message and the designer might not show it at design time but it works like a charm at runtime. Seems to be an issue with the XAML designer or some other internal component.
Well I have certain scenarios in our software where I need to display a translated string in case the binding returns null. Here my production number is set only at a certain point and until then I want to show "Unknown" translated in the user's language. For this we're using a custom markup which returns a binding to an intermediate object that supports translation.
Sadly MarkupExtension is inherited by BindingBase and not the other way around. So since PriorityBinding expects a collection of BindingBase I can't add my translation markup as fallback binding.
This is what I tried:
<PriorityBinding>
<Binding Path="ProductionNumber" />
<l:Translate Key="Unknown" Context="Common" />
</PriorityBinding>
So does anyone know how to work around this? Using PriorityBinding would be the easiest way to do this. Of course I could use a converter for this but then I would eliminate some essential features implemented in the markup which basically renders it useless.
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.
I need to create an application that will take an .ini file which will contain
min
max
default
values for the elements, allows user to edit these values and save a new .ini file. Since .ini files can not contain different elements in the specified groups the GUI needs to be generated dynamically.
From what I have read about WPF it largely stands on data-binding and Notifying Property changes.
Since my view model needs to accommodate different numbers of variables I am not going to have the ability to bind to properties, i was planning to attach one event handler to all text boxes which will pick the corresponding validation rule when the TextBox loses focus or Enter is pressed. After that, it should update the model accordingly if it passes validation and update the View using the model for the corresponding value.
I was wondering whether this sounded like a valid idea, whether there is similar design pattern I should read about or should I just steer away from WPF altogether?
You can still use bindings - since WPF supports item templating, and since you are using an MVVM pattern you can just create a VM for each sub-item in the list (you don't even need to do this you can bind directly in the template of each list item to a DTO or business object)
I'm currently doing a similar thing now - I have a list of material tests for a client, they want to have a variable number and type of tests for each material, but also be able to tweak and change those tests per order for their customer
They actually have two test types, but to describe the simpler of the two cases (which doesn't require child VMs as such) I just created an ItemsControl that has an item template:
<ItemsControl ItemsSource="{SomeBinding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{PropertyDescription}" />
<TextBox Text="{PropertyValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In this case the model just contains a list of property names/values and displays them in a stackpanel (you may need to set the ItemPanel using an ItemsPanelTemplate. Obviously you could have an extended ItemsControl that allows a DataTemplateSelector to display a different data template per type (in fact WPF already support per-type data templates).
I'm currently using Caliburn.Micro which actually does a lot of setting up child-templates for you - so if you create the bound items as a VM you can do something as simple as this:
<ItemsControl x:Name="SomeBinding" />
And CM takes care of the rest as long as the child items in the SomeBinding property are VMs themselves (though that's another story :P)
Wrap this library with a class that implements INotifyPropertyChanged so WPF can update itself once properties change. That way you can effectively databind to an INI file.
I have a WPF MVVM data form window with data validation. A lot of the controls are text boxes. Currently, the data binding trigger is set to the default, i. e. loss of focus. This means that a field is only validated when it is likely to be filled out completely. So when deleting a number and typing another number, the transient empty value will not be displayed as input error.
But a drawback is that the Save button can only be enabled when the focus moves out of the text box. (No matter where, just out of the edited control. Assuming there is anything else focusable.) If this is the only change, the user waits for the Save button to be available and nothing happens. For the Save button, I'd like to use an immediate binding trigger. How can that be done?
Edit: Forgot to mention that my Save button (which uses ICommand) is only enabled when the input is determined modified and valid. So the data will remain unmodified until data binding updates it, and that won't happen until the focus moves to another control.
I actually had a similar question a while back and the solution I ended using was a custom DependencyProperty that kicked off a timer when a key was pressed, and only actually processed the PropertyChange notification if a specific time had passed.
This means the bound property doesn't get updated (and validated) unless the user pauses in typing for a set period of times.
The code can be found here (may need a bit of cleanup), and it is used like this:
<TextBox
local:DelayedUpdateBehavior.TargetProperty="{x:Static TextBox.TextProperty}"
local:DelayedUpdateBehavior.Milliseconds="1000"
Text="{Binding MyTextProperty, UpdateSourceTrigger=Explicit}" />
Edit: Actually this link might be better. It's a markup extension so you can use it directly from your binding. I can't remember which of these two methods I used in the past, but I know it was one of them :)
<TextBox Text="{local:DelayBinding Path=MyTextProperty, Delay='00:00:01'}" />
Assuming you're using an ICommand type interface for the button click event:
You can...Implement string properties with INotifyPropertyChanged and bind them to your textbox controls. Now in your Command canexecute method you can check to see if the property is !nullorempty.
e/ grammar
Set your Binding's UpdateSourceTrigger property to PropertyChanged. The default for TextBoxes is LostFocus.
Update: So you want to have data binding working on your TextBox and only allow numbers? Have a look at this question: Create WPF TextBox that accepts only numbers
Or use a converter and bind the Save button's IsEnabled property to your TextBox (maybe using a MultiBinding if there's more than one), and use a converter which determines if the text is a valid number and returns true or false.
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