【问题标题】:Changing label text in background worker Error在后台工作人员错误中更改标签文本
【发布时间】:2018-08-31 00:03:20
【问题描述】:

我试图在后台工作进程期间更改表单上的标签但是它显示未处理的异常。我查看了错误,它说要像接受的答案在这里所说的那样调用它:Update label text in background worker winforms

我已经通过更改复选框列表中的值成功地做到了这一点,但我使用了相同的方法,并且对于它不会调用的标签,当我键入它时,我在代码中得到红色错误行。

我的后台工作者:

private void bw2_DoWork(object sender, DoWorkEventArgs args)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    func.sshConnect();
    for (int num = 0; num < checklist.Items.Count; num++)
    {
        if (checklist.GetItemChecked(num))
        {
            string project = checklist.Items[num].ToString();
            lblStatus.Text = "Opening " + project + "..."; //error here
            if (func.svnCheckoutProject(project))
            {
                lblStatus.Text = project + " Opened"; //same error here
                func.sshRunCommand("echo " + project + " >> " + Properties.Settings.Default.serverUserFilesPath + Properties.Settings.Default.Username);
            }
            else
            {
                //error message
            }
        }
        worker.ReportProgress(num * (100 / checklist.Items.Count));
    }
}

我尝试用这个替换有错误的行,但在 Visual Studio 中它在调用下给它一个红线,并且不会让我构建它。

lblStatus.Invoke((MethodInvoker)delegate { lblStatus.Text = "Opening " + project + "..."; });

出现错误时,Visual Studio 将我指向此处:MSDN 我在复选框列表中使用了这种方法,它可以工作,但在标签上尝试它,它不起作用。

关于它为什么不起作用或其他方式的任何想法?

【问题讨论】:

    标签: c# winforms backgroundworker


    【解决方案1】:

    不要从DoWork 事件更新您的 UI 控件 - 您位于与 UI 线程不同的线程上。您可以调用 Invoke,但它确实不适合它。

    BackgroundWorker 已经提供了一个构造,用于在工作线程运行时定期更新 UI 线程,并且您已经在使用它 - 它是 ReportProgress 方法。那是您应该从中更新标签的地方。那里的任何东西都会在主 UI 线程上运行。


    您可以将任何您想要的object 传递给ReportProgess 方法:

    worker.ReportProgress(num * (100 / checklist.Items.Count),
                          string.Format("Opening {0} ...", project));
    

    然后将值转换回并在ProgressChanged 事件中使用:

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        var message = e.UserState.ToString();
    
        lblStatus.Text = message;
    }
    

    【讨论】:

    • 我知道我在一个单独的线程上,这就是代码的重点,所以它不会冻结我的表单。报告进度将 int 作为变量,我将如何向它发送字符串?后台工作人员会影响显示的字符串,因此必须从那里调用它,调用它的线程安全方式适用于复选框列表但不适用于标签,因此可以完成
    • 非常好,不是我想要的,但效果更好。我看到的唯一问题是在不需要时必须报告百分比,但这只是一个小偏好。谢谢,效果很好
    • 我已经在我的代码中进入了另一个层次。我的后台工作人员处理登录,因此我的表单不会冻结,问题是我现在想在后台进程期间更新控件(我已经使用百分比 uservar 作为状态标签)。最好是创建一个对象数组并通过 reportprogress 传递所需的信息,还是创建 2 个依次工作的后台工作人员?
    • 我知道我可以通过任何我想要通过的东西,但它是如何做到的,因为我已经发送了一些东西。我喜欢元组的外观并且不喜欢使用它们。再次感谢
    • 看着它在它被传递到报告进度后我将如何将它转换回一个元组?我似乎无法将其转换为获取值
    【解决方案2】:

    在大多数情况下,您会希望使用 BeginIvoke 而不是 Invoke

        public void changeLabelText(System.Windows.Forms.Label lib, String whateva)
        {
            if (lib.InvokeRequired)
            {
                lib.BeginInvoke(new MethodInvoker(() => changeLabelText(lib, whateva)));
            }
            else
            {
                lib.Text = whateva;
            }
        }
    

    在你的 DoWork 中调用方法

    private void bw_DoWork(object sender, DoWorkEventArgs args)
    {
    //do something
    //change the text of a label lb1
    changeLabelText(lb1, "I'm reporting some progress....");
    }
    

    【讨论】:

    • 使用 BackgroundWorker 时,应避免 UI 控件的 Invoke 或 BeginInvoke。根据MSDN :您必须小心不要在 DoWork 事件处理程序中操作任何用户界面对象。相反,通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。
    • 很多时候 ProgressChanged 和 RunWorkerCompleted 根本不够用。是的,您应该小心,因为在从多个线程更新控件或出现死锁时可能会得到意外结果,但是我可以很容易地将“应该小心”解释为这样做,但要小心而不是“你不应该这样做”完全没有。”同样,可能存在我不知道的危险。另一方面,我们可能都应该使用任务而不是后台工作者
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-09
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多