31
votes

I have WPF ListBox which is bound to a ObservableCollection, when the collection changes, all items update their position.

The new position is stored in the collection but the UI does not update. So I added the following:

    void scenarioItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        ToolboxListItem.UpdatePositions();
        lstScenario.ItemsSource = null;
        lstScenario.ItemsSource = ToolboxListItem.ScenarioItems;
        this.lstScenario.SelectedIndex = e.NewStartingIndex;
    }

By setting the ItemsSource to null and then binding it again, the UI is updated,

but this is probably very bad coding :p

Suggestions?

8
Can you please give more detail on what you mean by "when the collection changes, all items update their position", just so I can be sure I am answering your question properly?Donnelle

8 Answers

76
votes

I have a Listbox bound to an object property which is of type List<MyCustomType>() and I verified that the following code updates the listbox when the List is updated.

void On_MyObjProperty_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
   MyListBox.Items.Refresh();
}

If you're still facing issues, scan the VS IDE output window (Ctrl+W, O) and see if you can spot any binding errors reported.

10
votes

WPF binding a list / collection of items to a ListBox, but UI not refreshing after items updated, Solved.

I'm just stupid. While I'd read a lot about using ObservableCollection<> instead of List<>, I just continued to ignore this suggestion and went following other suggestions, to no avail. Got back to my books and reread. It's pretty well explained that ObservableCollection<> is a must use because List<> doesn't provide the INotifyCollectionChange interface needed for the ListBox to update its display when the items change in the collection.

This is the updated code:

private ObservableCollection<StringWrapper> m_AppLog;
ObservableCollection<StringWrapper> Log { get { return m_AppLog; } }

Pretty simple, and doesn't require anything else (e.g. Refresh()). Because ObservableCollection takes care itself of triggering the change event, I was able to remove the unnecessary call:

// notify bound objects
OnPropertyChanged("Log");

ObservableCollection doesn't support an update by a thread which didn't create it. Because my list (a visual log to show the recent errors/info messages) can be updated from different threads, I add to adjust my code this way to ensure the update was done with the list's own dispatcher:

public void AddToLog(string message) {
    if (Thread.CurrentThread != Dispatcher.Thread) {
        // Need for invoke if called from a different thread
        Dispatcher.Invoke(
            DispatcherPriority.Normal, (ThreadStart)delegate() { AddToLog(message); });
    }
    else {
        // add this line at the top of the log
        m_AppLog.Insert(0, new StringWrapper(message));
        // ...

Also note that ObservableCollection<> doesn't support RemoveRange() contrary to List<>. This is part of the possible adjustments required when switching from List to ObservableCollection.

6
votes

I may be having a similar problem to what you are having, but I'm not sure.

I had an ObservableCollection<MyEntity> and a ListBox bound to it. But for some strange reason my ListBox was not being updated when I changed the properties of the MyEntity objects in the list.

After searching for a while I found the following page and I just had to let you know:

http://www.wblum.org/listbind/net3/index.html

It is a very good description of what you have to do to get a ListBox to update when the list, or the objects within it, changes. Hoping you will benefit from this.

4
votes

I had the same problem yesterday, and it's a complete piece of crap :) ... I'm not setting mine to null anymore though. In my scenario, I am setting it to MyList.ToArray() (after every time I add to the list).

I've seen multiple "oh, you need to use an ObservableList" <-- complete crap.

I've seen multiple "oh, call 'Refresh'" <-- complete crap.

Please forgive my upsettedness, but I also would expect this to work :)

3
votes

I know it's already a bit older but today I faced the same issue. I updated an property of an object inside an ObservableCollection and the View did not update, but then I found this awesome article.

I think it's a very clean solution to manually trigger the update of an ObservableCollection:

CollectionViewSource.GetDefaultView(this.myObservableCollection).Refresh();
2
votes

This is old stuff but, use an ObservableCollection. IF you want the UI to see updates to properties in the Objects of the ObservableCollection you need to implement INotifyPropertyChanged in the class defenition for that object. Then raise the property changed event in the setter of each property.

Public Class Session
Implements INotifyPropertyChanged

Public Event PropertyChanged As PropertyChangedEventHandler _
   Implements INotifyPropertyChanged.PropertyChanged

Private Sub NotifyPropertyChanged(ByVal info As String)
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub

Private _name As String = "No name"
''' <summary>
''' Name of Session
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property Name() As String
    Get
        Return _name
    End Get
    Set(ByVal value As String)
        _name = value
        NotifyPropertyChanged("Name")
    End Set
End Property
0
votes

If you have an ObservableList of objects, and you're changing properties inside those objects, the notification doesn't apply since the collection is not changing directly. I have been forcing notification after changing my object properties by using Insert() to re-add my changed object to the collection, then RemoveAt() to remove the old copy. It's not pretty, but it works.

0
votes

To me, it looks more like a bug in ListBox and ListView. I am binding to an ObservableCollection, the items in the collection implement INotifyPropertyChanged. The UI shows no added items when I dynamically press my 'add item' button however I have a counter control that is bound to MyCollection.Count. This counter control increments each time I press my 'add item' button. If I resize the view, the list box shows all my added items. So, the ItemSource binding on the ListBox control is broken. I also took care not to create a new MyCollection at any point, which would break the binding. Boo hoo.