【问题标题】:Dynamic Label Text within a for loopfor循环中的动态标签文本
【发布时间】:2015-08-18 00:48:30
【问题描述】:

我在 C# 中做了一个简单的 WF,试图动态更改标签。

但是,当我运行此代码时,没有可见的变化,直到代码运行后,它才变为“处理 9”,0-8 从未显示。是因为它在循环内吗?

 private void button1_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < 10; i++)
        {
            label9.Text = "Processing " + i.ToString();
            Thread.Sleep(1000);
        }
    }

编辑:X-Tech 的代码有效,但是当我尝试将其合并到我的代码中时,我遇到了以下线程问题。我正在尝试在进度条摇摆时动态更改循环中的 label.text:

System.Windows.Forms.dll 中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理 附加信息:跨线程操作无效:控件“progressBar1”从创建它的线程以外的线程访问。 如果有这个异常的处理程序,程序可以安全地继续

我不知道如何解决这个问题,因为我对 Task.Factory.StartNew() 和 Invoke 语句不是很熟悉。

编辑 2:改编代码:

 Task.Factory.StartNew(() =>
 {
      for (int i = 0; i < lstFilesToZip.Items.Count; i++)
      {
          zipArray[i] = lstFilesToZip.Items[i].ToString();
          // Report progress.
          ExecuteSecure(() => label9.Text = "Processing :" + Path.GetFileNameWithoutExtension(lstFilesToZip.Items[i].ToString()));
          ExecuteSecure(() => progressBar1.PerformStep());

          string zipFileName = Path.GetFileName(zipArray[i]);
          string newFolder = txtDestinationFolder.Text + "\\" + Path.GetFileNameWithoutExtension(zipArray[i]);
          //check to see if a directory with the file name for the zip file already exists, if not, it will create it
          if (Directory.Exists(newFolder) == false)
          {
                DirectoryInfo newPath = Directory.CreateDirectory(newFolder);
                if (lstCommonFiles.Items.Count > 0)
                {
                     //copies each file in the common file list to each new folder created, the boolean indicates that it will overwrite existing files (true) or won't (false)
                     for (int k = 0; k < lstCommonFiles.Items.Count; k++)
                     {
                          File.Copy(lstCommonFiles.Items[k].ToString(), (newFolder + "\\" + Path.GetFileName(commonArray[k])), true);
                     }
                }
                //adds the zip file into the folder as well
                File.Copy(zipArray[i], (newFolder + "\\" + zipFileName), true);

          }
    }

    if (txtCommonFiles.Text.Length <= 0)
    {
          string result = "There are no common files selected, would you still like to proceed?";
          DialogResult result1 = MessageBox.Show(result, "Common Files Missing", MessageBoxButtons.YesNo);
          if (result1 == DialogResult.No)
          {
               return;
          }
    }
    string[] dirs = Directory.GetDirectories(txtDestinationFolder.Text);

    // Grabs the folders in the newly created directory
    foreach (string dir in dirs)
          lstDestinationFinal.Items.Add(dir);

    //send sample file contents to preview window
    string[] sampleFiles = Directory.GetFiles(lstDestinationFinal.Items[0].ToString());

    //grabs the files
    foreach (string zipFiles in sampleFiles)
         lstSampleFile.Items.Add(zipFiles);

    ExecuteSecure(() =>tabControl1.SelectedTab = tabPage2);
});

}

以及添加的方法:

 private void ExecuteSecure(Action action)
    {
        if (InvokeRequired)
        {
            Invoke(new MethodInvoker(() => action()));
        }
        else
        {
            action();
        }
    }

【问题讨论】:

  • 那是因为你在 UI 线程中做工作,所以 UI 被冻结直到方法完成。使用 BackgroundWorker 及其 ReportProgress 实时更新您的标签。
  • @Darw1n34 您在哪一行遇到异常?
  • @X-TECH 我认为我们的电线交叉了,我感谢你并告诉你它有效:)
  • @Darw1n34 你的意思是它现在可以工作了吗?
  • @X-TECH 好的,谢谢。

标签: c# for-loop label


【解决方案1】:

试试这个

Task.Factory.StartNew(() =>
{
     for (int i = 0; i < 10; i++)
     {              
          Invoke(new MethodInvoker(() => label9.Text = "Processing " + i.ToString()));
          Thread.Sleep(1000);
     }
});

编辑:

Task.Factory.StartNew(() =>
{
     for (int i = 0; i < 10; i++)
     {
          // Any GUI control which you want to use within thread,
          // you need Invoke using GUI thread. I have declare a method below for this
          //Now use this method as
          ExecuteSecure(() => label9.Text = "Processing " + i);
          ExecuteSecure(() => progressBar1.Value = i * 10);
          //... other code etc.
          Thread.Sleep(1000);
     }
});


//---
private void ExecuteSecure(Action action)
{
    if (InvokeRequired)
    {
        Invoke(new MethodInvoker(() => action()));
    }
    else
    {
        action();
    }
}

【讨论】:

  • 是的,我想我是在用 BackgroundWorker 踢它的老派......:p
  • @x-tech 你能看看我对我的 OP 所做的编辑吗?
  • @Darw1n34 我已经编辑了我的帖子,请检查。现在你需要把 GUI 控件放到ExecuteSecure 方法来避免CrossThreadException
  • @X-Tech 这就是我所缺少的,我一直在玩弄 if(InvokeRequired) 但在我的循环中这样做,而不是作为一个单独的方法。
  • @X-TECH 谢谢,我发布了我的代码的相关部分。希望这对其他人有帮助。
【解决方案2】:

如果您希望您的 UI 实时更新,请使用 BackgroundWorker,否则所有工作都在 UI 线程上完成并保持冻结状态直到工作结束。

private void button1_Click(object sender, EventArgs e)
    {
        int limit = 10;
        var bw = new BackgroundWorker();
        bw.WorkerReportsProgress = true;
        bw.DoWork += (s, ee) =>
        {
            for (int i = 0; i < limit; i++)
            {
                bw.ReportProgress(i);
                Thread.Sleep(1000);
            }
        };
        bw.ProgressChanged += (s, ee) => label9.Text = "Processing" + ee.ProgressPercentage.ToString();
        bw.RunWorkerAsync();
    }

【讨论】:

    【解决方案3】:

    不妨试试

     private void button1_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < 10; i++)
        {
            label9.Text = "Processing " + i.ToString();
            Thread.Sleep(1000);
            Form1.Refresh();
        }
    }
    

    【讨论】:

    • 这可能行得通,但这是一个非常糟糕的做法。
    • 这是一个糟糕的解决方案,因为它会占用 GUI 10 秒。有关如何正确操作的提示,请参阅@Kilazur 对问题的评论。
    • @Kilazur - 将阅读有关 Refresh() 的不良做法。这对我有用。
    【解决方案4】:

    试试这个:

    private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10; i++)
            {
                label9.Text = "Processing " + i.ToString();
                Application.DoEvents();
                Thread.Sleep(1000);
            }
        }
    

    【讨论】:

    • 这一次会占用 GUI 线程一秒钟,十次,而且它使用了Application.DoEvents,这很危险,因为它允许重入。例如,如果用户在等待处理第一次单击时再次单击 button1,会发生什么情况?最好使用 BackgroundWorker 并正确禁用相应的控件。
    • 多年前,我向一位高级开发人员提出了同样的问题,他给了我这个答案。当然,您应该使用线程并正确执行,但当时我还没有那么先进,他理解这一点。有时当开始学习时,答案不可能是爬山。
    • X-TECH 发布的已接受答案以更少的代码行数解决了 OP 的问题,同时避免了挂起和DoEvents 重入错误,而且理解起来并不比你自己的难. (尝试向初学者解释 Application.DoEvents() 的实际作用。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    • 2018-01-03
    • 2018-03-25
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    相关资源
    最近更新 更多