2011-08-22 50 views
2

我目前正在爲自己的家庭項目工作。該程序是使用winforms用C#編寫的。C#winform backgroundworker

我目前遇到的問題如下:

我有我的MainForm稱爲lvwGames 一個ListView當我沒有調試運行程序,它運行良好。 但是,當我開始調試時,出現錯誤。這與後臺工作線程有關。

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

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的響應性,並通知用戶它有多遠,使用進度條。

如果有任何代碼丟失,或者您真的明白爲什麼會發生這種情況請解釋我爲什麼在這種情況下會導致錯誤。你不需要爲我修復它。我只是想知道它是什麼原因造成的。

感謝您花時間閱讀本文。我希望能夠儘快繼續使用調試器。 :)

+2

這是因爲您正在從後臺線程更新UI,而不是調用UI線程進行自己的更新。查看解決方案的其他問題。 – ChrisF

回答

1

這是因爲你試圖訪問從後臺線程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方法將始終需要調用調用,因此您可以繼續前進並像那樣寫。

+0

謝謝。這解決了我的問題,並告訴我爲什麼發生。 –

2

從後臺線程訪問可視控件時需要調用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(); 
    } 
} 

Manipulating Controls from Threads

...For example, you might call a method that disables a button or updates a display on a form in response to action taken by a thread. The .NET Framework provides methods that are safe to call from any thread for invoking methods that interact with controls owned by other threads. The Control.Invoke method allows for the synchronous execution of methods on controls...

+0

非常感謝您的幫助。謝謝。 :) –

1

這種情況發生的原因,就像編譯cliams,你要更新從另一個線程UI控件的內容。你不能那樣做,因爲UI控制可以在主線程中更新只有

請有看看這個SO與提供的示例代碼回答:

Invoke from another thread

0

如果您在studio中以調試模式運行,後臺工作器無法正常工作。如果您有使用窗口句柄檢索郵件的調用,則它們將失敗。例如,如果您有一個progressChanged事件處理程序,並且這會更改可能失敗的文本框中的文本。

我有這種情況:有一個後臺工作者的窗體。如果我只是先啓動工作人員而不先獲得對話框,那麼它工作正常。如果我顯示一個對話框,然後啓動後臺工作,那麼它會失敗。當我正常運行程序時,它不會失敗。它以某種方式破壞事件和前景窗口之間的鏈接。我已經改變了我的代碼來使用調用,現在所有的代碼都可以在發佈版中運行時以及調試時使用。

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

我沒有做同樣的示例到微軟。我做了代表,分配給我需要運行的功能。並調用它們。

樣本僞代碼:

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線程中運行,必須調用是安全的。