繁体   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