WPF lengthy Validation Scenario - wpf

in my scenario I have a Linq2SQL Data backend.
my Dataobjects implement IDataErrorInfo to catch errors like Name==null (fast to execute Validationrules that only require the value, nothing special so far )
the Dataobjects are organized in a tree-structure, so each has a Parent and Children
How can I validate if a chosen Name is Unique under the Children of a Dataobjects' Parent?
The problem I'm facing is, that the unique Name validation requires a Database roundtrip which lags typing if UpdateSourceTrigger="PropertyChanged" on the TextBox binding to the Name.
On the other hand, I could set UpdateSourceTrigger="LostFocus", but the problem with that is, that I enable/disable a "Save" button on valid/invalid data. Now in the invalid state you can't click the Save button, so there's no way the Textbox could lose Focus to update (only tabbing away which is ugly, but there are more "unusabilities" with LostFocus (e.g. Error keeps displaying while typing and thus changing the name).
what would be ideal was a way to say for individual validationrules to apply on different events like so:
<TextBox Grid.Column="1">
<TextBox.Text>
<Binding Path="Foldername">
<Binding.ValidationRules>
<wpfresources:UniqueChildValidationRule ValidationStep="UpdatedValue" **UpdateSourceTrigger="LostFocus"**>
... stuff here ...
</wpfresources:UniqueChildValidationRule>
<DataErrorValidationRule **UpdateSourceTrigger="PropertyChanged"**/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
What is the best way to solve this?
EDIT
This MSDN article seems to suggest, that a BindingGroup would be the way to go. I'll look into that...

I finally figured a way to accomplish what I wanted.
The use of BindingGroup worked, but I stripped my solution down because what I needed essentially was a way to show a Validationerror in the correct Textbox.
I did so now with an attached property that I read out on execution of the "save"-functionality. then I manually validate and set a ValidationError on the textbox. that way the textbox can keep its PropertyChanged UpdateSourceTrigger and the lengthy validation is done on submit.

For what it's worth, validation that requires potentially arbitrary time (talking to a database over a network, e.g.) is one form of validation that does not need to be reflected in the UI in realtime. If you can work it so that it is (pre-caching the values that will be checked by reading them from the DB before you need them, e.g.), so much the better, but this is the one scenario where reporting an error after the user has submitted the data is generally acceptable as long as you don't destroy the information that the user entered.

Related

WPF DataGrid validation when bound to a DataView

I am attempting to validate input on a DataGrid which is populated by a DataView (e.g. myDataGrid.ItemsSource = myDataView). However, all the WPF DataGrid validation examples I have seen assume that the DataGrid source is a C# class. I can't figure out how to hook up a single cell (i.e. a column) to a code-behind validation. Can someone give an example or point me to one?
Ok, I did some quick tests; I think the main impediment to really doing it manually is that the DataGridCell doesn't create any bindings for its content when assigning directly a DataView for display. However, if you're willing to jump through the hoops of assigning custom DataGridTemplateColumns when creating the DataGrid, you can access the bindings on, say, the TextBox instances inside the template, and set custom validation errors on them.
Well actually, either this or override the default style of the textboxes generated by the DataGrid; you see, the default textbox style doesn't have any borders or anything, so setting a validation error on it doesn't have any effect. Making it into a normal textbox with DataTemplate or overriding its style would enable you to actually make something visible as an effect of setting the validation error.
However you'll need to do some research by yourself; I did a quick prototype and it works, but I can't create the binding correctly in the DataTemplate (either I forgot all the WPF I knew since I worked with it last, or I only know how to work with proper bindings :)). If you get somewhere with that it's easier going forward:
Somehow get access to a BindingExpression; what I did was attach a handler to the LostFocus event and checking if what lost focus was a textbox that was inside a DataGridCell; if so, I simply mark that binding as invalid (with t representing a TextBox instance):
var x = t.GetBindingExpression(TextBox.TextProperty);
Validation.MarkInvalid(x, new ValidationError(new ExceptionValidationRule(), x.ParentBinding, "error", new Exception("error")));
I must confess I'm not sure anymore what each argument's role is in the ValidationError constructor is (I think the exception message is displayed by default in the validation tooltip? Or is it the error content?), but I'm sure you can figure it out. Now if you don't mind, I think I'll take a break... like I said, it's not easy going against the flow :)
So I did some more research, and what I was basically missing was that I can specify the column name with the Path attribute of a Binding (or even use the column ordinal in brackets, e.g. Path="[0]"). After that realization, everything is basically the same as using a class property. So a typical DataGrid column definition I use follows:
<DataGridTextColumn Header="Regular" EditingElementStyle="{StaticResource ValidationErrorStyleBoxRA}" ElementStyle="{StaticResource ValidationErrorStyleBlockRA}" Width="60">
<DataGridTextColumn.Binding>
<Binding Path="HourlyRate" StringFormat="F3" ValidatesOnExceptions="True" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:HourlyRatesAmountValidate ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>

