我的应用程序有2个线程 - 主UI线程和另一个线程。
应用程序在单独的线程上收集文件名列表。然后我希望这个列表显示在我的GUI中。程序按照需要工作,直到线程完成。它会导致以下错误
"The calling thread must be STA, because many UI components require this."
运行以下代码,正如您所看到的,它需要2个Action Parameters - 一个用于AddCurrentFile,另一个用于Complete。在duplication.GetDuplicateList()
期间,它调用了2种方法。
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;
}
});
AddCurrentFile为我的属性提供了一个值,并根据需要使用INotifyProperty我的GUI更新:
private void Complete(List<Duplicate> duplicateList)
{
this._duplicateList = duplicateList;
}
问题是当我调用CreateParent(),因为这会创建一个新的UserControl - 然后我得到The calling thread must be STA, because many UI components require this.
但这发生在UserControl的构造函数中。以下代码是失败的地方
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;
}
因此,为了安抚线程,我在CreateParent()方法中添加了this._duplicatesThread.Join;
然而,我认为(并且请纠正/同意我)此时发生的事情是因为我加入了该线程,它实际上取消了非UI线程,因此停止了它的进程!因此,它永远不会在this._duplicatesThread.Join;
现在,我希望程序员不要问这个问题,但是,我觉得这里的问题更多的是我的设计而不是代码(虽然我很高兴知道模式和我的代码是他的垃圾。
我的问题是,由于我不能通过新线程(因为它不是UI线程)这样做,我需要做什么?这几乎就像我需要一个像NewThread.FinishedExecuting += DoThisThing()
答案 0 :(得分:3)
您应该将ViewModel与View(UI)分开,并使用WPF数据绑定将它们绑定在一起。实际上可以从后台工作线程更新ViewModel对象,WPF框架将自动封送数据绑定UI控件的INotifyPropertyChanged.PropertyChanged
通知(至少在.NET 4.5,AFAIK中)。这样,控件将根据需要在主UI线程上自动更新。
但是,我宁愿以与UI元素相同的方式处理ViewModel,即手动将所有ViewModel更新从后台线程封送到主UI线程。这可以使用Dispatcher.Invoke
(同步)或Dispatcher.InvokeAsync
(异步,推荐)来完成。
也可以使用Progress<T>
模式将更新传播到UI线程。这是一个相关的问题/答案:
How to execute task in the wpf background while able to provide report and allow cancellation?
答案 1 :(得分:0)
Thread thread = new Thread(MethodWhichCallesTheConstructor);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join(); //Wait for the thread to end