Как обновлять индикаторы выполнения по отдельности (одновременные загрузки)

В настоящее время я пытаюсь добавить в свое приложение параллельные загрузки, но я не знаю, как обрабатывать событие DownloadProgressChangedEvent, чтобы отображать прогресс на нескольких индикаторах выполнения.

Я использую datagridview с предопределенными строками для каждого файла, который пользователь может загрузить, и в каждой строке есть ячейка с индикатором выполнения.

Проблема сейчас в том, что я не знаю, как обновлять каждый индикатор выполнения по отдельности, потому что сейчас все выбранные индикаторы выполнения показывают одинаковый процент, и они просто переключаются между этапами загрузки1 и загрузки2.

Вот код, который я использую:

Чтобы начать загрузку:

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");     
        }

    }

(Я также не могу загрузить более двух файлов одновременно - третья загрузка не начнется, пока не закончатся первые два)


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);        
    }

Проблема, вероятно, в том, что все индикаторы выполнения используют одно и то же событие DownloadProgressChangedEvent, но я не уверен, как создать несколько таких событий, не зная нужного числа ...

Так что я надеюсь, что кто-нибудь сможет мне в этом помочь,

заранее спасибо!


person f4bzen    schedule 07.06.2012    source источник


Ответы (2)


Вы хотите использовать другой метод DownloadFileAsync: http://msdn.microsoft.com/en-us/library/ms144197.aspx

Третий параметр - это userToken, который передается как часть DownloadProgressChangedEventArgs (он находится в свойстве UserState).

Итак, когда вы делаете вызов DownloadFileAsync, передайте уникальный токен (целое число или что-то еще), который вы затем можете связать с progressBar, которое необходимо обновить.

    //(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);
}
person jglouie    schedule 07.06.2012
comment
fyi: для токена пользователя вы передаете полосу выполнения, которую хотите обновить. @jglouie, я могу добавить быстрый пример кода, если хотите (писал один для своего ответа, но я скорее отдаю вам должное) - person Scott Chamberlain; 07.06.2012
comment
Конечно (пример был бы полезен для OP). Либо передайте сам progressBar, либо, если есть другие параметры, используйте настраиваемый класс или какой-либо тип уникального ключа, который может использоваться со словарем поиска. Множество вариантов :) - person jglouie; 07.06.2012
comment
Поразмыслив над этим, было больше смысла передавать ряд вместо одной полосы. Если я допустил ошибки, исправляйте их. - person Scott Chamberlain; 07.06.2012
comment
Прежде всего, спасибо за ответ! Но у меня все еще есть проблемы с этим примером кода. Мне нужно как-то изменить следующую строку? var rowToUpdate = (DataGridViewRow)e.UserState; Я всегда получаю invalidCastException в этой строке - person f4bzen; 07.06.2012
comment
Что вы передаете в качестве третьего параметра функции DownloadFileAsync ()? - person jglouie; 07.06.2012
comment
Я пробовал это с простым целым числом, а также с точным номером строки индикатора выполнения. Я также не совсем уверен, что мне с этим делать var rowToUpdate - person f4bzen; 08.06.2012
comment
Хорошо - все, что вы передаете в качестве третьего параметра, будет отображаться как e.UserState. Поэтому вам нужно передать строку (dataGridView1.Rows [z]), если вы собираетесь преобразовать ее как DataGridViewRow в обработчике. Имеет ли это смысл? - person jglouie; 08.06.2012
comment
Ну да, в этом есть смысл ... [ссылка] i45.tinypic.com/2wbs5rr.jpg) Это мой datagridview с двумя выбранными файлами, которые я хочу загрузить. Теперь будет два разных объекта UserState, верно? Итак, что мне делать с этим e.UserState в моем DownloadProgressChanged-Event, чтобы получать информацию о ходе каждой загрузки по отдельности? - person f4bzen; 08.06.2012

Похоже, вам нужен индикатор выполнения для многостраничного прогресса:

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()
    {
    }
}

Пример использования:

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();
    }
}
person SimpleVar    schedule 07.06.2012