【问题标题】:Weird behavior of Background worker and multiple progress bar c#后台工作人员和多个进度条c#的奇怪行为
【发布时间】:2013-03-28 15:10:10
【问题描述】:

我尝试模拟一个运行时间较长的过程。在我的应用程序中,我使用背景并动态添加进度条。当我点击开始按钮时,一个进程开始并在面板上添加了一个进度条。如果我只单击一次,则一切正常,但如果我单击两次或更多,则添加该数量的进度条,但最后一个进度条进度增加,最后其他进度条进度突然增加。

我希望添加任何数量的进度条都应该同时处理,而不是最后一个和最后一个。一切都应该像许多进程在自己的上下文中同时运行一样一起运行。这是我的代码,我希望如果有人看一下,他们就能理解缺陷在哪里。请帮我修复最后添加的进度条进度增加的缺陷。如果可能,运行我的代码并查看问题。

public partial class Form2 : Form
{
    MyBackgroundWorker backgroundWorker1 = null;
    Dictionary<string, string> dct = new Dictionary<string, string>();
    public Form2()
    {
        InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {

    }

    private void btnStart_Click(object sender, EventArgs e)
    {
        backgroundWorker1 = new MyBackgroundWorker();
        backgroundWorker1.WorkerReportsProgress = true;   
        backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
        backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
        backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
        backgroundWorker1.RunWorkerAsync(txtNumber.Text);

    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        string strID = DateTime.Now.ToString("yyyyMMdd") + DateTime.Now.ToString("hhmmss")+DateTime.Now.Millisecond.ToString();
        MyBackgroundWorker tmpBg = (sender as MyBackgroundWorker);
        tmpBg.Name = "bg_" + strID;

        ProgressBar pb = new ProgressBar();
        pb.Minimum = 0;
        pb.Maximum = 100;
        pb.Name = "pb_" + strID;
        pb.Width = txtNumber.Width;
        this.BeginInvoke((MethodInvoker)delegate
        {
            flowLayoutPanel1.Controls.Add(pb);
        });

        dct.Add(tmpBg.Name, pb.Name);

        int input = int.Parse(e.Argument.ToString());
        for (int i = 1; i <= input; i++)
        {
            Thread.Sleep(2000);
            backgroundWorker1.ReportProgress(i * 10);
            if (backgroundWorker1.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }
    }

    // This event handler updates the progress bar. 
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        MyBackgroundWorker tmpBg = (sender as MyBackgroundWorker);
        string strName = tmpBg.Name;

        if (!string.IsNullOrEmpty(strName))
        {
            strName = strName.Substring(3);
            ProgressBar pb = (ProgressBar)this.flowLayoutPanel1.Controls.Find(("pb_" + strName), true)[0];
            pb.Value = e.ProgressPercentage;
        }
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MyBackgroundWorker tmpBg = (sender as MyBackgroundWorker);
        string strName = tmpBg.Name;
        strName = strName.Substring(3);
        ProgressBar pb = (ProgressBar)this.flowLayoutPanel1.Controls.Find(("pb_" + strName), true)[0];
        pb.Value = 100;
        //MessageBox.Show("Done");
    }

}

    public class MyBackgroundWorker : System.ComponentModel.BackgroundWorker
        {
            public MyBackgroundWorker()
            {
            }

            public MyBackgroundWorker(string name)
            {
                Name = name;
            }

            public string Name { get; set; }
        }

更新部分

 private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MyBackgroundWorker tmpBg = (sender as MyBackgroundWorker);
            string strName = tmpBg.Name;
            strName = strName.Substring(3);
            ProgressBar pb = (ProgressBar)this.flowLayoutPanel1.Controls.Find(("pb_" + strName), true)[0];
            //pb.Value = 100;
            //MessageBox.Show("Done");
            flowLayoutPanel1.Controls.Remove(pb);
        }

【问题讨论】:

  • 您可能不应该在单击按钮时将事件处理程序添加到后台工作程序。或者至少检查它是否为空并添加这种情况。尝试将它们添加到 Form2_Load 而不是 btnStart_Click
  • 是的,我做了整个后台工作者相关的代码,我移到了 form_load。我只是调用 RunWorkerAsync(txtNumber.Text);从我第一次单击时的 btnstart_click 事件开始,然后在我下次单击时就可以了,我收到错误“此 BackgroundWorker 当前很忙,无法同时运行多个任务。”我想在按钮上单击 N 次,应该没有任何问题,这就是为什么我每次都从 button_click 事件创建一个新的 BackgroundWorker 实例。有任何建议来更改代码,使我能够单击按钮 N 次而不会出现任何错误。
  • 对不起,我误解了你在做什么