Exception (validation) handling in WPF DataGrid

If the binding engine cannot convert the entered text into the bound property's data type in a DataGridTextColumn (binding below), the cell gets a red border, and you cannot edit any other cells until the error is fixed. The border remains even if you tab out of the cell.
<DataGridTextColumn Binding="{Binding IntegerProperty, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True, ValidatesOnExceptions=True}" />
This is all great in my setup. The problem is that if I throw an exception in a property setter, the behavior is different. First the red border (validation error) is removed immediately when I tab out from the cell and I can continue to edit the rest of the grid's cells.
Is there any way to make the exception thrown in property setter behave similarly as the binding engine's way of handling FormatExceptions? The biggest annoyance is that the validation error is removed after moving out from the cell.
I think you should NOT throw an exception in a property setter.
Take control of the validation by creating your own ValidationRules objects.
This might help you or you might be beyond this.
http://www.wpfsharp.com/2012/02/03/how-to-disable-a-button-on-textbox-validationerrors-in-wpf/
When the exception is in the UI, the invalid value is not even passed through to the bound property.
Also, you can handle the exception instead of just throwing it and set the property to a default value or clear it out or something.
There's nothing wrong with throwing an exception in a setter. After all, some setters call validation or other procedures while they're being set. For example, I may have a Connected property for a database that attempts to open a database connection when set to true. This could be set to a toggle button on my wpf window. That said, you could create a listener--something similar to this link: http://www.switchonthecode.com/tutorials/wpf-snippet-detecting-binding-errors.
In my case, I'm using MVVM and Prism/Unity framework. Once I retrieve the value and either trap an exception or raise one, I call an internal event within my View Model called NotifyViewOfException. I pass in the exception object and it handles it from there by displaying a window. You don't have to use Prism/Unity to do this. Based on your description, I don't know what you're using so I won't go into detail about that piece of it.

DataBinding happens only first time

I have two User Controls in my window, both are bound to the same context.
One of them is getting updated and the other is not.
What could be the reason?
Sounds like you have an issue with the bindings. Make sure your dependency properties bound to each control are both getting notified via OnPropertyChanged. If both properties aren't getting notified this would be your issue regardless if they share the same datacontext (viewmodel).
Blessings,
Jeff
Beware of UserControls by default they bind one way you have to specify TwoWay:
<Binding Mode="TwoWay" ...>
...
</Binding>
Do you see any binding errors in the Output window? If so you can post that and maybe we can understand.
If not try to put a dummy converter in the binding and see if its methods are hit.
One from the multiple issues could be binding source address is changed.
Without seeing the code, we only guess:
Check that the property names in the bindings are an exact match (including case). It is quite common to have typing errors that cause bindings to fail (silently).

WPF Binding Converters

