Reputation: 8461
My application has 2 threads - the main UI thread, and another thread.
The application collects a List of file names on the separate thread. I then want this list to be displayed in my GUI. The program works as desired until the thread completes. It results in the following error
"The calling thread must be STA, because many UI components require this."
The following code is run, and as you can see it takes 2 Action Parameters - one for AddCurrentFile and one called Complete. During the duplication.GetDuplicateList()
it invokes the 2 methods.
this._duplicatesThread = new Thread(() =>
{
try
{
duplication.GetDuplicateList(new Action<string>(AddCurrentFile), new Action<List<Duplicate>>(Complete));
CreateParent();
}
catch (Exception ex)
{
string s = ex.ToString();
throw;
}
});
The AddCurrentFile provides a value to my property, and using the INotifyProperty my GUI updates as desired:
private void Complete(List<Duplicate> duplicateList)
{
this._duplicateList = duplicateList;
}
The issue is when I called the CreateParent() as this creates a new UserControl - I then get The calling thread must be STA, because many UI components require this.
But this occurs during the constructor of the UserControl. The following code is where it fails
private void CreateParent()
{
ObservableCollection<DuplicateControl> parent = new ObservableCollection<DuplicateControl>();
foreach (var duplicate in this._duplicateList)
{
DuplicateControl ic = new DuplicateControl();//This fails as soon as I enter the constructor. The DuplicateControl uses the UserControl as a base class
parent.Add(ic);
}
this.ParentDuplicate = parent;
}
So, to appease the thread, within the CreateParent() method I added this._duplicatesThread.Join;
However, I think (and please do correct/agree with me) what is happening at this point is because I'm joining within the thread, it's effectively cancelling the non-UI thread, therefore stopping at that point of it's process! As such, it never executes the line after this._duplicatesThread.Join;
Now, I'm hoping this isn't better asked at Programmers, but, I get the feeling the issue here is more of my design than the code (although I'd be delighted to know that both the pattern and my code is rubbish he he).
My question is, since I can't do this via the new Thread (as it's not a UI thread), what do I need to do? It's almost as if I need a delegate like NewThread.FinishedExecuting += DoThisThing()
Upvotes: 3
Views: 194
Reputation: 61736
You should have separated the ViewModel from the View (UI) and use WPF data-binding to bind them together. It's actually possible to update the ViewModel objects from a background worker thread, the WPF framework will automatically marshal INotifyPropertyChanged.PropertyChanged
notifications for the data-bound UI controls (at least, in .NET 4.5, AFAIK). This way, the controls will be updating themselves automatically on the main UI thread, as desired.
However, I'd rather treat the ViewModel in the same way as a UI element, i.e., manually marshal all ViewModel updates from the background thread to the main UI thread. This can be done with Dispatcher.Invoke
(synchronously) or Dispatcher.InvokeAsync
(asynchronously, recommended).
It's also possible to use Progress<T>
pattern to propagate updates to the UI thread. Here is a related question/answer:
How to execute task in the wpf background while able to provide report and allow cancellation?
Upvotes: 3
Reputation: 10287
Thread thread = new Thread(MethodWhichCallesTheConstructor);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join(); //Wait for the thread to end
Upvotes: 0