从后台工作者更新GUI

Ale*_*lex 6 c# multithreading backgroundworker winforms

问题的名称是:" 从后台工作者更新GUI ",但正确的名称世界是:" 从后台工作者更新GUI或从后台工作者报告多个变量(除了整数) "

请让我解释一下我的情况.在一个程序中,我有一个后台工作者来分析信息.作为这种分析的结果 - 形式GUI元素应该填充必要的数据.在GUI中我想更新

  • 2 datagridviews
  • 1个列表框
  • 5个标签

据我所知 - 我只能通过后台工作者的方法本地报告1个intReportProgress().

所以问题是 - 我怎样才能传递List<>(+一些其他变量:string,int)ReportProgress()?基本上 - 我想用信息更新GUI,但是"1整数"不会这样做.所以要么可以通过ReportProgress()OR 传递多个变量, 我可以使用InvokeBackgroundWorker内部的一个来更新GUI.我个人不喜欢这个Invoke方法...你有什么看法?

这是我的代码(见评论):

   private void button9_Click(object sender, EventArgs e) // start BW
    {
        bw.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
        bw.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);

        bw.WorkerReportsProgress = true;
        bw.WorkerSupportsCancellation = true;

        bw.RunWorkerAsync(10);
    }

    private void button10_Click(object sender, EventArgs e) // cancel BW
    {
        bw.CancelAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        int count = (int)e.Argument;
        for (int i = 1; i <= count; i++)
        {
            if (bw.CancellationPending)
            {
                e.Cancel = true;
                break;
            }

            List<List<string>> list_result = new List<List<string>>();
            list_result = Proccess();

            bw.ReportProgress(list_result.Count()); // right now I can only return a single INT

            /////////// UPDATE GUI //////////////
            // change datagridview 1 based on "list_result" values
            // change datagridview 2
            // change listbox
            // change label 1
            // change label ..          

            Thread.Sleep(20000);
        }

        MessageBox.Show("Complete!");
        e.Result = sum;
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        prog_count++;
        listBox1.Items.Add("Count: (" + prog_count.ToString() + "/20). Found: " + e.ProgressPercentage.ToString() + ".");
    }
Run Code Online (Sandbox Code Playgroud)

Gra*_*ICA 6

UserState打电话时有一个参数ReportProgress.

var list_result = new List<List<string>>();

new backgroundWorker1.ReportProgress(0, list_result);
Run Code Online (Sandbox Code Playgroud)

参数类型是一个object所以您必须将其强制转换为您需要的类型:

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    var userState = (List<List<string>>)e.UserState;
}   
Run Code Online (Sandbox Code Playgroud)

这个棘手的问题是,你如何确定你是否传回一个List或一个列表,或一个字符串,数字等.你将不得不测试ProgressChanged事件中的每种可能性.

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    var myList = e.UserState as List<List<string>>;
    if (myList != null)
    {
        // use list
        return;
    }

    int myNumber;
    if (Int32.TryParse(e.UserState.ToString(), out myNumber))
    {
        // use number
        return;
    }

    var myString = e.UserState.ToString();
    // use string
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以创建一个包含所需(或使用Tuple)所有值的类,在后台运行所有内容以填充该类,然后将其传递给RunWorkerCompleted事件,并从那里一次更新UI.

  • 你的第一个片段创建了一个名为`list_result`的变量,但是你将未定义的名为`myList`的东西传递给后台worker的`ReportProgress`方法.你的意思是通过`list_result`还是你离开了一条线? (2认同)

Tra*_*chs 5

我编写了两个非常简单的方法,使您能够调用代码(仅在需要时),并且只需要编写一次代码。我认为这使Invoke使用起来更加友好:

1)BeginInvoke

public static void SafeBeginInvoke(System.Windows.Forms.Control control, System.Action action)
{
    if (control.InvokeRequired)
        control.BeginInvoke(new System.Windows.Forms.MethodInvoker(() => { action(); }));
    else
        action();
}
Run Code Online (Sandbox Code Playgroud)

2)调用

public static void SafeInvoke(System.Windows.Forms.Control control, System.Action action)
{
    if (control.InvokeRequired)
        control.Invoke(new System.Windows.Forms.MethodInvoker(() => { action(); }));
    else
        action();
}
Run Code Online (Sandbox Code Playgroud)

可以这样称呼:

SafeInvoke(textbox, () => { textbox.Text = "text got changed"; });
Run Code Online (Sandbox Code Playgroud)

或者你可以

System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
Run Code Online (Sandbox Code Playgroud)

(只会在调试模式下更改行为),看看是否遇到问题。
实际上,您经常不这样做。我花了相当多的时间才发现案件确实很需要Invoke来避免混乱。

  • [将CheckForIllegalCrossThreadCalls设置为false是否安全?](http://stackoverflow.com/a/13345622/719186) (2认同)