【问题标题】:Showing a progress bar that updates while creating a lot of controls在创建大量控件时显示更新的进度条
【发布时间】:2011-09-01 14:06:59
【问题描述】:

我编写了一段代码,用于创建大量控件并将它们布局在画布上以可视化一棵树。现在这段代码可能会花费很多时间,特别是因为它有时必须查询外部服务以查看是否有更多的子节点。

所以我想在这段代码执行时显示一个进度条。对于我程序的其他部分,我使用了一个后台工作人员来报告进度。但是,由于我必须创建以后可以交互的控件,所以我在这里看不到如何使用后台工作程序或其他线程解决方案。

由于这是 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 线程上创建控件并在那里执行所有操作,同时仍然很灵活。

【问题讨论】:

  • 我认为后台工作人员对此非常可行,只要您在添加控件时调用调用。
  • 使用 BackgroundWorker 或单独的线程并调用 Invoke()/BeginInvoke() 是正确的方法。如果您遇到线程异常,您可以发布您的代码吗?另外,您确定要在启动“工作...”线程之前创建进度条吗?
  • 嗨 havardhu 和 David Lively,感谢您的提示。我知道 Invoke/BeginInvoke。但这仍然会让我在后台线程上创建一个控件,所以我应该花一些时间来解决这个问题。你们如何看待使用 progressChanged 事件来执行由其他线程提供的委托来执行这些操作?
  • 三个 1 秒线程睡眠调用的目的是什么?
  • @Valentin Kuzub,模拟做实际工作,这只是测试代码,看看这个想法是否有效:)

标签: c# wpf progress-bar multithreading


【解决方案1】:

一般我不经常使用 BackgroundWorker,但我可以建议以下:

DoWork 的逻辑 - 它在非 UI 线程上执行

  1. 获取节点数,以便报告实际进度
  2. 开始构建树(并在UI Dispatcher 上调用Invoke 所以UI 线程 正在添加节点)并向ReportProgress 报告进度(已经 添加节点)/(节点总数)同时枚举所有节点

ProgressChanged 中只需用新值更新一些 ProgressBar

【讨论】:

    猜你喜欢
    • 2012-05-18
    • 1970-01-01
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 2012-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多