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).
Related
I'm developing a WPF/MVVM application and I have a listbox binding to data in a ViewModel. At various points I need the view model to cause the listbox to scroll to a given element.
How can I do this without creating a custom control and while still maintaining good separation of concerns?
I've currently got it working by creating a custom behavior class in the view layer with a dependency property VisibleIndex which the XAML code then binds to an integer in the view model:
<ListBox x:Name="myListBox"
local:ListBoxVisibilityBehavior.VisibleIndex="{Binding VisibleIndex}">
When the integer is set it triggers the dependency properties update handler which tells the listbox to scroll to the associated index.
This seems a bit hacky though because the dependency property value is never changed by the listbox and the update handler only gets called when the value changes, so the only way to ensure that the relevent item is visible is to do something like this:
// view-model code
this.VisibleIndex = -1;
this.VisibleIndex = 10;
The only reason I'm using a behaviour class at the moment is for binding my custom dependency property, is there a way to do something like this with events instead?
Attached properties are somewhat required in your case - as at some point, 'somewhere' you need to call the following method...
ListBox.ScrollIntoView(item)
or
ListBoxItem.BringIntoView();
And for that you need some sort of code behind - and attached properties/behaviors are a nice way of packaging that, w/o impacting your MVVM.
Having said that - if you just need to have your 'selected item' scrolled into view at all times (which is the case most of the time). Then you could use a different attached-property based solution (that again):
mvvm how to make a list view auto scroll to a new Item in a list view
All you have to do then is to set or bind to SelectedItem.
That's a bit 'nicer' if you wish - but the mechanism is the same.
For anyone else interested in the answer to this one of the MS engineers on the WPF forum cleared it up for me. Instead of binding to an event directly you bind to a wrapper object that encapsulates that event. The behaviour can then grab the reference to the wrapper from its DP and do whatever it wants with it i.e. subscribe to the event, trigger it etc.
I am working on an application that uses WPF/C# with MVVM. I have one particular ObservableCollection<> which is bound to ListBox.
Scenario 1: When the application is running, I modify this ObservableCollection<> and ListBox is populated as expected.
Scenario 2: With new requirements I have to fill this ListBox by default with some init values. So I have added a method for initializing it in my ViewModel. I call this method in OnStartup() after initializing View & ViewModel. DataContext is also set properly. In this scenario values are updated in the ObservableCollection<>, Unfortunately they are not reflected in the ListBox.
Just to verify if anything is wrong with the OnStartup(), I added the same method call in a callback on ContentRendered from the View, instead of OnStartup() it did work fine.
So my question, When exactly MVVM guarantees that all the bindings are setup correctly?
Edit:
One more observation, if I pop a message/dialog ListBox is populated as expected. It calls ContentRendered callback. And then it populates correctly.
I am almost convinced that ContentRendered should be the function that guarantees the bindings.
I can't say with certainty that this is your issue, without seeing the change to the code, but it sounds like you may now have an initial value of null for the property to which you are binding.
If you subsequently set that property to an ObservableCollection<T>, there is no automatic change notification.
One option would be to raise a notification that the property value changed (from null to something), and then let the ObservableCollection<T> handle change notification from there.
The better solution would be to initialize the property with an empty ObservableCollection<T> from the get-go, then your initialization to default values will mean adding those values and change notification should happen as you expect.
You might get the same problem by starting with a non-null collection, then setting the property to a new instance of a collection, without raising a notification, but I'm not 100 percent certain of that.
If this is not your issue, then I'll be happy to take another look.
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.
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.