2011-08-22 69 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线程中运行,必须调用是安全的。