Reputation: 1907
I've managed to implement a Task
in my class UpdateManager that downloads a file from my webspace.
public async Task DownloadPackageTask(IProgress<int> progress)
{
var webRequest = WebRequest.Create(new Uri("http://www.mywebspace.de/archive.zip"));
using (var webResponse = await webRequest.GetResponseAsync())
{
var buffer = new byte[1024];
FileStream fileStream = File.Create("Path"));
using (Stream input = webResponse.GetResponseStream())
{
int received = 0;
double total = 0d;
var size = GetFileSize(new Uri("...")); // Gets the content length with a request as the Stream.Length-property throws an NotSupportedException
if (size != null)
total = (long) size.Value;
int size = await input.ReadAsync(buffer, 0, buffer.Length);
while (size > 0)
{
fileStream.Write(buffer, 0, size);
received += size;
progress.Report((received/total)*100));
size = await input.ReadAsync(buffer, 0, buffer.Length);
}
}
}
}
This works well, the file is being downloaded and also if I add Debug.Print((received/total)*100)
it outputs the correct percentage, everything is alright. The method is marked as async so that it can be awaited/wrapped asynchronously in a task.
The problem occurs in another class UpdaterUi that is basically the interface between the manager and the user interface and calls the method like that:
public void ShowUserInterface()
{
TaskEx.Run(async delegate
{
var downloadDialog = new UpdateDownloadDialog
{
LanguageName = _updateManager.LanguageCulture.Name,
PackagesCount = _updateManager.PackageConfigurations.Count()
};
_context.Post(downloadDialog.ShowModalDialog, null); // Do this with a SynchronizationContext as we are not on the UI thread.
var progressIndicator = new Progress<int>();
progressIndicator.ProgressChanged += (sender, value) =>
downloadDialog.Progress = value;
await TaskEx.Run(() => _updateManager.DownloadPackageTask(progressIndicator));
});
}
It never calls the anonymous method there that should be invoked as soon as the progress changes, but nothing happens, I debugged it with breakpoints.
The problem is maybe that the progressIndicator
is not created on the UI-thread, but on the new thread created by TaskEx.Run
. It doesn't fire the event and consecutively the UI does not update the progressbar it contains (which it does in the setter of the Progress
-property that is initialized above).
The problem is that I don't know what to do in order to make it working, how am I supposed to change the implementation in my project to get it working, is my thought of that threading problem correct?
Thanks in advance!
Upvotes: 2
Views: 1351
Reputation: 73502
Your speculation about the problem is right. Progress<T>
should be created in UI thread in order to be notified in UI thread.
It works by capturing the SynchronizationContext
and posting the delegate for execution in the captured context. Since it is created in non UI context(default context or threadpool context), it will raise ProgressChanged
in threadpool thread.
If you move this line var progressIndicator = new Progress<int>();
out of the TaskEx.Run
, it should work (if ShowUserInterface
is called from UI thread).
I see you're creating UpdateDownloadDialog
in a worker thread. You shouldn't do that. Move that also to UI thread.
Upvotes: 5