-1

I'm having an issue getting a progressbar to show download progress. The files are downloading without issue, but something is causing my progressbar to not update and I can't figure out why.

I've tried setting the progressBar value manually in the download and wc_DownloadProgressChanged method, but the only place it actually changes is the Form1_Load method.

using System;
using System.Windows.Forms;
using System.Threading;

namespace Launch
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            Downloader downloader = new Downloader();

            ThreadStart job = new ThreadStart(downloader.download);
            Thread thread = new Thread(job);
            thread.Start();

        }

        private void ProgressBar_Click(object sender, EventArgs e)
        {

        }

        public void SetProgress(int val)
        {
            progressBar.Value = val;
        }

        public void SetVisible(bool val)
        {
            progressBar.Visible = val;
        }
    }
}
using System;
using System.Data;
using System.Net;
using Newtonsoft.Json;

namespace Launch
{
    class Downloader
    {

        public void download()
        { 
            WebClient client = new WebClient();

            string url = "https://someurl.com/manifest.json";
            string json = client.DownloadString(url);

            DataSet dataSet = JsonConvert.DeserializeObject<DataSet>(json);

            DataTable dataTable = dataSet.Tables["Required"];

            foreach (DataRow row in dataTable.Rows)
            {

                string remoteUri = row["url"].ToString();
                string fileName = row["name"].ToString();

                client.DownloadProgressChanged += client_DownloadProgressChanged;
                client.DownloadFile(remoteUri, fileName);

                Console.WriteLine("Did something with " + remoteUri);

            }
        }

        private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {

            var form = new Form1();
            form.SetProgress(e.ProgressPercentage);

        }

    }
}

Would anyone be able to shed some light on what I'm doing wrong here?

EDIT:

I was able to get this working for the most part using DownloadFileAsync, but the progress bar was bouncing back and forth, I'm assuming because it's trying to calculate the progress for each individual file as bytes are received, so I'd like to get this working with DownloadFile.

The issue I'm now having with DownloadFile is that I have it running as a task but it's skipping all of the files (not downloading any of them, just prints them all out to console super fast).

Here's the code I'm using currently:

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            WebClient client = new WebClient();
            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);

            string url = "https://someurl.com/manifest.json";
            string json = client.DownloadString(url);

            DataSet dataSet = JsonConvert.DeserializeObject<DataSet>(json);

            DataTable dataTable = dataSet.Tables["Required"];

            foreach (DataRow row in dataTable.Rows)
            {

                string remoteUri = row["url"].ToString();
                string fileName = row["name"].ToString();

                Task.Run(() => {
                    client.DownloadFile(remoteUri, fileName);
                });

                Console.WriteLine("Did something with " + remoteUri);

            }

        }

        private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {

            this.BeginInvoke((MethodInvoker)delegate {
                double bytesIn = double.Parse(e.BytesReceived.ToString());
                double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
                double percentage = bytesIn / totalBytes * 100;
                label1.Text = "Downloaded ";
                label2.Text = e.BytesReceived.ToString();
                label3.Text = e.TotalBytesToReceive.ToString();
                progressBar.Value = int.Parse(Math.Truncate(percentage).ToString());
            });

        }    

Any ideas?

