A better way of forcing data bound WPF ListBox to update?

60,395

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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 :)

Solution 5

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();
Share:
60,395
jmistx
Author by

jmistx

Utterly Insane developer

Updated on November 26, 2020

Comments

  • jmistx
    jmistx over 3 years

    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?