1

I am making a WPF app that needs to download multiple files from online. Currently, when I call the event which leads to the file download, my app hangs for a while as it downloads the file and only returns to functioning when the download is complete. Is there a way to make it such that the file downloads in the background and the application doesn't hang? I'm relatively new at this so the most information possible would be excellent. Thank you!

Edit: I should have been more clear. I have a function called downloadFile(), and when the main event is triggered, I need to download multiple files. Here is my code:

private void DownloadAll()
{
    foreach(string moddiekey in moddiekeys)
    {
        download_file_by_moddiekey(moddiekey );
    }               
}
private async void Event(){
    await Task.Run(() => { DownloadAll(); });
}

But this still hangs my application.

Edit 2: Showing download functions

private void download_file_by_url(string url)
{
    try
    {
        string[] splits = url.Split('/');
        string filename = splits[splits.Length - 1];
        WebClient wc = new WebClient();
        wc.DownloadFile(url, filename);
        wc.Dispose();
    }
    catch { }
}

private void download_file_by_moddiekey(string moddiekey)
{
    ...
    download_file_by_url("github link/" + moddict[moddiekey]);
    ...
}

1 Answers1

-2

I have a couple suggestions that may help. First looking at your code my assumption is that Event() is tied to a UI event. Calling await on a UI thread will always freeze the UI. One approach to preventing this is to use ContinueWith(). Implementing this in your case would look like:


private async void Event(){
    DownloadAll().ContinueWith(OnDownloadComplete);
}

private void OnDownloadComplete(Task t)
{
   if (t.IsCanceled || t.IsFaulted)
   {
      // indicate to user that download has failed
   }

   // optionally perform a task after download has been completed
}

Because the await is removed from the call to DownloadAll() the UI can continue doing it's thing well the tasks complete. After the task is completed the OnDownloadComplete() method will be called.

If you want to update the UI (e.g. display download status) from one of these methods (which would be running on a non-UI thread) you will need to send those updates back to the UI thread:


private void UpdateStatus(string message)
{
    Dispatcher.Invoke(() => {
                 MyLabel.Text = message;
            });
}

then in your download_file() method you could do something like:


private async void download_file_by_url(string url)
{
   // code to download file omitted
 
   UpdateStatus($"Downloaded file from {url}!"); 
}

if that doesn't work I would trying adding a ConfigureAwait call to your task. E.g:


private async void SomeMethodThatUsesAwait()
{
   await DoSomething().ConfigureAwait(false);
}
Darin
  • 47
  • 1
  • 6
  • 2
    I think you have a few wires crossed here. – TheGeneral Jul 24 '20 at 02:31
  • 1
    you can provide the context in the continueWith directly, no need for dispatcher – Master Azazel Jul 24 '20 at 02:34
  • @TheGeneral can you expand? Am I misunderstanding the question or did I state something incorrectly? – Darin Jul 24 '20 at 03:14
  • @Darin events with async void will actually run unobserved on the current sync context, secondly `ContinueWith` is taken care of with the `IAsyncStatemachine` implementation when using `await` and would be largely redundent here if use appropriately , the `Dispatcher.Invoke` would be a good guess but there is no evidence the OP has a UI marshalling problem. The problem is the question leaves too much to the imagination and just cant be reliably answered – TheGeneral Jul 24 '20 at 04:12