Is this senerio valid?
I have a View to maintain an Item.
I have View Model which exposes the Item Object (implements INotifyPropertyChanged) as a Property to which is View is bound.
Is it valid for me to pass the Item object to a Backgroundworker where it is modified, then raise the PropertyChanged event when the BackgroundWorking is complete?
Or should the BackgroundWorker in no way modify the Item object. I would update the existing Item object with the results passed back by the BackgroundWorker. This would be done in the RunWorkerCompleted event. But does this lock the UI thread and defeat the object of having a backgound worker?
Confused?
I'll try to explain.
The User takes an option to create an Item. I create the View and View Model. In the View model an empty Item object is created. He is presented with a View to maintain the Item. On selecting the Item Type property, this instigates a complex process to create a list of proerties to be entered by the User. I could block the UI thread whilst the list is created but this gives a poor user experience. I want to pass the processing to a background thread while keeping the UI alive. At present, I set a flag to indicate the section on the View is loading, pass the Item object to the BackgroundWorker which updates the observable collection of Properties. When the BackgroundWorking is complete I call the PropertyChanged event which updates the section of the View which is bound to the list and turn off the flag to indicate the section is loading. This seems to work with no issues. But I have a gut feeling that I should not be updating bound onjects from the View Model in a background thread.
Thanks Tim
This sounds ok. As long as your item-object is no DependencyObject, you can change the properties of them in the background worker.
DataBinding to properties of your object will work, the binding engine will do the thread-switching automatically for you.
However, dont't fill data-bound collections or manipulate properties of DependencyObjects (such as UI-Controls) in the background worker without dispatching the manipulations. This would lead to an exception.
Edit:
Only for clarification: The real question is not, if the item-object is a DependencyObject but if the property is a CLR-property or a DependencyProperty. Because DependencyProperties are bound to DependencyObjects, I often use the above simplification, but its not the full truth.
This means that if you have a CLR-property, you can set its value from a foreign thread, regardless of your class is a DepenendencyObject or not. This is a slight difference to my first statement.
Related
I have read quite a number of posts on this site about handling of changes in datagrid. The general consensus seems to be that datagrid items should implement INotifyPropertyChanged and then do whatever necessary in OnPropertyChanged().
However I'm not quite clear what a recommended practice is when view model and model are taken into account.
Let's say that my model has many fields and mulitiple lists of items of different type. The lists of items are bound as ItemSource to datagrids in a corresponding view. In my view model (or model) I'd like to have a flag (e.g. 'IsDirty') that tells me if anything was changed in fields and/or in items of the lists.
Handling changes made to fields is simple because their corresponding OnPropertyChanged() will be called in the view model. However, datagrid items' OnPropertyChanged() will be called in items themselves.
How do I let the parent view model (or model) know that a change was made to one of the items in one of the datagrids? Obviously I could give each item a reference to its parent view model (or model) but I wonder if there is a better and recommended practice.
Thanks.
Maintaining a parent IsDirty property can most simply be accomplished through listening to child items' OnPropertyChanged events, combined with the use of ObservableCollections and the CollectionChanged event.
There are normally only three use cases
Child item property is updated
Child item is added to the Children collection
Child item is deleted from the Children collection.
The simplest solution is to create a Children property of type ObservableCollection<Child> and hook into the CollectionChanged event. This event fires whenever a Child is added or removed from the collection. In this event you can capture all the data that you need:
On Parent initialisation, add all the initial children to the collection, hooking into each one's OnPropertyChanged event as you do so, toggling IsDirty whenever a property is changed. After initialisation,...
Child added - hook into the child's OnPropertyChanged event and toggle IsDirty.
Child deleted - de-hook from the Child's OnPropertyChanged event.
Using MVVM, my viewmodel is my WPF window's data context. In the viewmodel is an observablecollection that holds the items in a listbox. When I add items to the collection, the listbox updates as expected
As part of my generic undo function, the observablecollection can be replaced with an older version. This happens by passing the collection to a method by reference and changing the reference. Everything after the undo works correctly except for the listbox. This continues to show data from the old reference.
How can I either stop this from happening or change the reference that the datacontext uses so that my listbox is "undone" and then continues working?
You need to provide some change notification in order to trigger the UI to update which you won't get from reassigning a ref variable to another instance. To get the notification you can either Clear and re-fill the original ObservableCollection instance or fire a PropertyChanged event for the collection property's name after swapping the instances.
Using the MVVM pattern, properties are changed in the ViewModel, with setters raising the PropertyChanged event. The event is handled in the View (automatically by WPF) and bindings are refreshed.
In your case, the value is being changed without the setter being called, so the PropertyChanged event (if it exists) is not being raised.
One option might be to manually raise the PropertyChanged event from the undo code. This would allow you to keep your existing design (please note that INotifyPropertyChanged.PropertyChanged is different to ObservableCollection.CollectionChanged - do a bit of research if this is not clear).
The second option would be to handle the CollectionChanged event, and keep a record of ItemsAdded and ItemsRemoved.
Your undo mechanism can then re-add any items which were removed, or remove any items which were added. This might require a bit of design tweaking.
I would go with the second design, as I think the design rethink might be a good idea. If you are using MVVM, you should be using Commands, and if you are using Commands you can implement Undo/Redo functionality neatly by extending the Commands (remember that an action made by a user is usually a bit more than a value change).
Ok, following on from yesterday I've added a new layer of complexity. We still have a theoretical Model class, ViewModel and View. This time my Model has a Threading.Timer (Chosen specifically to get timer callbacks on the "wrong" thread.
The Model has an ObservableCollection. The Timer callback adds items to the collection.
The ViewModel simply passes the collection to the View which contains a listbox bound to the collection.
This doesn't work.
The model also exposes a string which is updated in the same timer callback.
This too is exposed via the viewmodel and bound to a TextBox.
This does work.
I've seen hints in my googling that updating collections doesn't make INotifyCollectionChanged work as expected. I get a total implosion, not even an exception, just immediate termination of the application.
So there are two questions:
One relates to our discussion yesterday. I'm using INotifyPropertyChanged and ObservableCollections in my Model because they are the thing upon which the view does work. It still makes sense to me to use these mechanisms to notify my viewmodel, or what ever that the underlying model has changed. So how do I deal with updates occuring on a different thread?
Second, what is happening that makes INotifyPropertyChanged work with the binding? I'm binding a string property to a DependencyProperty called Text, so is it the DependencyProperty system that marshals my change back to the UI thread? Edit: And can I rely on it, i.e. does it do this because they expect me to talk to it cross-thread, or is it just a catch all that I shouldn't rely on?
The ListBox is bound through ItemsSource="{Binding ObsCollection}". When this crashes the application. Actually, at first I started the timer when the Model was created which happened when the Window's DataContext was set, so it would actually bomb Visual Studio...
Thanks
WPF controls have thread-affinity, what this means is that their properties can only be modified from the UI thread. Therefore, if you update a property value from a Timer (other than a DispatcherTimer), you will have to marshal this update onto the UI thread. This is perfomed via the dispatcher:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => // update your control here));
The databinding framework does not ensure that updates are marshalled onto the UI thread, therefore, if you update your model from a different thread, this will cause issues. therefore you need to use the same pattern as above. In other words, if you are adding objections to your observable collection, this add must be performed via the Dispatcher.
This problem is quite prevalent in WPF. I think the best option is to have your own ObservableCollection<> subclass that takes care of dispatching event notifications to the UI thread automatically.
And since the wheel has already been invented, I 'm going to simply refer you to the answer to this question: ObservableCollection and threading.
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.
I am using MVVM as my WPF architecture and I have a WPF TreeView implemented as delay loading (child items are not loaded recursively until you expand).
However, I need to implement asynchronous loading as well upon expansion. Is there a way to do that? I need to keep this in control level and not in code-behind/application level.
Thanks
Why do you need to implement this in the control itself?
Assuming that isn't an absolute requirement, I would handle this in the property that returns the children of the specified node. If the children haven't been populate yet, use a background thread to load the children, and as they are found notify the UI thread and then add the child object to the collection of children. Assuming that you're using an ObservableCollection (or at least a collection that implements INotifyCollectionChanged), as the children are added they will appear in the UI asynchronously.
If you use lazy loading and HierarchicalDataTemplates you will only get the items you've selected loading. Keep in mind, the TreeView will need to load one level below what it is displaying to determine whether or not it should show the Expand/Collapse Toggle button
Here is how to do this:
Create an attached property of type IEnumerable
In the PropertyChanged handler for the property, cast sender to TreeViewItem and check the IsExpanded property.
If IsExpanded is true, do a Dispatcher.BeginInvoke at ApplicationPriority.ApplicationIdle priority to your fill routine
If IsExpanded is false, set an event to detect when it becomes true and in the callback do the Dispatcher.BeginInvoke to your fill routine
In your fill routine, set the ItemsSource of the target control to the attached property value
Use a style to set your attached property on TreeViewItem instead of setting it through HierarchicalDataTemplate (omit ItemsSource there)
This prevents the TreeViewItem from ever having its ItemsSource set until it is expanded. Creating this mechanism is some work, but once it is created you can make any TreeView delay-load by simply removing the ItemsSource from the HierarchicalDataTemplate and setting it in your ItemContainerStyle instead.
For every UI element that needs delayed loading I have used a 'View GUID'.
Explanation by example:
At the beginning, Control.Tag = "0000-0000 ...."
User makes an interaction,
a random GUID is generated, namely, guid1
Control.Tag = guid1
a thread is created which takes (what do to, control, guid1)
in between, the user makes another interaction
a random GUID is generated (guid2 this time)
Control.Tag = guid2
a thread is created which takes (what do to, control, guid2)
The first thread finishes:
it invokes a code into UI thread which takes results and the guid it got before execution
the invoked code checks that Control.Tag != guid1, makes no changes
The second thread finishes:
it invokes a code into UI thread which takes results and the guid it got before execution
the invoked code sees that Control.Tag == guid2, makes changes