views:

5803

answers:

7

Hi,,

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?

+1  A: 

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

Timothy Khouri
Trust me, I can understand your frustration, so no offence taken.I have tried using GetBindingExpression but that retuls in a null :p
TimothyP
my "GetBindingExpression" doens't return null... but even if I call "UpdateTarget" it does nothing :( ... only in this case... it works great for everything else.
Timothy Khouri
+11  A: 

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.

Gishu
This does seem to solve the problem, so I'll move the bug question into another post. Thank you!
TimothyP
Thank you Gishu!
Artur Carvalho
+4  A: 

What is it an ObservableCollection of? Does that class implement INotifyPropertyChanged, and do you trigger PropertyChanged when your position value is updated?

Donnelle
Aha... so then I wouldn't have to refresh manually?
TimothyP
I added this now, but the .Refresh() is still required it would seem, I call it in the CollectionChanged event of the Observable list.What have I gained from implementing the interface?
TimothyP
I'm not sure I understand exactly what you're doing with the collection and the positions. I implemented a button to change the collection- the new list would show (and the position values were not updated in the UI). When I added PropertyChanged to the position getter, the position would display
Donnelle
Setter, not getter.
Donnelle
A: 

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.

A: 

Have a similar problem, a listbox ui_Log is bound to a property Log (simple property, not dependency):

// bind message list to log list box
Binding bindingLog = new Binding();
bindingLog.Source = ((MyAppType)Application.Current);
bindingLog.Path = new PropertyPath("Log");
bindingLog.Mode = BindingMode.OneWay;
ui_Log.SetBinding(ListBox.ItemsSourceProperty, bindingLog);

Log is a list of custom string wrappers (to make sure strings, even equals, are assigned a different storage space):

public List<StringWrapper> Log { get { return m_AppLog; } }

Then the list is extended by inserting new items at the top. As Log is not a dependency property, a manual change notification is triggered:

// add a log entry
public void AddToLog(string message) {
    // add this line at the top of the log
    m_AppLog.Insert(0, new StringWrapper(message));

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

Interestingly, the list is created and partially filled before the listbox is actually displayed. All items inserted before the first time the listbox is displayed are visible, but not the ones inserted after, the listbox doesn't update its items source.

rolling
+1  A: 

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.

rolling
+1  A: 

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
KP