WPF binding not notifying of changes - wpf

I have a WPF sorting/binding issue. (Disclaimer: I am very new to WPF and databinding so apologise if I am asking a really dumb question :-))
Firstly, I have a linqToSql entity class Contact with an EntitySet<Booking> property Bookings on it.
If I directly bind this Bookings property to a ListView, the application seems to correctly notify of changes to the selected item in the ListView, such that a textbox with {Binding Path=Bookings/Comments} updates correctly.
// This code works, but Bookings is unsorted
var binding = new Binding();
binding.Source = contact.Bookings;
bookings.SetBinding(ItemsControl.ItemsSourceProperty, binding);
However, as I can't seem to be able to find a way to sort an EntitySet (see this post), I am trying to bind instead to an Observable collection, e.g:
// This code doesn't notify of selected item changes in the ListView
var binding = new Binding();
binding.Source = new ObservableCollection<Booking>(contact.Bookings.OrderByDescending(b => b.TravelDate).ToList());
bookings.SetBinding(ItemsControl.ItemsSourceProperty, binding);
But this doesn't seem to notify the comments textbox correctly such that it updates.
If anyone has a solution for either sorting the data before or after its bound, or another solution that will work that would be much appreciated.

You should bind to a CollectionView rather than the collection itself. That will allow you to specify whatever sorting criteria you require. Example:
var collectionView = new ListCollectionView(contact.Bookings);
collectionView.SortDescriptions.Add(new SortDescription("TravelDate", ListSortDirection.Ascending));
var binding = new Binding();
binding.Source = collectionView;
bookings.SetBinding(ItemsControl.ItemsSourceProperty, binding);

Hainesy,
Does the Booking object implement INotifyPropertyChanged to notify change in Comments property?
If not, you cannot expect TextBox which is bound to Comments property to be updated automatically when Comments change
Using ObservableCollection in this case will only get you the benefit of updating the view with changes when Booking objects are added or deleted from the collection
-Rajesh

Related

Adding items to ILIst as sourcecollection of a ListCollectionView

According to the documentation, you have to implement INotifyCollectionChanged on the source collection of a ListCollectionView to propagate adding/removing of collections items.
So I don't understand how his is working:
var parent = new Parent();
parent.Childs = new List<Child>();
parent.Childs.Add(new Child());
parent.Childs.Add(new Child());
parent.Childs.Add(new Child());
var view = new ListCollectionView(parent.Childs);
Assert.AreEqual(3, parent.Childs.Count);
Assert.AreEqual(3, view.Count);
parent.Childs.Add(new Child());
Assert.AreEqual(4, parent.Childs.Count);
Assert.AreEqual(4, view.Count);
Please, can anybody explain how this is working?
Why wouldn't this work? You are asserting after you have added the Child items. The Count property will always return the corrent number of items.
The reason why you need to implement the INotifyCollectionChanged interface is for a view to be able to know when it should refresh the UI.
For example, if you add an item to a data-bound collection that you are displaying in a ListView, the source collection needs to raise the CollectionChanged event for the new item to automatically show up in the view.

Can databinding in WPF be done simply with a dataview like in winforms?

Short and simple question. In Winforms you can bind a dataview to a combobox or some other control by simply:
combobox.DataSource = dataview
combobox.DisplayMember = "Something"
In WPF I've generally done databinding using an ObservableCollection and edits to the xaml. Is there a way to quickly do it, like the above?
Edit:
This seems to be the simplest/quickest thing I can come up with, anything inherently wrong with it?
combobox.ItemSource = dataview
combobox.DisplayMemberPath = "Something"
You can set binding programmatically, although, per my understanding of the MVVM pattern, the best practice is to set binding in the View (xaml) not the ViewModel or View code-behind.
How to set binding programmatically:
Binding myBinding = new Binding("Name");
myBinding.Source = dataview // data source from your example
combobox.DisplayMemberPath = "Something"
combobox.SetBinding(ComboBox.ItemsSourceProperty, myBinding);
With this, when your dataview is updated, the updates will be shown in your ComboBox.
You can do this:
List<Person> someListOFPersons = new List<Person>();
comboBox.DataContext = someListOfPersons;
comboBox.DisplayMemberPath = "FirstName";
You will not see changes in the collection. So if a person is added to or removed from the list the combobox will not see it.

Observable Collection is not updating the datagrid

I am using a Dim All_PriceLists As System.Collections.ObjectModel.ObservableCollection(Of BSPLib.PriceLists.PriceListPrime) where PriceListPrime implements Inotify for all properties in it.
I bound the All_PriceList to a datagrid as DataGrid1.ItemsSource = All_PriceLists but when I do All_PriceLists=Getall() where Getall reads and gets the data from the DB, the datagrid is not updating.
It updates only when I hack it this way:
DataGrid1.ItemsSource = Nothing
DataGrid1.ItemsSource = All_PriceLists
Could you please tell me where I have gone wrong or what I should implement. Thank you.
You have several solutions to your problem
Update the ItemsSource directly (instead of replacing the local member variable)
DataGrid1.ItemsSource = new ObservableCollection(Of PriceListPrime)(GetAll())
Update the ObservableCollection (as mentioned in another answer)
All_PriceList.Clear();
For Each item in Getall()
All_PriceList.Add(item)
Next
Set your DataContext to a view model and bind to a property of the view model
Dim vm as new MyViewModel()
DataContext = vm
vm.Items = new ObservableCollection(Of PriceListPrime)(GetAll())
The view model will implement INotifyPropertyChanged and raised the PropertyChanged event when the Items property is changed. In the Xaml your DataGrid's ItemsSource will bind to the Items property.
The problem is that you are not updating the collection, you are replacing it, which is different.
The datagrid remains bound to the old list and the updated data is stored in a new unbound collection. So, you are not hacking a solution, your are binding the datagrid to the new collection, which is correct.
If you want a more automatic solution, you should bind your datagrid to a dataset/datatable, which is totally different code.
You should update ObservableCollection instead of creating new one if you want the app to react on your changes.
So, clear All_PriceList collection and add new items into it. Example:
All_PriceList.Clear();
For Each item in Getall()
All_PriceList.Add(item)
Next
ObservableCollection doesn't support AddRange, so you have to add items one by one or implement INotifyCollectionChanged in your own collection.

Updating IValueConverter through code

I'm trying to figure out if its possible to update an IValueConverter through the code behind.
My situation is that I've got two ComboBoxes. Once the first one is updated, I change the ItemsSource property of the second to be one of a variety of enums. I've grabbed an EnumToFriendlyNameConverter from CodeProject, but I'm not sure how to set it.
If I set the converter in the ItemsSource (see below) then it gets ignored when I next set the items source.
ItemsSource="{Binding Converter={StaticResource enumItemsConverter}}"
I found that it is possible by using an ItemTemplate but then I have to manually place in a label, which then has a different style to my other combobox. Getting the styles right just seems like a lot of work...
When you change the ItemsSource you just have to apply the converter again or modify the ItemsSource instead of replacing it.
e.g. create a new binding:
private void ChangeItemsSouce(IEnumerable newItems)
{
Binding binding = new Binding();
binding.Source = newItems;
binding.Converter = new EnumToFriendlyNameConverter();
comboBox.SetBinding(ComboBox.ItemsSourceProperty, binding);
}
Or modify the existing binding:
private void ChangeItemsSouce(IEnumerable newItems)
{
var binding = comboBox.GetBindingExpression(ComboBox.ItemsSourceProperty);
binding.ParentBinding.Source = newItems;
}

WPF bindings not refreshing

I have a listbox on my form that looks like this:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged"/>
I am binding the following collection to it:
ObservableCollection<Hand> oHands = new ObservableCollection<Hand>();
using the following code:
lbResults.DataContext = oHands;
Binding binding = new Binding();
lbResults.SetBinding(ListBox.ItemsSourceProperty, binding);
The oHands collection gets populated via a background worker that announces via an event whenever a new Hand object is available. The ListBox refreshes perfectly when something is added. The ToString() result of the Hand object is displayed and that is what I want - so far so good. However, when the background worker finishes
void finder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
oHands = new ObservableCollection<Hand>(oHands.OrderBy(o => o.PotSize));
lbResults.SetBinding(ListBox.ItemsSourceProperty, new Binding());
}
The items in the list are still showing up in the original order. I can confirm that the list is re-ordered but the items are still showing up in the original order. How do I refresh this binding?
you dont want to assign oHands a new collection. just Clear() the collection then add the results from the operation. don't update the binding
instead of replacing the entire observable collection, you could just clear it and add all your new items. that wouldn't affect your binding.
You could also use a CollectionViewSource as your binding, and set the order on that instead of reordering the whole collection.
Wouldn't it be a lot easier to just set the itemsource directly?
lbResults.ItemsSource = oHands;
You're really just supposed to inherit from the INotifyPropertyChanged interface, but heres another way to force an update to a binding:
BindingExpression exp = BindingOperations.GetBindingExpression(lbResults, Listbox.ItemsSourceProperty)
exp.UpdateTarget()
Edit: I also just noticed you aren't setting any binding in the XAML and appear to be doing it programmatically with an empty Binding. I haven't tried that way before, so see if changing your XAML to this might help:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged" ItemsSource="{Binding Path=oHands}"/>
Then you set lbResults.DataContext to point to the class that has the member oHands. This is what worked for me in my project (in IronPython, so forgive me if my examples didn't convert to C# perfectly).

Resources