Question

So I want to load my data collections in a background thread and then bind my treeview to the new collection (rather than having the background thread queue things up on the dispatcher every time it wants to add an item to the list [sounds inefficient]).

Is this possible? I create the new data structure and it is output as pd.result on a background thread. When the UI thread checks that the dialog box has closed, it should then set

ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;

after this the event OnLoadVCD is called. I have an event handler that then tries to set a treeview's itemsource to the new collection.

this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;

This crashes with the error: "The calling thread cannot access this object because a different thread owns it."

Not even sure how to debug it, the call stack doesn't give any real details.

However, if I just set the Itemsource to a blank new empty collection like so:

this.AvailableModulesTreeView.ItemsSource = (IEnumerable<object>)new List<object>();

it doesn't crash (but then it's not displaying my data either). Any ideas what could be causing the crash?

I thought it might be that I was updating the UI from the wrong thread, so I tried both calling the dispatcher with begininvoke, and checking that I am indeed the UI thread with dispatcher.checkaccess(). so that does not seem to be the issue. However, I really don't know what is going on.

Another way I could implement this is to just make my parsing routine just update the original data structure that is bound to the treeview by caling dispatcher on each item as it is added to the observable collection. However, even if that is the only solution, I really dislike not knowing why something doesn't work. In my mind, it seems reasonable to just create an entirely new data structure on a different thread, and then rebind the new datastructure to the treeview, discarding the old one. It also seems cleaner to me than dozens of 1 line ObservableCollectionInstance.Add calls being placed on the dispatcher while parsing through a file on the background thread.

Full Code:

method called by UI thread

public bool LoadPortInterface(string VCDFileName)
{
    ProgressDialog pd = new ProgressDialog("Loading File: ", VCDFileName);
    pd.Owner = Application.Current.MainWindow;
    pd.WindowStartupLocation = WindowStartupLocation.CenterOwner;
    ModuleHierarchyVM.TopLevelModules.Clear();


    VCDData TempVCDOutput = null;
    Func<object> handler = delegate
    {
        return VCDParser.ParseVCDFileForAllPorts(VCDFileName, this, pd.Worker, out TempVCDOutput);
    };
    pd.RunWorkerThread(handler);
    pd.ShowDialog();
    if (pd.DialogResult == true)
    {
        ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;
        VCDOutput = TempVCDOutput;
    }
    OnLoadVcd();
}

Response to OnLoadVCD event handler in graphviewer:

void gvvm_LoadVCDEvent(object sender, EventArgs e)
{
    this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;
}
Was it helpful?

Solution 2

You are most likely creating, reading or modifying the ObservableCollection on a thread other than the UI thread. Also make sure you are not adding to or removing from the ObservableCollection on anything other than the UI thread.

To debug this, put a breakpoint wherever you access/modify the observable collection and note the thread number (Thread window in VS) that hits that breakpoint. It should always be the same.

You could use another structure (List/Array) to hold the results, then call back to the UI thread to update/create the ObservableCollection. Updating the ObservableCollection is inexpensive, even for hundreds of items.

What does get expensive is that ObservableCollection will raise a change event on every change, which may be handled by the UI components to change their layout, which has to be done on the UI thread anyway. This UI event handling is why ObservableCollection prevents you from modifying across threads.

If you are adding/removing a large number of items, it may be better to create a new collection and reassign the DataSource. If you always do this, you can use a List instead. ObservableCollection is for when you want to modify the list and have the control only change the smallest amount possible. Changing the DataSource (eg to List) will clear the control and rebuild it, which may be better for many changes.

See:

Updating an ObservableCollection in a separate thread

How do I update an ObservableCollection via a worker thread?

What's the best way to update an ObservableCollection from another thread?

OTHER TIPS

I think it will be easer to use TPL "Task Parallel Library"

For example, if you want to create new thread you can create it as task.

var task = Task.Factory.StartNew(() =>
{
   // write what you want to do here
}

You can get more examples from this link Task Parallelism (Task Parallel Library)

So, to update the UI from thread, you can run this thread in the same of UI thread as below:

        var uiContext = TaskScheduler.FromCurrentSynchronizationContext();

        _taskFactoryWrapper.StartTask(() => DoSomeWork(viewSettings.AnyValue)).ContinueWith(task =>
        {

                viewSettings.Result= task.Result;

        },TaskContinuationOptions.AttachedToParent)
            .ContinueWith(t => EndingUploadingProgress(viewSettings), uiContext);

So you can create a TaskScheduler associated with the current UI thread.]

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top