Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用backgroundworker和报告进度来实时向listBox添加项目?_C#_.net_Winforms - Fatal编程技术网

C# 如何使用backgroundworker和报告进度来实时向listBox添加项目?

C# 如何使用backgroundworker和报告进度来实时向listBox添加项目?,c#,.net,winforms,C#,.net,Winforms,今天,我正在backgroundworker dowork事件中等待,直到所有要加载到listBox1的项目。 相反,我希望实时查看加载到listBox1的每个项目 在表单构造函数中,我将启动backgroundworker: backgroundWorker2.RunWorkerAsync(); 这是我制作进度报告的方法: static List<string> videosList = new List<string>(); static List&

今天,我正在backgroundworker dowork事件中等待,直到所有要加载到listBox1的项目。 相反,我希望实时查看加载到listBox1的每个项目

在表单构造函数中,我将启动backgroundworker:

backgroundWorker2.RunWorkerAsync();
这是我制作进度报告的方法:

static List<string> videosList = new List<string>();
        static List<string> videosUrl = new List<string>();
        public async void RetrieveUploadsList()
        {
            UserCredentials();
            var youtubeService = new YouTubeService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
            });

            var channelsListRequest = youtubeService.Channels.List("contentDetails");
            channelsListRequest.Mine = true;

            var channelsListResponse = await channelsListRequest.ExecuteAsync();

            foreach (var channel in channelsListResponse.Items)
            {
                var uploadsListId = channel.ContentDetails.RelatedPlaylists.Uploads;

                Console.WriteLine("Videos in list {0}", uploadsListId);

                var nextPageToken = "";
                while (nextPageToken != null)
                {
                    var playlistItemsListRequest = youtubeService.PlaylistItems.List("snippet");
                    playlistItemsListRequest.PlaylistId = uploadsListId;
                    playlistItemsListRequest.MaxResults = 50;
                    playlistItemsListRequest.PageToken = nextPageToken;
                    var playlistItemsListResponse = await playlistItemsListRequest.ExecuteAsync();

                    foreach (var playlistItem in playlistItemsListResponse.Items)
                    {
                        videosList.Add(playlistItem.Snippet.Title + "  " + playlistItem.Snippet.ResourceId.VideoId);
                        videosUrl.Add("http://www.youtube.com/v/" + playlistItem.Snippet.ResourceId.VideoId);

                        backgroundWorker2.ReportProgress(0, playlistItem.Snippet.Title + "  " + playlistItem.Snippet.PublishedAt);
                    }
                    nextPageToken = playlistItemsListResponse.NextPageToken;
                }
            }
            Invoke(new MethodInvoker(
                   delegate
                   {
                       if (this.listBox1.Items.Count > 0)
                       {
                           this.listBox1.SelectedIndex = 0;
                           axWindowsMediaPlayer1.URL = videosUrl[0];
                       }
                   }));
        }
在嫁妆活动中,我做到了:

private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
        {
            RetrieveUploadsList();
        }
private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            listBox1.Items.Add(e.UserState);
        }
然后在进度变更事件中,我做了:

private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
        {
            RetrieveUploadsList();
        }
private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            listBox1.Items.Add(e.UserState);
        }
但我得到了一个例外:


此操作已对其调用OperationCompleted,进一步调用是非法的。

在BackgroundWorker的DoWork处理程序中使用async/await是一个坏主意,因为您已经了解了这一点。您只能在WAIT语句之前调用backgroundWorker2.ReportProgress,因为在您点击WAIT的那一刻,方法的其余部分将被安排为一个延续,并且工作进程将转换为完成状态,之后您将无法报告进度。紧接着,线程将在方法的其余部分完成之前继续进行其他工作,这可能不是您所期望的

抛开后台工作程序,使用其他异步友好的进度报告机制。我建议使用progress/IProgress或简单地将其添加到列表框中,作为RetrieveUploadsList方法的一部分,只要在UI线程上调用它,就可以这样做。

已经对其进行了一般性解释。让我说清楚。async/await实际上是BackgroundWorker的替代品,因此不应与BackgroundWorker结合使用

下面是使用async/await执行所述操作的实际示例:

首先要注意的是,使用async/await,不需要考虑BackgroundWorker的RunWorkerCompleted事件的等价物。使用模式如下

try
{
    await SomeMethodAsync();
    // Handle here the successful completion
} 
catch (TaskCanceledException)
{
    // Handle the cancellation here
}
catch (Exception ex)
{
    // Handle the exception here
}
private async Task RetrieveUploadsListAsync(IProgress<string> progress)
{
    UserCredentials();
    var youtubeService = new YouTubeService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = credential,
        ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
    });

    var channelsListRequest = youtubeService.Channels.List("contentDetails");
    channelsListRequest.Mine = true;

    var channelsListResponse = await channelsListRequest.ExecuteAsync();

    foreach (var channel in channelsListResponse.Items)
    {
        var uploadsListId = channel.ContentDetails.RelatedPlaylists.Uploads;

        Console.WriteLine("Videos in list {0}", uploadsListId);

        var nextPageToken = "";
        while (nextPageToken != null)
        {
            var playlistItemsListRequest = youtubeService.PlaylistItems.List("snippet");
            playlistItemsListRequest.PlaylistId = uploadsListId;
            playlistItemsListRequest.MaxResults = 50;
            playlistItemsListRequest.PageToken = nextPageToken;
            var playlistItemsListResponse = await playlistItemsListRequest.ExecuteAsync();

            foreach (var playlistItem in playlistItemsListResponse.Items)
            {
                videosList.Add(playlistItem.Snippet.Title + "  " + playlistItem.Snippet.ResourceId.VideoId);
                videosUrl.Add("http://www.youtube.com/v/" + playlistItem.Snippet.ResourceId.VideoId);

                progress.Report(playlistItem.Snippet.Title + "  " + playlistItem.Snippet.PublishedAt);
            }
            nextPageToken = playlistItemsListResponse.NextPageToken;
        }
    }
}
对于回调,您可以使用IProgress。它不像BackgroundWorker的ProgressChanged事件那样受到限制。正如您所看到的,您可以指定数据的类型,而且不限于一个调用。有一个标准的实现类Progress,它使用SynchronizationContext,有效地将调用编组到UI线程

让我们将所有这些应用到您的方法中。目前,您正在为回调提供一个字符串,因此等效的方法是使用IProgress。因此,做这些工作的纯方法应该是这样的

try
{
    await SomeMethodAsync();
    // Handle here the successful completion
} 
catch (TaskCanceledException)
{
    // Handle the cancellation here
}
catch (Exception ex)
{
    // Handle the exception here
}
private async Task RetrieveUploadsListAsync(IProgress<string> progress)
{
    UserCredentials();
    var youtubeService = new YouTubeService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = credential,
        ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
    });

    var channelsListRequest = youtubeService.Channels.List("contentDetails");
    channelsListRequest.Mine = true;

    var channelsListResponse = await channelsListRequest.ExecuteAsync();

    foreach (var channel in channelsListResponse.Items)
    {
        var uploadsListId = channel.ContentDetails.RelatedPlaylists.Uploads;

        Console.WriteLine("Videos in list {0}", uploadsListId);

        var nextPageToken = "";
        while (nextPageToken != null)
        {
            var playlistItemsListRequest = youtubeService.PlaylistItems.List("snippet");
            playlistItemsListRequest.PlaylistId = uploadsListId;
            playlistItemsListRequest.MaxResults = 50;
            playlistItemsListRequest.PageToken = nextPageToken;
            var playlistItemsListResponse = await playlistItemsListRequest.ExecuteAsync();

            foreach (var playlistItem in playlistItemsListResponse.Items)
            {
                videosList.Add(playlistItem.Snippet.Title + "  " + playlistItem.Snippet.ResourceId.VideoId);
                videosUrl.Add("http://www.youtube.com/v/" + playlistItem.Snippet.ResourceId.VideoId);

                progress.Report(playlistItem.Snippet.Title + "  " + playlistItem.Snippet.PublishedAt);
            }
            nextPageToken = playlistItemsListResponse.NextPageToken;
        }
    }
}
最后,我们可以像这样创建UI感知方法

private async void LoadUploadsListAsync()
{
    try
    {
        var progress = new Progress<string>(text => this.listBox1.Items.Add(text));
        await RetrieveUploadsListAsync(progress);
        if (this.listBox1.Items.Count > 0)
        {
            this.listBox1.SelectedIndex = 0;
            axWindowsMediaPlayer1.URL = videosUrl[0];
        }
    }
    catch (TaskCanceledException)
    {    
    }
    catch (Exception ex)
    {
    }
}

然后删除BackgroundWorker,从表单构造函数调用LoadUploadsListAsync,或者从表单加载事件调用LoadUploadsListAsync

Kirill在此之前,我在RetrieveUploadsList中使用invoke来添加到listBox1,而不是执行reportprogress。但是,我必须等到所有的项目都被加载后,才能在列表框中看到它们,我想在实时添加每个项目而不是等待中看到它们。我从未试过使用Progress@SharondohpSharonas,如果您在UI线程上同步执行工作,则会出现这种行为。我没有在你发布的代码中看到这一点,因为每次你在while nextPageToken中点击等待语句空循环,您的UI线程将获得一个喘息器,并将能够绘制上一次迭代添加到列表中的所有项目。测试中是否可能使用playlitemsListrequest.ExecuteAsync.Result?原因将导致所述问题。另外,如果您在UI线程上调用RetrieveUploadsList,则不需要调用。Ivan工作正常。如果我想在LoadUploadsListAsync中添加一个Thread.Sleep100;我想让它慢一点的项目添加进度。我不知道如何以及在哪里添加线程@SharondohpSharonas在异步世界中,您可以使用wait Task.Delay100;相反