我编写了一段代码,可以在画布上创建大量控件并将它们布局,以显示树。现在这段代码可能需要花费很多时间,特别是因为它有时必须查询外部服务以查看是否有更多的子节点。
所以我想在执行此代码时显示进度条。对于我的程序的其他部分,我使用报告进度的后台工作程序。但是,由于我必须创建以后可以交互的控件,所以我不会在这里看到如何使用后台工作程序或其他线程解决方案。
由于这是WPF,我也无法调用Application.DoEvents()。所以我的问题是,如何在能够定期更新GUI的可视部分的同时创建大量控件?
对于我的其他代码,我使用的是我在应用程序繁忙部分布局的Adorner,我更喜欢可以继续使用的解决方案,我还是更喜欢使用BackgroundWorker的解决方案,但我很确定这是不可能的。
我看过其他SO主题,但到目前为止我找不到一个好的答案
Creating controls in a non-UI thread
Creating a WinForm on the main thread using a backgroundworker
编辑:
根据这篇MSDN文章http://msdn.microsoft.com/en-us/magazine/cc163328.aspx,如果需要,BackgroundWorker应该自动在UI线程上异步调用,但这不是我看到的行为,因为我仍然看到一个跨线程异常。
Edit2:nvm,这不完全正确:BackgroundWorker still needs to call Invoke?
Edit3:经过一些阅读和一些提示后,这就是我的解决方案。有人得到任何提示/提示吗?
// Events for reporting progress
public event WorkStarted OnWorkStarted;
public event WorkStatusChanged OnWorkStatusChanged;
public event WorkCompleted OnWorkCompleted;
private BackgroundWorker worker;
private delegate void GuiThreadWork(object state);
private PopulatableControlFactory factory = new PopulatableControlFactory();
public Canvas canvas;
public void PerformLayout(TreeNode node)
{
OnWorkStarted(this, "Testing");
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync(node);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
OnWorkCompleted(this);
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var workTuple = (Tuple<GuiThreadWork, TreeNode>)e.UserState;
workTuple.First.Invoke(workTuple.Second); //Or begin invoke?
if (OnWorkStatusChanged != null)
OnWorkStatusChanged(this, e.ProgressPercentage);
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
TreeNode node = (TreeNode)e.Argument;
Thread.Sleep(1000);
worker.ReportProgress(33, Tuple.New(Place(node), node));
Thread.Sleep(1000);
worker.ReportProgress(66, Tuple.New(Place(node.children[0]), node.children[0]));
Thread.Sleep(1000);
worker.ReportProgress(100, Tuple.New(Place(node.children[1]), node.children[1]));
}
private GuiThreadWork Place(TreeNode node)
{
GuiThreadWork threadWork = delegate(object state)
{
PopulatableControl control = factory.GetControl((TreeNode)state);
Canvas.SetLeft(control, 100);
Canvas.SetTop(control, 100);
canvas.Children.Add(control);
};
return threadWork;
}
简而言之:我使用后台工作程序的progressChanged事件,因为它总是被编组到GUI线程。我传递了一个代表和一些状态的元组。这样我总是在GUI线程上创建控件并在那里执行所有操作,同时仍然保持灵活性。
答案 0 :(得分:0)
通常我不经常使用BackgroundWorker,但我可以建议如下:
DoWork
的逻辑 - 它在非UI线程上执行
Invoke
上调用UI Dispatcher
以便UI线程
正在添加节点)并将进度报告给ReportProgress
(已经
添加节点)/(总计数节点),同时枚举所有节点ProgressChanged
中的只需使用新值
更新一些ProgressBar