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

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.

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.

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

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.

How to assign context and refresh it in Entity Framework?

I created a new entity object and bound it to controls in another window (edit window). After modifying and saving I assigned a new entity object into the one in the main window. The old entity object is bound into a datagrid, now I want the datagrid to display the data that I had modified and saved.
ObjectContext.Refresh Method (RefreshMode, Object) seems to be what I want but I don't know how to use it correctly.
In short :
I have a main window with datagrid displaying the whole data of the table. Users can pick one row and edit it in a edit window. After saving, the datagrid should display what has been modified.
Your best bet here is to use an ObservableCollection as your data source for the datagrid instead of the query.
And look at implementing INotifyPropertyChanged interface in your Customer class.
The ObservableCollection is initially populated by the database query. User changes are made to elements within the ObservableCollection and once complete you then just need to trigger transferring the changes to wherever you originally obtained your list of Customer objects
By doing this changes made both to the collection of Customers and to individual Customer objects (if present within the datagrid) will be automatically updated for you.
edit
I must admit that I'm a bit rushed to offer up any code at the moment, but here's a pretty good article that explains how to use ObservableCollections and classes that implement INotifyPropertyChanged. It also has code examples, which although in VB.NET should give you enough of an idea to get started.
In effect you separate your code into distinct layers UI (View), business logic (View Model) and data layer (Model where your entity framework resides).
You bnd your datagrid to the ObservableCollection type property in your Customers class and your edit csutomer window is bound to as instance of your Customer class.

Items control data source's item property update, not reflecfted in the items control's itempanel template instance

I have a custom panel control that is intended to be used as an itemspaneltemplate in a items control.
The itemscontrol will be databound to a data source.
This datasource is a List, and each item in the list is a custom business object.
In the application, the user is able to update each of these business objects in the list, and that fires the notification on property changed as expected.
Now my problem is here:
When the user updates the object's properties in the data source (the itms in the List) that the items control is bound to, my custom panel control is not able to get that notification, so as a result the items control does not get updated with the updated items in its view.
I tried using an ObservableCollection instead of List - the problem is still the same.
I must be missing something fundamental here... please help with any pointers, answers or solution.
Change notification in a collection is a bit tricky. Say you have a collection of Products. you can implement change notification is three different places.
Change notification in the Product class (implementing INotifyPropertyChanged in class Product)
Change notification in the collection itself (i.e. using ObservableCollection)
Change notification in the class that holds the collection, that is, implementing INotifyPropertyChanged on the class that contains the collection. (usually this would be the ViewModel under MVVM)
Those tree ways are not the same, and each is for a different situation.
Let's say that the collection is ObservableCollection<Product> Products {get;set;}
if you want changes in the product to register (i.e., if you're doing something like Products[0].Name = "New Product"; then #1 is the right one.
If you want to do Products.Add(new Product(...)) then #2 is the right one.
If you want to do Products = new ObservableCollection<Product>() then #3 is the correct one. This is especially tricky since i'm not changing the collection, but creating a new one, so the ObservableCollection won't help - I'd need to implement INPC in the containing class.

ItemsControl that loads items one by one asynchronously

I am creating a custom DataGrid by deriving the traditional tookit based WPF DataGrid. I want a functionality in the grid to load items one by one asynchronously, wherein as soon as ItemsSource is changed i.e. a new collection is Set to the ItemsSource property or the bound collection is Changed dues to items that rae added, moved or removed (wherein the notifications comes to the data grid when the underlying source implements INotifyCollectionChanged such as ObservableCollection).
This is because even with virtualising stackpanel underneath the datagrid takes time to load (2-3 seconds delay) to load the data rows when it has several columns and some are template based. With above behavior that delay would "appear" to have reduced giving datagrid a feel that it has the data and is responsive enough to load it.
How can I achieve it?
Thx
Vinit.
Sounds like you are looking for data virtualization', which typically means creating your own custom type that resembles IList, and doing a lot of work to hydrate objects after-the-fact.
You will end up having your data that the grid is displaying look something like this:
Index 0: new MyDataObject(0);
Index 1: new MyDataObject(1);
And MyDataObject implements INotifyPropertyChanged.
In the constructor, you do the logic necessary to time, schedule, or interpret when the real results should be read. Until then, you return rather empty data... null and string.Empty from your properties.
Then, once the data becomes available (ideally in a background thread, read from wherever - your own local data, or a database or web service), then you can update the real underlying property values and fire the property change notifications so that the UI gets properly loaded then.
It's a little too complex to just jump into, so some searching will help. Hope this gets you started.

Resources