标签: c# progress-bar backgroundworker


【解决方案1】:

我想我已经按照你想要的方式工作了。

  1. 在 ProgressChanged 中,我以另一种方式获取进度条。

  2. 在 DoWork 中,我以百分比和当前 backgroundWorker 报告进度。

  3. 我删除了Thread.Sleep,因为我认为它造成了问题,在DoWork 中添加了Console.WriteLine 以减慢程序速度(否则它比ProgressBar 渲染速度更快)

    public partial class Form2 : Form
    {
        MyBackgroundWorker backgroundWorker1 = null;
        Dictionary<string, string> dct = new Dictionary<string, string>();
        public Form2()
        {
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
    
        }
    
        private void btnStart_Click(object sender, EventArgs e)
        {
            backgroundWorker1 = new MyBackgroundWorker();
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
            backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
            backgroundWorker1.RunWorkerAsync(int.Parse(txtNumber.Text));
    
        }
    
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            string strID = DateTime.Now.ToString("yyyyMMdd") + DateTime.Now.ToString("hhmmss") + DateTime.Now.Millisecond.ToString();
            MyBackgroundWorker tmpBg = (sender as MyBackgroundWorker);
            tmpBg.Name = "bg_" + strID;
    
            ProgressBar pb = new ProgressBar();
            pb.Minimum = 0;
            pb.Maximum = 100;
            pb.Name = "pb_" + strID;
            pb.Width = txtNumber.Width;
            this.BeginInvoke((MethodInvoker)delegate
            {
                flowLayoutPanel1.Controls.Add(pb);
            });
    
            dct.Add(tmpBg.Name, pb.Name);
    
            int input = (int)e.Argument;
            for (int i = 1; i <= input; i++)
            {
                Console.WriteLine(tmpBg.Name + ": " + (i * 100 / input));   //for testing (this line slows the program down a lot)
                tmpBg.ReportProgress(i * 100 / input);
                if (tmpBg.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }
            }
        }
    
        // This event handler updates the progress bar. 
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            MyBackgroundWorker tmpBg = (sender as MyBackgroundWorker);
            string strName = tmpBg.Name.Substring(3);
            ProgressBar pb;
    
            if (!string.IsNullOrEmpty(strName))
            {
                foreach (Control c in flowLayoutPanel1.Controls)
                {
                    if (c.Name == ("pb_" + strName))
                    {
                        pb = (ProgressBar)c;
                        pb.Value = Math.Min(100,e.ProgressPercentage);
                    }
                }
            }
        }
    
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MyBackgroundWorker tmpBg = (sender as MyBackgroundWorker);
            string strName = tmpBg.Name;
            strName = strName.Substring(3);
            ProgressBar pb = (ProgressBar)this.flowLayoutPanel1.Controls.Find(("pb_" + strName), true)[0];
            pb.Value = 100;
            //MessageBox.Show("Done");
        }
    
    }
    
    public class MyBackgroundWorker : System.ComponentModel.BackgroundWorker
    {
        public MyBackgroundWorker()
        {
        }
    
        public MyBackgroundWorker(string name)
        {
            Name = name;
        }
    
        public string Name { get; set; }
    }
    

【讨论】:

  • 感谢您的回答,但有时这一行 ProgressBar pb = (ProgressBar)this.flowLayoutPanel1.Controls.Find(("pb_" + strName), true)[0];当我添加太多进度条单击按钮时,将索引抛出异常。如何解决?
  • 我目前正在修改我发现的更多内容
  • 我们知道 RunWorkerCompleted 事件在 backgroundWorker1 完成工作时触发。所以从那里我尝试动态删除进度条并看到在完全更新进度条之前进度条被删除了。你能看看我的 RunWorkerCompleted 事件吗?谢谢
  • 我看到很多人在完全更新进度条时遇到问题。在 win 7 中,进度条非常慢,但我没有修复。所以如果你有任何技巧来显示带有完整绿色部分的进度条,然后在几秒钟后删除。
  • 是的,我也有这个问题。我能想到的唯一解决方案是创建一个计时器,根据后台工作人员命名,并在后台工作人员完成时启动它。在滴答事件中,通过计时器名称找到进度条,然后将其删除。让计时器等待 2 秒。 Timer 似乎没有可编辑的Name 字段。你可能不得不用它来做你对 BackgroundWorker 所做的事情
猜你喜欢
  • 2023-03-16
  • 2011-10-07
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多