I am currently stuck at a dead end with the following situation:
I have a List<Category> collection with each Category containing an Id, Name and a List<string[]> property called Subcategory (where array contains the Name and Id of that subcagtegory - I didn't feel like creating another class).
I have a TreeView with HierarchicalDataTemplate bound properly to categories and subcategories. I also have check boxes next to the tree node and I bind IsChecked to a MultiBinding of the Id of the sub/category and the an overall list of sub/categories that should be checked-off.
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource IsCategoryChecked}">
<Binding Path="Id"/>
<Binding Path="myDataSet.Tables[Categories]/cat_subcat"/>
</MultiBinding>
</CheckBox.IsChecked>
The converter I use simply checks if the name of the sub/category I provide is part of the list (both are values I'm binding to). This works fine and overall I am quite happy with the result.
The problem comes with converting the check/uncheck back. When user changes the IsChecked value I must either add the Id of the sub/category to the list of all checked-off categories or remove that Id from it. Unfortunately when I've attempted to implement the ConvertBack() method for IMultiValueConverter, I only have access to the ACTUAL value that changed (the true or false value of IsChecked) and have NO access to the Id of the sub/category that THAT SPECIFIC CHECKBOX is bound to.
SOLUTIONS I'VE TRIED but failed miserably:
Saving the sub/category name when I do the initial conversion is not an option since I'm using the same Converter for ALL the sub/categories and thus I'd only save the last sub/category I've tried to convert.
Using a MouseUp/KeyUp event is useless because (for whatever reason) they fail to be triggered (a bug?).
I am contemplating to ignore the ConvertBack() and use Checked/Unchecked events instead but feel like that is a rather "dirty" solution because I might just as well ignore the bindings altogether! Is this the only way to go? I've ran out of options but still hope for a "good" solution, if it's out there!
You have a couple of options, and I've used both in production:
Overload Checked/Unchecked, as you've said, which gives you the most control over the situation
Add an IsCategoryChecked property to your strongly typed table in the code behind which handles the changes required behind the scenes, and you bind to the IsCategoryChecked property rather than using the converter
The second one works fairly well thanks to the partial classing with strongly typed datasets. However, it still isn't "seamless".

How to postpone an update to a Binding in WPF

Is there an easy way to tell a Two-way WPF data binding to wait a few milliseconds after the last change before updating the Source with the new property value?
I'm implementing a filter feature for a ListBox where I have a textbox, and I want to filter the content of the ListBox according to what I type. I'm using data binding to connect the pieces together. Filtering the list can be quite time consuming, so I don't want to do it after every character that is typed: hence my request.
I have been using Paul Stovell's DelayBinding Extension (his site's down at the moment, so I can't link to it). However, I suspect that it is the cause of a memory leak in my applicataion (caused by it not removing event handlers).
Does anybody else have any other ideas?
I'm also a few years late, but if you're using WPF 4.5+ there is now an property exactly for this purpose, it's called Delay.
Description
The amount of time, in milliseconds, to wait before updating the
binding source.
Example usage
<TextBlock Text="{Binding Name, Delay=500}"/>
A little late to the question here (just a few years :) but for anyone who's interested I had a similar requirement in a project so I created two markup extensions called DelayBindingExtension and DelayMultiBindingExtension.
They work like normal Bindings with the addition that you can specify UpdateSourceDelay and/or UpdateTargetDelay, both of which are TimeSpan properties. Also, I've verified that it's leak-free (it makes use of the propertychanged callback of a dependency property binding through inheritance context rather than the DependencyPropertyDescriptor).
Example usage for a DelayBinding
<TextBox Text="{db:DelayBinding Path=TextProperty,
UpdateSourceTrigger=PropertyChanged,
UpdateSourceDelay='00:00:01'}"/>
And for a DelayMultiBinding
<cs:ColorSelector.SelectedColor>
<db:DelayMultiBinding Mode="TwoWay"
Converter="{StaticResource ColorConverter}"
UpdateSourceDelay="00:00:02"
UpdateTargetDelay="00:00:01">
<Binding Path="Red" />
<Binding Path="Green" />
<Binding Path="Blue" />
</db:DelayMultiBinding>
</cs:ColorSelector.SelectedColor>
Source code and sample usage for DelayBinding and DelayMultiBinding can be downloaded here.
If you're interested in the implementation details, you can check out my blog post about it here: DelayBinding and DelayMultiBinding with Source and Target delay
First, to answer your question, I would add the UpdateSourceTrigger binding extension which will let you control when the binding updates. Try LostFocus first but it sounds like you might want to go for Explicit.
Second, if your filtering takes a long time I would look into using CollectionViewSource on your ListBox. Bea Stollnitz has a good primer on it here and I used this blog post to show me how to filter. When I changed over I noticed a huge speed difference over my other implementation even though they use the same filtering functions. Also CollectionViewSource will automatically handle updating filtered items if the list you're bound to changes, even on the item level if you're binding to an ObservableCollection (this is the original reason I changed to CollectionViewSource).
HTH

Resources