簡體   English   中英

C#-使用BackgroundWorker中的控件填充面板

[英]C# - Populating Panel with controls from BackgroundWorker

因此,我正在編寫一個小型Twitter客戶端供我使用。 我使用的是一個大面板的組合,而較小的面板代表各個推文。 在每個較小的面板中,我都有一個PictureBox和一個RichTextBox。

現在,我的問題是,加載10條以上的tweet會導致速度變慢,因為我正在動態生成面板。 因此,我決定使用BackgroundWorker進行此操作,然后將這些面板添加到主面板中。

我已經通過將文本從其他主題寫入文本框來完成了很多次(甚至為此編寫了教程)。 但是我無法使它起作用。 我收到錯誤消息:

Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.

碼:

List<Panel> panelList = new List<Panel>();

foreach (UserStatus friendStatus in list)
{
    PictureBox pbTweet = new PictureBox();
    // ...
    // code to set numerous properties
    // ...

    RichTextBox rtbTweet = new RichTextBox();
    // ...
    // code to set numerous properties
    // ...

   Panel panelTweet = new Panel();
    // ...
    // code to set numerous properties
    // ...

   panelTweet.Controls.Add(pbTweet);
   panelTweet.Controls.Add(rtbTweet);

   panelList.Add(panelTweet);
}

if (panelMain.InvokeRequired)
    panelMain.BeginInvoke((MethodInvoker)delegate { foreach (Panel p in panelList) { panelMain.Controls.Add(p); } });

有人注意到任何問題嗎?

panelTweet是在BackgroundWorker的線程上創建的,並且可以從您的委托的主線程中訪問( panelMain.Controls.Add(p);// p = panelTweet )。

您必須在主線程中調用所有該函數,而不僅僅是最后一部分。


您可以這樣重寫函數:

private void AddControls()
{
    if(panelMain.InvokeRequired)
    {
        panelMain.BeginInvoke(new MethodInvoker(AddControls));
        return;
    }

    foreach (UserStatus friendStatus in list)
    {
        PictureBox pbTweet = new PictureBox();
        // ...
        // code to set numerous properties
        // ...

        RichTextBox rtbTweet = new RichTextBox();
        // ...
        // code to set numerous properties
        // ...

        Panel panelTweet = new Panel();
        // ...
        // code to set numerous properties
        // ...

        panelTweet.Controls.Add(pbTweet);
        panelTweet.Controls.Add(rtbTweet);

        panelMain.Controls.Add(panelTweet)
    }
}

使用后台線程時,必須將修改數據控件的部件與要檢索數據的部件完全分開。 即使需要花費一些時間,所有修改表單控件的代碼也必須在UI線程上調用。 沒有辦法解決這個問題。

通常,這是一個很好的策略,因為通常,將數據放入內存是較慢的部分,而更新UI是相對較快的部分。

在您的代碼示例中,所有代碼都是UI修改部分,因此必須全部放入UI線程中。

編輯:要優化UI部件,您可以嘗試在要修改的面板上調用SuspendLayoutResumeLayout

您可以嘗試在ProgressChanged處理程序中創建控件。 這樣,您將能夠在后台線程中進行一些初始化(用戶圖片檢索等),並在GUI線程中進行可視化。

但是請注意,最可能的性能問題是由於創建RichTextEdit和PictureBox所需的大量資源。 考慮創建自定義控件,該控件僅包含在Paint事件上呈現的用戶圖像和文本,例如

您不能在后台線程中創建任何WinForms UI控件。

有幾種解決方法-我先開始:

Control getPanelForUser( UserStatus friendStatus ) {
    PictureBox pbTweet = new PictureBox { /* set props */ };
    RichTextBox rtbTweet = new RichTextBox { /* set props */ };

    Panel panelTweet = new Panel { /* set props */ };

    panelTweet.Controls.Add(pbTweet);
    panelTweet.Controls.Add(rtbTweet);

    return panelTweet;
}

然后在您的后台工作者中:

foreach (UserStatus friendStatus in list)
    panelMain.BeginInvoke(
        delegate ( object o ) { 
            panelMain.Controls.Add(getPanelForUser( o as UserStatus ));
        }, 
        friendStatus );

不過,這可能仍然很慢-值得加載一個子集,然后滴入更多的子集。您也可以僅加載可見列表-隱藏其他列表,直到它們滾動為止。 然后,您一次只加載一個頁面。

您嘗試將在外部線程X上創建的Panel p添加回winform線程Y。

將整個創建內容放入BeginInvoke處理程序中。 這樣,所有控件都將在Winform線程Y中創建。

好的,所以看看答案,看來我只是SOL。 我需要在UI線程中進行所有處理,從而使它變得無響應。

暫無
暫無

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

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