簡體   English   中英

從后台工作者更新GUI

[英]Updating the GUI from background worker

問題的名稱是:“ 從后台工作者更新GUI ”,但正確的名稱世界是:“ 從后台工作者更新GUI或從后台工作者報告多個變量(除了整數)

請讓我解釋一下我的情況。 在一個程序中,我有一個后台工作者來分析信息。作為這種分析的結果 - 形式GUI元素應該填充必要的數據。 在GUI中我想更新

  • 2 datagridviews
  • 1個列表框
  • 5個標簽

據我所知 - 我只能通過后台工作者的ReportProgress()方法本地報告1個int

所以問題是 - 如何通過ReportProgress()傳遞List<> (+其他一些變量: stringint ReportProgress() 基本上 - 我想用信息更新GUI,但“1整數”不會這樣做。所以要么可以通過ReportProgress()傳遞多個變量,或者我可以使用BackgroundWorker內部的Invoke來更新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() + ".");
    }

調用ReportProgress時有一個UserState參數。

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

new backgroundWorker1.ReportProgress(0, list_result);

參數類型是一個object因此您必須將其強制轉換為您需要的類型:

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

這個棘手的問題是,如何確定您是否傳回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
}

或者,您可以創建一個包含所需所有值的類(或使用Tuple ),在后台運行所有內容以填充該類,然后將其傳遞給RunWorkerCompleted事件,並從那里一次更新UI。

我編寫了兩個非常簡單的方法,使您能夠調用代碼(僅在需要時),並且只需編寫一次代碼。 我認為這使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();
}

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();
}

它可以像這樣調用:

SafeInvoke(textbox, () => { textbox.Text = "text got changed"; });

或者你也可以

System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;

(它只會改變調試模式btw中的行為)並查看是否遇到問題。
你經常不這樣做。 我花了很長時間才找到案例非常需要Invoke才能讓事情搞得一團糟。

從另一個線程更新UI的基本模式是:

If controlItem.InvokeRequired Then
    controlItem.Invoke(Sub() controlItem.Text = textUpdateValue)
Else
    controlItem.Text = textUpdateValue
End If

這可以更新您的控件列表,而無需通過ReportProgress傳遞任何內容。 如果您想從線程內更新控件,我認為您不需要檢查InvokeRequired,因為它始終是必需的。 但是,最佳實踐可能是通過屬性公開控件的設置,然后進行全面檢查,以便您可以從任何地方調用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM