簡體   English   中英

C#Winform后台工作人員

[英]C# winform backgroundworker

我目前正在為自己做一個家庭項目。 該程序使用Winforms以C#編寫。

我當前遇到的問題如下:

我在名為lvwGames的主窗體中有一個列表視圖,當我運行該程序而不進行調試時,它運行良好。 但是,當我開始調試時,出現錯誤。 這與后台工作線程有關。

請允許我發布一些代碼來幫助我。

    private void MainViewLoad(object sender, EventArgs e)
    {
        RefreshGamesListView();
    }

這里沒什么特別的。 我之所以調用RefreshGamesListView()是因為我不得不多次刷新。

被調用的方法如下所示。

    public void RefreshGamesListView()
    {
        pbRefreshGamesList.Value = 0;
        bgwRefreshList.RunWorkerAsync();
    }

因此,在調用該方法時,將調用后台工作程序並運行dowork方法。 這個很大。

    private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
    {
        List<Game> games = _mainController.RetrieveAllGames();

        int count = 1;
        foreach (Game game in games)
        {
            string id = game.id.ToString();
            var li = new ListViewItem(id, 0);
            li.SubItems.Add(game.title);
            li.SubItems.Add(game.Genre.name);
            li.SubItems.Add(game.Publisher.name);
            li.SubItems.Add(game.Platform.name);
            li.SubItems.Add(game.CompletionType.name);
            li.SubItems.Add(game.gameNotice);
            lvwGames.Items.Add(li);

            double dIndex = (double)(count);
            double dTotal = (double)games.Count;
            double dProgressPercentage = (dIndex / dTotal);
            int iProgressPercentage = (int)(dProgressPercentage * 100);

            count++;
            bgwRefreshList.ReportProgress(iProgressPercentage);
        }
    }

當我在調試中運行代碼時,代碼在lvwGames.Items.Add(li);上。 它給了我以下錯誤:

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

我完全不知道為什么。 我認為這是特定於代碼的。 但這也可能意味着我沒有完全了解后台工作人員,尤其是何時正確使用它。

我之所以使用它,是因為我正在從數據庫中加載一個大列表,我想在加載列表時保持UI的響應性,並使用進度條通知用戶它有多遠。

如果缺少任何代碼,或者您實際上了解了發生這種情況的原因,請向我解釋為什么在這種情況下會導致錯誤。 您不需要為我修復它。 我只想知道原因。

感謝您抽出寶貴的時間閱讀這篇文章。 我希望能夠盡快繼續使用調試器。 :)

從后台線程訪問可視控件時,需要調用Conrol.Invoke

if (_lvwGames.IsHandleCreated) {

    Action addGameToList = () => {
        string id = game.id.ToString();
        var li = new ListViewItem(id, 0);
        li.SubItems.Add(game.title);
        ....
        _lvwGames.Items.Add(li);
    };

    if (_lvwGames.InvokeRequired) {                        
        _lvwGames.Invoke(addGameToList);
    } else {
        addGameToList();
    }
}

線程控制

...例如,您可能調用一種方法,以響應線程執行的操作來禁用按鈕或更新表單上的顯示。 .NET Framework提供了可以從任何線程安全調用的方法,以調用與其他線程擁有的控件進行交互的方法。 Control.Invoke方法允許在控件上同步執行方法...

發生這種情況的原因,就像編譯器的用戶體驗一樣,您將從另一個線程更新UI控件內容。 您不能這樣做,因為UI控件只能在主線程中更新。

請通過提供的示例代碼查看此SO答案:

從另一個線程調用

這是因為您試圖從后台線程訪問UI控件( lvwGames )。 使其工作的方法要求您將信息封送回主UI線程,然后從那里更新控件:

private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
    List<Game> games = _mainController.RetrieveAllGames();

    int count = 1;
    foreach (Game game in games)
    {
        string id = game.id.ToString();
        var li = new ListViewItem(id, 0);
        li.SubItems.Add(game.title);
        li.SubItems.Add(game.Genre.name);
        li.SubItems.Add(game.Publisher.name);
        li.SubItems.Add(game.Platform.name);
        li.SubItems.Add(game.CompletionType.name);
        li.SubItems.Add(game.gameNotice);

        // This is the new line you need:
        lvwGames.Invoke(new MethodInvoker(delegate { lvwGames.Items.Add(item) }));

        double dIndex = (double)(count);
        double dTotal = (double)games.Count;
        double dProgressPercentage = (dIndex / dTotal);
        int iProgressPercentage = (int)(dProgressPercentage * 100);

        count++;
        bgwRefreshList.ReportProgress(iProgressPercentage);
    }
}

通常,您將首先檢查InvokeRequired屬性,如其他答案中所述,但是如果始終從后台線程調用它,則實際上並不需要。 您的DoWork方法將始終需要調用調用,因此您最好繼續進行編寫。

如果您在Studio中以調試模式運行,則后台工作程序無法正常工作。 如果您有使用Windows句柄檢索消息的呼叫,則它們將失敗。 例如,如果您有一個progressChanged事件處理程序,這將更改文本框中的文本,這可能會失敗。

我有這種情況:具有后台工作人員的窗體。 如果我只是在不首先打開對話框的情況下啟動工作程序,那么它就可以正常工作。 如果顯示對話框,然后啟動后台工作程序,則它將失敗。 當我正常運行程序時,它不會失敗。 調試環境以某種方式破壞了事件和前台窗口之間的鏈接。 我已經更改了代碼以使用invoke,現在所有版本都可以在發行版中運行和調試時使用。

這是一個鏈接,說明如何使程序線程安全。 http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx

我沒有對Microsoft進行相同的示例。 我做了代表,分配了我需要運行的功能。 並調用了它們。

樣本偽代碼:

class MyClassWithDelegates
{
    public delegate void ProgressDelegate( int progress );
    public ProgressDelegate myProgress;
    public void MyProgress(int progress)
    {
         myTextbox.Text = ..... ; // this is code that must be run in the GUI thread.
    }

    public MyClassWithDelegates()
    {
      myProgress = new ProgressDelegate(MyProgress);    
    }
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Invoke( myProgress, e.ProgressPercentage );
    }

}

為了安全起見,必須調用可能必須在應用程序的GUI線程中運行的所有代碼。

暫無
暫無

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

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