创建对象时Webclient下载未开始

时间:2018-11-15 22:08:59

标签: vb.net winforms webclient

我在BackgroundWorker中有一个WebClient,但是由于某种原因,在启动对象之前创建对象时,它没有开始下载。 在主线程上工作正常。


像这样不起作用:

Dim AddRPB As New ProgressBar
Dim client As New WebClient
AddHandler client.DownloadProgressChanged, AddressOf DownloadingProgress
AddHandler client.DownloadDataCompleted, AddressOf DownloadComplete
client.DownloadDataAsync(New Uri(WebLink), Data)

像这样工作:

Dim client As New WebClient
AddHandler client.DownloadProgressChanged, AddressOf DownloadingProgress
AddHandler client.DownloadDataCompleted, AddressOf DownloadComplete
client.DownloadDataAsync(New Uri(WebLink), Data)
Dim AddRPB As New ProgressBar

Dim AddRPB As New ProgressBar

单行以某种方式打破了它,我不明白为什么。

1 个答案:

答案 0 :(得分:1)

这可能并不完全准确,但这是我通过一些测试并在Reference Source的帮助下得出的:

ProgressBar实例化之前/之后

WebClientSynchronizationContexts一起使用,以便将数据发布回UI线程并调用其事件处理程序(BackgroundWorker也是如此)。当您调用其Async方法之一时,WebClient立即创建一个绑定到调用线程的SynchronizationContext的异步操作。如果上下文不存在,则会创建一个新上下文并将其绑定到该线程。

如果在RunWorkerAsync事件处理程序中完成此操作而没有(或之前没有)创建ProgressBar,则将为BackgroundWorker的线程创建一个新的同步上下文。

到目前为止,一切都很好。一切仍然有效,但是事件处理程序将在后台线程而不是UI线程中执行。

在开始下载之前创建ProgressBar

在开始下载之前使用ProgressBar实例化代码,您现在正在非UI线程中创建控件,这将导致创建新SynchronizationContext并将其绑定到该控件。后台线程以及控件本身。这个SynchronizationContext有点不同,它是一个WindowsFormsSynchronizationContext,它使用Control.Invoke()Control.BeginInvoke()方法与他们认为是UI线程的方法进行通信。在内部,这些方法将一条消息发布到UI的消息泵,告诉它在UI线程上执行指定的方法。

这似乎是出问题的地方。通过在非UI线程中创建控件,从而在该线程中创建WindowsFormsSynchronizationContextWebClient现在将在调用事件处理程序时使用该上下文。 WebClient将调用WindowsFormsSynchronizationContext.Post(),后者依次调用Control.BeginInvoke()以在同步上下文的线程上执行该调用。唯一的问题是:该线程没有消息循环,无法处理BeginInvoke消息。

  • 无消息循环= BeginInvoke消息将不被处理

  • 消息将无法处理=没有任何东西调用指定的方法

  • 该方法未调用=将永远不会引发WebClient的{​​{1}}或DownloadProgressChanged事件。

最后,所有这些再次归结为WinForms的黄金法则:

将所有与UI相关的工作留在UI线程上!


  

编辑:

     

如评论/聊天中所述,如果您所做的只是将进度条传递给DownloadDataCompleted的异步方法,则可以像这样解决它,并让WebClient在UI线程,然后为您返回:

Control.Invoke()