f4bzen
f4bzen

Reputation: 301

How to update progressbars individually (simultaneous downloads)

I´m currently trying to add parallel downloads to my application but I don´t know how to handle the DownloadProgressChangedEvent to display the progress in multiple progressbars.

I´m using a datagridview with predefined rows for each file the user is able to download and each row has a cell with a progressbar in it.

The problem now is, that I don´t know how to update each progressbar individually, because right now, all selected progressbars are showing the same percentage and they´re just jumping between the progress of download1 & download2.

Here´s the code im using:

To start the downloads:

private void download_button_Click(object sender, EventArgs e)
    {
        start = DateTime.Now;
        download_button.Enabled = false;

        Rows = dataGridView1.Rows.Count;
        Checked = 0;

        CheckedCount = 0;

            //count the selected rows
            for (i = 0; i < Rows; i++)
            {
                Checked = Convert.ToInt32(dataGridView1.Rows[i].Cells["checkboxcol"].FormattedValue);

                CheckedCount += Checked;

                richTextBox3.Text = CheckedCount.ToString();
            }


        for (int z = 1; z < CheckedCount; z++)
        {             
            _MultipleWebClients = new WebClient();

            _MultipleWebClients.DownloadFileCompleted += new AsyncCompletedEventHandler(_DownloadFileCompleted);
            _MultipleWebClients.DownloadProgressChanged += new System.Net.DownloadProgressChangedEventHandler(_DownloadProgressChanged);
            _MultipleWebClients.DownloadFileAsync(new Uri(_downloadUrlList[z].ToString()), @"F:\test" + z + ".mp4");     
        }

    }

(I´m also unable to download more than two files simultaneously - the third download won´t start until the first two are finished)


DownloadProgressChangedEvent:

    private void _DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
        for (int c = 0; c < CheckedCount; c++)
        {
            dataGridView1.Rows[_downloadRowNrList[c]].Cells[3].Value = e.ProgressPercentage;
        }

        float size = ((e.TotalBytesToReceive / 1024) / 1024);
        label1.Text = size.ToString();

        double dn = (double)e.BytesReceived / 1024.0 / (DateTime.Now - start).TotalSeconds;
        label2.Text = (dn.ToString("n") + " KB/s) " + e.ProgressPercentage);        
    }

The problem probably is, that all progressbars are using the same DownloadProgressChangedEvent, but I´m not sure how to create multiple of these events without knowing the needed number...

So i hope that someone is able to help me with this,

thanks in advance!

Upvotes: 2

Views: 1110

Answers (2)

YoryeNathan
YoryeNathan

Reputation: 14502

Sounds like you need a progress bar for multi-parted progress:

public partial class ProgressBarEx : ProgressBar
{
    private readonly Dictionary<Guid, double> _partsProgress =
        new Dictionary<Guid, double>();
    private readonly Dictionary<Guid, double> _partsSizes =
        new Dictionary<Guid, double>();

    private double _value;
    private double _maximum;

    public ProgressBarEx()
    {
        this.InitializeComponent();
    }

    public int Parts
    {
        get { return this._partsSizes.Count; }
    }

    public new int Minimum { get; private set; }

    public new double Maximum
    {
        get { return this._maximum; }
        private set
        {
            this._maximum = value;
            base.Maximum = (int)value;
        }
    }

    public new double Value
    {
        get { return this._value; }
        private set
        {
            this._value = value;
            base.Value = (int)value;
        }
    }

    [Obsolete("Not useable in ProgressBarEx.")]
    public new int Step
    {
        get { return 0; }
    }

    public Guid AddPart(double size)
    {
        if (size <= 0)
        {
            throw new ArgumentException("size");
        }

        var partId = Guid.NewGuid();

        this.Maximum += size;
        this._partsSizes.Add(partId, size);
        this._partsProgress.Add(partId, 0);

        return partId;
    }

    public bool RemovePart(Guid partId)
    {
        double size;
        if (!this._partsSizes.TryGetValue(partId, out size))
        {
            return false;
        }

        this.Maximum -= size;
        this._partsSizes.Remove(partId);

        this.Value -= this._partsProgress[partId];
        this._partsProgress.Remove(partId);

        return true;
    }

    public bool ContainsPart(Guid partId)
    {
        return this._partsSizes.ContainsKey(partId);
    }

    public double GetProgress(Guid partId)
    {
        return this._partsProgress[partId];
    }

    public void SetProgress(Guid partId, double progress)
    {
        if (progress < 0 || this._partsSizes[partId] < progress)
        {
            throw new ArgumentOutOfRangeException("progress");
        }

        this.Value += progress - this._partsProgress[partId];
        this._partsProgress[partId] = progress;
    }

    public void AddProgress(Guid partId, double progress)
    {
        this.SetProgress(partId, progress + this._partsProgress[partId]);
    }

    [Obsolete("Not useable in ProgressBarEx.")]
    public new void PerformStep()
    {
    }
}

Example usage:

public Form1()
{
    InitializeComponent();

    var pbe = new ProgressBarEx {Location = new Point(100, 100)};
    this.Controls.Add(pbe);

    for (var i = 0; i < 4; i++)
    {
        var size = i * 10 + 30;

        var partId = pbe.AddPart(size);

        var pb = new ProgressBar
                     {
                         Maximum = size,
                         Location = new Point(100, i * 30 + 130)
                     };

        this.Controls.Add(pb);

        var timer = new Timer {Interval = 1000 + i * 100};

        timer.Tick += (sender, args) =>
                          {
                              pb.Value += 5;
                              pbe.AddProgress(partId, 5);

                              if (pb.Value == pb.Maximum)
                              {
                                  timer.Stop();
                              }
                          };

        timer.Start();
    }
}

Upvotes: 0

jglouie
jglouie

Reputation: 12880

What you want to do is use the other DownloadFileAsync method: http://msdn.microsoft.com/en-us/library/ms144197.aspx

The third parameter is a userToken which gets passed as part of the DownloadProgressChangedEventArgs (it's in the UserState property).

So, when you make the DownloadFileAsync call, pass in a unique token (an integer, or something else) that you can then associate with the progressBar that needs updating.

    //(Snip)

    //in download_button_Click, pass the row you are updating to the event.
    for (int z = 1; z < CheckedCount; z++)
    {             
        _MultipleWebClients = new WebClient();

        _MultipleWebClients.DownloadFileCompleted += new AsyncCompletedEventHandler(_DownloadFileCompleted);
        _MultipleWebClients.DownloadProgressChanged += new System.Net.DownloadProgressChangedEventHandler(_DownloadProgressChanged);
        _MultipleWebClients.DownloadFileAsync(new Uri(_downloadUrlList[z].ToString()), @"F:\test" + z + ".mp4", dataGridView1.Rows[z]);     
    }
}

private void _DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    var rowToUpdate = (DataGridViewRow)e.UserState;
    RowToUpdate["ProgressBar"].Value = e.ProgressPercentage;
    RowToUpdate["TextProgress"].Value = e.ProgressPercentage;
    RowToUpdate["BytesToRecive"].Value = ((e.TotalBytesToReceive / 1024) / 1024).ToString();

    double dn = (double)e.BytesReceived / 1024.0 / (DateTime.Now - start).TotalSeconds;
    RowToUpdate["Speed"].Value = (dn.ToString("n") + " KB/s) " + e.ProgressPercentage);
}

Upvotes: 2

Related Questions