1
votes

Can't seem to get an ObservableCollection to update. The TreeView shows empty. Error: this type of collectionview does not support changes to its sourcecollection from a thread differentfrom the dispatcher thread.

Class level var

public ObservableCollection<TSItem> tsItems { get; set; }

Initialize Components

tsItems = new ObservableCollection<TSItem>();
bwRun.DoWork += bwRun_DoWork;
InitializeComponent();
tvTest.ItemsSource = tsItems;

from the background worker I use the following method to add to the collection

 private void AddTreeViewItem(TSItem item)
 {
     tsItems.Add(item);
 }

xaml

<TreeView x:Name="tvTest" HorizontalAlignment="Left" Height="249" Margin="140,36,0,0" VerticalAlignment="Top" Width="257">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Image Source="{Binding Icon}" />
                    <TextBlock Text="{Binding Header}" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
3
Do you actually start your background worker? I wasn't able to reproduce your issue. - Somedust
yes, updated post. The worker is started in the Loaded event. - Tsukasa
tsItems should be public... - Nahum

3 Answers

3
votes

You should dispatch changes to UI thread:

 private void AddTreeViewItem(TSItem item)
 {
    Dispatcher.BeginInvoke(new Action(() => tsItems.Add(item)));
 }
2
votes

You have to invoke back onto the UI thread before updating your observable collection.

As a shortcut to just get it working, consider Application.Dispatcher.Invoke or BeginInvoke.This requires a reference to PresentationCore so may not be great for MVVM if you want to be super pure about it.


If you want to be purer one way or another, you need to do a bit more work. Most of this involves trapping the SynchronizationContext of the UI thread.

  • Create your own implementation of INotifyCollectionChanged that wraps a SynchronizationContext and forwards the events of an internal ObservableCollection
  • Grab the SynchronizationContext in your constructor using SynchronizationContext.Current. This only works if your constructor is accessed on the UI thread.
  • Pass a SynchronizationContext into every class that needs ones.
1
votes

@Somedust is correct. I would also add that you say that you are updating the observable collection via the background worker. If you are doing that, you need to ensure that you are back on the UI thread when doing so.