I would like to know how people are going about validating collections in WPF. Lets say for example that I have an observable collection of ViewModels that I am binding to the items source of a grid, and the user can add new rows to the grid and needs to fill them.
First of all I need to validate on each row to ensure that required fields of each ViewModel are filled in. This is fine and simple to do for each row.
However, the second level of validation is on the collection as a whole. For example i want to ensure that no two rows of the collection have the same identifier, or that no two rows have the same name. Im basically checking for duplicate properties within different rows. I also have more complex conditions where I must ensure that there is at least one item within the collection that has some property set.
How do I get a validation rule that would allow me to check these rules, validating on the whole collection rather than the individual items. I also want to print any validation error above the datagrid so that the user can fix the problem and the message will update or disappear as the user fixes each different rule.
Anyone have any experience of the proper way to do this?
The trick is to place your collection validation logic such that it's called when the ItemsControl's ItemsSource property changes. If you're using IDataErrorInfo on your view-model, then set ValidatesOnDataErrors=True on the ItemsSource binding and, when the bound collection property's name is passed into the interface's error indexer, run the logic to determine if the property is still valid or not. If you're using custom validation rules, then putting the rules into the ItemsSource binding should be fine, to.
Next, in your view-model, raise the PropertyChanged event for the ItemsSource-bound property whenever an event occurs which changes the collection's valid/invalid state. For example, if the collection needs a certain number of elements, then listen to the CollectionChanged event. Whenever the collection changes, raise the PropertyChanged event for the ItemsSource-bound property. This tells WPF that the property changed, which leads to its revalidation. Thus, your collection validation logic will run whenever the collection changes and, if the collection is invalid, WPF displays the error adorner or, if the collection becomes valid, WPF removes the adorner.
Related
My app has a background thread that periodically retrieves data from an external source, in the form of key/value pairs. I would like to expose this data for binding, presumably by storing them in some kind of static(?) model, as the data will be needed by numerous views throughout my app. There are potentially hundreds of these keys, and may be different for each customer, so I can't simply create an INotifyPropertyChanged model with a property for each value.
The app has multiple views visible at any one time, and each of these will have numerous controls (usually textboxes) that I want to bind to individual items in the above collection. When a value in the collection is updated, any controls bound to only that item should change to reflect the new value. I'm assuming an ObservableCollection wouldn't be suitable here, as a change to a single item will result in all controls updating, regardless of which item they are bound to?
To throw a further complexity into the mix, some values (which are numeric) will need formatting for display, e.g. number of decimal places, or adding a suffix such as "volts". The formatting rules are user-defined so I can't hardcode them into (say) the XAML binding's StringFormat expression. Ideally I should be able to access both the raw value (e.g. for calculations), and the formatted version (for display purposes). I'm sure it must be possible to achieve the latter using some clever WPF feature!
I would appreciate any pointers on how I can solve these requirements.
Edit: it's worth mentioning that I've previously tried implementing the model as some kind of collection. The problem is that it won't be initially populated with all values, and these only get added some time later. When they do eventually get added, a bound control doesn't update - presumably because it wasn't initially able to bind to the missing value.
I would take a different approach, namely a variation of Event Aggregation. I would have a single class that manages the overall collection (probably a singleton class like franssu suggested), but instead of binding directly to the collection in that class you create smaller models that are more specific to the individual views.
When your main model receives a new item, it publishes an event, which is consumed by the smaller models who can inspect the new item and determine whether or not they should add that item to their internal collection (the one the individual views are bound to). If it doesn't "belong" to their view, they can simply ignore the event.
You could use similar event publishing for updates to items and such, although if you're binding to the actual items you probably don't need that.
Just implement the INotifyCollectionChanged Interface and the INotifyPropertyChanged and you ll get a Collection like the ObservableCollection.
But rember if you select a Item from your Collection (as example a ObservableCollection) and you change that item your other controls won t update. So if you have a class Person in your Collection and you change the name of one person the other controls won t get the new name of the person.
Inside the Person object you still have to implement the INotifyPropertyChanged Interface and raise the event when your name changes.
So what I want to tell you is: A Collection with the interface INotifyCollectionChanged will only tell the bound controls: There is a new Item, there has been a item removed or a items index changed BUT not if the item itself changes.
So you ll need a Collection that provides the points above and a Item contained by the collection that raises events if a property of it changes.
ObservableCollection is perfect here. You should find that a standard ItemsControl bound to an ObservableCollection will only update the controls of the items that have changed, not every item in the collection.
This is the reason ObservableCollection exists - the events that it raises specifically identify items that have changed, so that the UI can handle them sensibly.
I've tested this locally with a small WPF app and it works fine. Worth noting, though, that a virtualised items panel would probbaly appear to break this behaviour when it scrolls...
EDIT: rereading your question, you actually say "When a value in the collection is updated..." If your collection contains instances of a class, and you update properties on the class, you don't even need ObservableCollection for this to work - you just need the class to implement INotifyPropertyChanged.
Say I have a ListBox that is bound to an observable collection on my view model, where the data item is a simple class that has a Name and a Value property.
When I add or remove items from the view model collection, the ListBox updates automatically as I would expect.
However, if I change the name of a data item, I want the ListBox to refresh as well, so I raise a property change for the view model property that exposes the observable collection after I've updated the name in code. The ListBox won't update however.
It's as if the binding is saying, well, the object you're giving me (the collection itself) is the same as the last one I had, so it hasn't really changed, has it? Silly programmer, telling me to refresh when I don't need to!
There are only a couple of ways I have found to work around this issue:
Raise an explicit Reset notification from the collection itself (requires subclassing of ObservableCollection to do this)
Recreate the list using a new observable collection, then raise the property change - the collection object is different now so the binding updates
Neither of these two ways are ideal, although the first method is definitely preferable - it really shouldn't be this hard to get a listbox to update!
Can anyone explain why bindings work this way, and if there is any way to change the behaviour so that the binding will always update on a property change notification, regardless of whether the source has changed or not?
It sounds to me like the items within your ObservableCollection do not implement INotifyPropertyChanged. You need to do this, so that when you change the Name property of an item in the collection, the UI is updated. From your description, the framework and your bound collection are working exactly as the should.
I have simple SL user control. A listbox which shows all customers and on the right a number of textboxes and comboboxes that are bound to the SelectedItem (Customer) in the listbox. The SelectedItem bound to SelectedCustomer property.
I am looking for a pattern/methodology to deal with canceling changes made to the customer (in the bound textboxes and combo boxes).
The edit controls (textboxes and combo's) can be one or two way bound to the selecteditem of the listbox.
If they are two way bound, immediate changes in the textboxes are reflected in the listbox. If they are oneway bound the changes in the textboxes are not reflected in the SelectedCustomer object.
At the bottom of the edit form i have typical Save, Cancel, Delete buttons. The save button for instance would take the SelectedCustomer object (if twoway bound and I would send through service for saving on server).
If the textboxes are one way bound i have to capture somehow the textbox values and insert into some object for sending to the server for saving.
If I use twoway binding , and say the save operation fails...i have to set the SelectedCustomer values back to original values otherwise the client now continues to see data that has not been saved.
There must be an easy way of dealing with this type of scenario....
RIA Services with Entity Framework already provides this functionality, basically how RIA services works and you can do it too as follow.
Each class implements interface called IEditableObject, which provides methods BeginEdit/EndEdit and CancelEdit. And it also stores (copies) instance of same class with name "OriginalEntity" with same values that were loaded from server.
After the form shows up for user to modify, BeginEdit is called, which probably caches every property using reflection, in some sort of dictionary. If you call CancelEdit, then values from OriginalEntity are loaded back in object.
Upon some errors while saving changes, you can either refresh the entities from server (best way) or you can try loading properties back from OringalEntity property.
I wouldn't discard user changes, as that easily leads to user frustration. IMHO, the user should not be informed about connection problems by uncontrolled data rollbacks.
in my MVVM application I have a list of element implementing IDataErrorInfo and INotifyPropertyChanged
the view validate the VM objects when a propertychanged event is raised.
the problem is that the validation result depends not only by the internal state of the object but also by the "enviroment" that is the other objects belonging to the list.
So I need that the validation is called on all the elements of the list every time that an object is deleted or updated.
How can i force the validaiton in this way?
within the VM which holds the list, each time your list changes, iterate over the list and tell each object to raise property change notification either on the properties you know might be invalid or just every property by setting the property name to an empty string.
It sounds like your validation may be across multiple objects, in which case you need to run your validation from the VM mentioned above and set error messages in the appropriate objects in the collection and then raise the property changed event on those objects.
I have done this previously by having a SetErrorMessage(string string) public method on the objects implementing IDataErrorInfo, and a public OnPropertyChanged(string) method so that I could set errors and raise contextual property changed events from outside the object.
The errors would be stored in a dictionary and the this[string] property would look up the dictionary for errors as well as run its own validation.
Hey I have an answer to your question. The default list called ObservableCollection will raise an event only if elements are added or deleted. If the element changes property wont be raised.
You can subclass ObservableCollection and add event for every item. There is a very nice implementation in this link given by a fellow from StackOverFlow.
ObservableCollection that also monitors changes on the elements in collection
Its too good, I have tried and used. It works superb. Hope it helps you and puts you in right direction!!!
I am binding a ListView a property that essentially wraps the Values collection (ICollection) on a generic dictionary.
When the values in the dictionary change, I call OnNotifyPropertyChanged(property). I don't see the updates on the screen, with no binding errors.
When I change the property getter to return the Linq extension dictionary.Values.ToList(), without changing the signature of the property (ICollection) it works with no problem.
Any reason why the Values collection bind and notify properly without projecting to an IList<>?
Calling OnNotifyPropertyChanged() isn't exactly correct in this case, since the collection is still the same, however the items in the collection have changed. I don't know exactly how the WPF binding code works, but it might do a quick check to see if the reference it is binding to has changed, and if not it won't update the UI.
The reason that ToList() works is because each time it is called, a new instance of List<T> is returned, so when OnNotifyPropertyChanged() is fired, WPF picks up on the change and updates all of its bindings.
I don't know if this is possible or not, but the ideal solution would be to use a collection for bindings that implements INotifyCollectionChanged. This will fire events that WPF monitors so that items can be added, removed, etc. from the UI as appropriate.