Silverlight: Can't force update of binding to collection via INPC? - silverlight

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.

Related

How to reorder a ListBox's bound ItemsSource without access to the underlying collection?

I have created a control derived from a WPF ListBox that allows me to reorder the contents of the ListBox by dragging and dropping. I do this with the below code:
ListBox.Items.RemoveAt(sourceIndex);
ListBox.Items.Insert(targetIndex, droppedObject);
The problem shows up when I bind an ObservableCollection to this derived class's ItemsSource. When bound, an exception is thrown when I try to access the ListBox.Items and edit it. It instead wants me to edit the ObservableCollection instead. This makes sense to me, but I don't have access to this collection in the .cs file of my derived ListBox class. Since the implementation of my derived control should be generic, I shouldn't ever access a global ObservableCollection within it. I have attempted to access the ListBox's ItemsSource and try and do my updates to it, but I cannot seem to cast it into a list that allows me to easily edit it without knowing what the contents are. Is there an easy fix to this issue that I am missing?
but I cannot seem to cast it into a list that allows me to easily edit it without knowing what the contents are
You need to make sure that the type assigned to the ItemsSource property actually supports re-ordering of items, i.e. that it implements the IList interface. Otherwise you cannot use the RemoveAt and Insert methods.
Cast using the as operator:
var sourceCollection = ListBox.ItemsSource as IList;
if(sourceCollection != null)
{
sourceCollection.RemoveAt(sourceIndex);
sourceCollection.Insert(targetIndex, droppedObject);
}
If you get a null reference back, it means that the source collection doesn't implement the IList interface and you cannot move an item based on an index.
Yes, the correct way to do it is to actually reorder items in the underlying ItemsSource. I mean, you can coerce ListView to apply its own ordering (e.g. binding ItemsSource to a custom CollectionView instead), but things will get very messy when underlying collection changes. Personally, I'd avoid that route.
The proper answer would depend on why exactly do you want to order items and whether the ViewModel and Model layers should react to this reordering somehow (e.g. save the new items layout somewhere so it won't disappear after application is restarted). Normally, ViewModels are not expected to listen on changes to their ObservableCollections, but would rather accept 'MoveUp'/'MoveDown' commands from view and alter ObservableCollection accordingly.
On the other hand, if you're not creating an externally reusable library, and can guarantee that your ViewModels will not freak out if View starts to actually moving items around bound collection, its a relatively safe bet to just cast ItemsSource to non-generic IList interface and manipulate the items directly, as you suggested. Note that theoretically a bound collection may not implement IList, but in a real world application it almost certainly would.

WPF: How to trigger GUI behaviours in response to view-model events?

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.

WPF: How to create a collection that can be bound to

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.

WPF Binding to IDictionary<int,MyType>.Values Doesn't Respond to Property Changes?

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.

.NET WinForm ListBox item displays old value untill reassigned to itself

I populate a ListBox control with my own objects redefining ToString(). The objects are displayed correctly when I just add those objects using listBox1.Add(myObject). However, if I later change something in this object, no changes are displayed in the listbox. Debugging reveals that an object inside listBox1.Items is indeed changed, but it is not reflected on a screen.
Interestingly enough, if I reassign a particular listbox item to itself (sounds a bit weird, doesn't it?), like:
listBox1.Items[0] = listBox1.Items[0]
this line will display a correct value on screen.
What is going on here? Does it have anything to do with threading?
Since you're using ToString of the object to provide the text of the list box item, the ListBox has no idea that the value has changed. What you should do instead is have the object implement INotifyPropertyChanged then expose a public property such as Name or Text and return what you normally would have returned from ToString().
Then set the DisplayMember of the ListBox to the name of the new property.
Make sure you are correctly raising the PropertyChanged event in the object and the ListBox should be able to automatically pick up the changes.
Edit: Adrian's edit reminded me that I do believe you'll need to use a BindingList as your data source in order for the property change notifications to be picked up. A quick scan in Reflector looks like ListBox on its own will not pick up the property changes mentioned above. But INotifyPropertyChanged + BindingList should.
The ToString() value of each item is cached when the listbox is first displayed. If an item in the listbox's Items collection then changes, the listbox does not notice and still uses the cached ToString() values for display. To force the listbox to update, either call RefreshItems() to refresh all items, or call RefreshItem(int) specifying the index of the item to refresh.
From the MSDN docs for RefreshItems():
Refreshes all ListBox items and retrieves new strings for them.
EDIT: It turns out that both of these methods are protected, so cannot be called externally. In trying to find a solution, I came across this SO question that this question is basically a duplicate of.
Have you tried calling Refresh() on the ListBox? I think the problem is that the ListBox does not know your object changed. The reason reassigning the the item works is because the ListBox will repaint itself when the collection changes.
you could invalidate the control, forcing a re-paint... perhaps..

Resources