Zac1989
  • 53
  • 10
  • 1
    `foreach (DataRow row in dataTable.Rows) (...) client.DownloadProgressChanged += client_DownloadProgressChanged;`, all to `var form = new Form1(); form.SetProgress(e.ProgressPercentage);`? I suggest using a static HttpClient and the [Progress](https://learn.microsoft.com/en-us/dotnet/api/system.progress-1) class. It'll also prevent your code (when working) from fighting against itself for usable connections. – Jimi Jul 20 '19 at 06:48
  • As of now, you're creating a new Form (from a non-UI thread) each time a WebClient instance notified a progress. You could end up with, possibly, thousands. If the code could work in these conditions. – Jimi Jul 20 '19 at 07:11
  • Possibly, worse than before. One single WebClient that should throttle multiple synchronous downloads in a threadpool thread, started from the `Form.Load` event (bad choice, the Load event eats up the exceptions). Btw, why do you keep on calling `DownloadFile`, when this is the synchronous version which also doesn't raise the `DownloadProgressChanged` event. Test the [DownloadFileTaskAsync](https://learn.microsoft.com/en-us/dotnet/api/system.net.webclient.downloadfiletaskasync) version. Using the `Progress` class mentioned before. Code samples are available on SO about these. – Jimi Jul 20 '19 at 22:54
  • Plus, all the overlapping events (if these events were generated), should notify the progress using a single ProgressBar. If you find the Task based method difficult to implement, go for the event-driven one [DownloadFileAsync](https://learn.microsoft.com/en-us/dotnet/api/system.net.webclient.downloadfileasync). This will raise the `DonwloadProgress` and `DownloadCompleted` events (you also need to subscribe to the latter). You have to handle multiple Progress events and update multiple ProgressBars. You also need to dispose of each WebClient instance you create. – Jimi Jul 20 '19 at 23:25

2 Answers2

0

Use this code below :

    private void Form1_Load(object sender, EventArgs e)
    {
        WebClient client = new WebClient();

        client.DownloadProgressChanged += client_DownloadProgressChanged;
        client.DownloadStringCompleted += client_DownloadStringCompleted;

        Uri url = new Uri("url");
        client.DownloadStringAsync(url);


    }

    void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        SetVisible(false);
    }

    void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        SetProgress(e.ProgressPercentage);
    }
    public void SetProgress(int val)
    {
        progressBar.Value = val;
    }

    public void SetVisible(bool val)
    {
        progressBar.Visible = val;
    }
Saeiddjawadi
  • 312
  • 2
  • 13
  • 1
    The OP is trying to download multiple files from, possibly, multiple different remote resources at the same time, threading the process. This just sets up a single download. Btw, you should wire up the handlers before calling `DownloadStringAsync`. – Jimi Jul 20 '19 at 07:19
  • It can be used in method and call it separately. However one progress bar can not handle multiple download.Just can get multiple download progresses value and take average, then show in a progress bar. – Saeiddjawadi Jul 20 '19 at 07:38
  • That's why the OP is trying to create a new Form with a ProgressBar to report the progress of each download. Btw2, the OP is using `DownloadFile` and not `DownloadString` (or `DownloadStringAsync`). The OP should also verify whether `DownloadFile` actually raises the DownloadProgress/DownloadComplete events. – Jimi Jul 20 '19 at 07:49
  • This example unfortunately defeats the purpose of what I'm trying to achieve, which is setting download progress in the progressbar *from another class*. – Zac1989 Jul 20 '19 at 14:19
0

This likely should belong to https://codereview.stackexchange.com/

First of all, do not add a handler in a loop

client.DownloadProgressChanged += client_DownloadProgressChanged;

First time it will be fine, on 2nd item it will be called 2x times, on 3rd time it will be called 3 times, etc. You want to set handler once. Move it just after line:

WebClient client = new WebClient();

Second of all, each time a progress update is fired you are creating a new instance of the form. Create a private variable or property once.

private Form1 form = new Form1();

edit:

In case of UI, usually you can modify it only in the dispatcher thread that created it or use marshaling. I would just remove it as our already downloading the string async.

Also, I would not use e.ProgressPercentage, but something like:

Math.Truncate(e.BytesReceived / (double)e.TotalBytesToReceive * 100)
Community
  • 1
  • 1
Margus
  • 19,694
  • 14
  • 55
  • 103
  • Hi Margus, I've changed the code as you have suggested but it appears the progress bar still does not change. Any other ideas as to what may be wrong? Thank you. – Zac1989 Jul 20 '19 at 14:21
  • Hi, sorry, I'm not sure I understand, I'm not downloading it async, I think you're referring to someone else's answer. Not being in the same thread makes sense, would you be able to provide me an example of marshaling? – Zac1989 Jul 20 '19 at 15:05
  • @Zac1989 yes, mixed with Saeiddjawadi. An example https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread – Margus Jul 20 '19 at 15:13
  • Thanks for that, that give me some ideas. Please see my edit in my original post, I feel like I'm very close to a working solution, if you have any further insight. – Zac1989 Jul 20 '19 at 17:16