【问题标题】:Updating progressbar from another class in c#从 C# 中的另一个类更新进度条
【发布时间】:2019-07-20 06:28:39
【问题描述】:

我在获取进度条以显示下载进度时遇到问题。文件正在下载没有问题,但有些东西导致我的进度条没有更新,我不知道为什么。

我尝试在downloadwc_DownloadProgressChanged 方法中手动设置progressBar 值,但它实际改变的唯一地方是Form1_Load 方法。

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

        }

    }
}

有人能解释一下我在这里做错了什么吗?

编辑:

我能够使用DownloadFileAsync 让这个工作大部分时间,但进度条来回跳动,我假设是因为它试图计算每个人的进度文件作为字节被接收,所以我想用 DownloadFile 来解决这个问题。

我现在在使用 DownloadFile 时遇到的问题是,我将它作为一项任务运行,但它跳过了所有文件(不下载任何文件,只是将它们全部打印到控制台超快速)。

这是我目前使用的代码:

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

        }    

有什么想法吗?

【问题讨论】:

  • foreach (DataRow row in dataTable.Rows) (...) client.DownloadProgressChanged += client_DownloadProgressChanged;,都给var form = new Form1(); form.SetProgress(e.ProgressPercentage);?我建议使用静态 HttpClient 和 Progress<T> 类。它还会阻止您的代码(在工作时)与自己争夺可用连接。
  • 到目前为止,每次 WebClient 实例通知进度时,您都会(从非 UI 线程)创建一个新表单。你最终可能会得到数千个。如果代码可以在这些条件下工作。
  • 可能,比以前更糟。一个 WebClient 应该在线程池线程中限制多个同步下载,从 Form.Load 事件开始(错误的选择,Load 事件吞噬了异常)。顺便说一句,你为什么还要继续调用DownloadFile,因为这是同步版本,它也不会引发DownloadProgressChanged 事件。测试DownloadFileTaskAsync 版本。使用前面提到的Progress&lt;T&gt; 类。 SO 上提供了关于这些的代码示例。
  • 另外,所有重叠事件(如果生成了这些事件)应该使用单个 ProgressBar 通知进度。如果您发现基于任务的方法难以实现,请选择事件驱动的方法DownloadFileAsync。这将引发DonwloadProgressDownloadCompleted 事件(您还需要订阅后者)。您必须处理多个 Progress 事件并更新多个 ProgressBar。您还需要处理您创建的每个 WebClient 实例。

标签: c# multithreading progress-bar


【解决方案1】:

使用下面的代码:

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

【讨论】:

  • OP 正在尝试同时从多个不同的远程资源下载多个文件,线程化进程。这只是设置一个下载。顺便说一句,您应该在调用 DownloadStringAsync 之前连接处理程序。
  • 可以在方法中使用,单独调用。但是一个进度条不能处理多个下载。只需要获取多个下载进度值并取平均值,然后显示在一个进度条中。
  • 这就是为什么 OP 试图创建一个带有 ProgressBar 的新表单来报告每次下载的进度。顺便说一句,OP 使用的是DownloadFile 而不是DownloadString(或DownloadStringAsync)。 OP 还应该验证 DownloadFile 是否真的引发了 DownloadProgress/DownloadComplete 事件。
  • 不幸的是,这个例子违背了我想要达到的目的,即在进度条中设置下载进度从另一个类
【解决方案2】:

这可能应该属于https://codereview.stackexchange.com/

首先,不要在循环中添加处理程序

client.DownloadProgressChanged += client_DownloadProgressChanged;

第一次没问题,第二个项目会被调用 2 次,第三次会被调用 3 次,等等。你想设置一次处理程序。将其移到行后:

WebClient client = new WebClient();

其次,每次触发进度更新时,您都会创建一个新的表单实例。创建一次私有变量或属性。

private Form1 form = new Form1();

编辑:

在 UI 的情况下,通常您只能在创建它的调度程序线程中修改它或使用封送处理。我会删除它,因为我们已经在异步下载字符串。

另外,我不会使用 e.ProgressPercentage,而是类似:

Math.Truncate(e.BytesReceived / (double)e.TotalBytesToReceive * 100)

【讨论】:

  • 嗨,Margus,我已经按照您的建议更改了代码,但进度条似乎仍然没有改变。关于可能出错的任何其他想法?谢谢。
  • 嗨,对不起,我不确定我是否理解,我不是异步下载它,我认为您指的是其他人的答案。不在同一个线程中是有道理的,你能提供一个编组的例子吗?
  • @Zac1989 是的,与 Saeiddjawadi 混合。一个例子stackoverflow.com/questions/661561/…
  • 谢谢你,这给了我一些想法。请在我的原始帖子中查看我的编辑,如果您有任何进一步的见解,我觉得我非常接近一个可行的解决方案。
猜你喜欢
  • 2015-10-30
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
相关资源
最近更新 更多