用Socket下载文件,想做个进度条,但是显示不了进度

我本来是用Socket下载文件,想做个进度条,对客户有个提示,避免长时间无聊的等待。
代码如下:


private void setPrograss(int alllen, int curlen)
        {
            System.Drawing.Size grs = new System.Drawing.Size(alllen, curlen);
            progressBar2.Invoke(new EventHandler(setPrograss), new object[] { grs, EventArgs.Empty });
            //执行到这行就停止执行了,至少等待了五六分钟没反应
        }
        public delegate void degPrograss(int allLen, int curLen);
        private void setPrograss(object data, EventArgs e)
        {
            System.Drawing.Size grs = (System.Drawing.Size)data;
            if (progressBar2.Maximum == 1)
            {//实际长度不可能为1
                progressBar2.Maximum = grs.Width;
            }
            progressBar2.Value = grs.Height;
        }

现在问题是执行到
progressBar2.BeginInvoke(new EventHandler(setPrograss), new object[] { grs, EventArgs.Empty });
这句时,程序就不运行了,查网上说换BeginInvoke,结果是运行了,但进度条也不显示呀!
很急,请各位帮忙看看,要怎样写才对?

System.ComponentModel命名空间下,已经有了一个ProgressChangedEventHandler。

你自己定义的事件调用,滥用了事件的通用模式,代码比较难理解,还容易造成误解。最好直接用ProgressChangedEventHandler。


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

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            // InitializeComponent();
            button.Click += Button_Click;
            this.Controls.AddRange(new Control[] { progressBar2, button });
        }

        private void Button_Click(object sender, EventArgs e)
        {
            button.Enabled = false;
            ThreadPool.QueueUserWorkItem(state => 
            {
                for(int i = 1; i <= 100; i++)
                {
                    OnProgressChanged(this, new ProgressChangedEventArgs(i, null));
                    Thread.Sleep(50); // 模拟Socket下载文件
                }
            });
        }

        private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if(progressBar2.InvokeRequired)
            {
                progressBar2.Invoke((ProgressChangedEventHandler)OnProgressChanged, sender, e);
            }
            else
            {
                progressBar2.Value = e.ProgressPercentage;
                button.Enabled = progressBar2.Value == 100;
            }
        }

        ProgressBar progressBar2 = new ProgressBar() { Dock = DockStyle.Fill, Maximum = 100};
        Button button = new Button() { Text = "开始", Dock = DockStyle.Bottom };
    }
}

 

另外一种选择是使用Winform的BackgroundWorker。BackgroundWorker已经把UI Invoke帮我们做好了。


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

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            // InitializeComponent();
            button.Click += Button_Click;

            this.Controls.AddRange(new Control[] { progressBar2, button });
            backgroundWorker.DoWork += delegate
            {
                for (int i = 1; i <= 100; i++)
                {
                    backgroundWorker.ReportProgress(i);
                    Thread.Sleep(50); // 模拟进度
                }
            };
            backgroundWorker.ProgressChanged += OnProgressChanged;

        }

        private void Button_Click(object sender, EventArgs e)
        {
            button.Enabled = false;
            backgroundWorker.RunWorkerAsync();
        }

        private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar2.Value = e.ProgressPercentage;
            button.Enabled = progressBar2.Value == 100;
        }

        BackgroundWorker backgroundWorker = new BackgroundWorker() { WorkerReportsProgress = true};
        ProgressBar progressBar2 = new ProgressBar() { Dock = DockStyle.Fill, Maximum = 100};
        Button button = new Button() { Text = "开始", Dock = DockStyle.Bottom };
    }
}

 

其实跟你贴出的代码没啥大的关系,而是跟你的“客户调用”代码有关。当UI主线程循环调用它,不切换、不释放,那么进度条控件根本得不到刷新显示的机会。

随便写一个例子:

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

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            for (var i = 0; i <= 100; i++)  //循环中你可以拖动窗口测试UI并发逻辑
            {
                await Task.Delay(300);
                await SetProgress(i);
            }
        }

        private async Task SetProgress(int n)
        {
            await Task.Yield();
            this.progressBar1.Value = n;
        }
    }
}

 

你可以看到,过程要异步操作,让 UI 线程得以释放、切换,才能看出并发效果来。

或者更简单直接点,直接写为这样的吧:

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

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            for (var n = 0; n <= 100; n++)  //循环中你可以拖动窗口测试UI并发逻辑
            {
                await Task.Delay(300);
                this.progressBar1.Value = n;
            }
        }
    }
}
你可以看到,this.progressBar1.Value = n; 这一句是同步代码,相当于你题目中的调用逻辑。这一句是阻塞 UI 线程的。但 是
小蜜蜂论坛发帖机因为有了 await Task.Delay()语句,那么这句同步阻塞代码被异步调用它了,所以它可用肉眼可能到显示了并发的效果。

发布了74 篇原创文章 · 获赞 0 · 访问量 3079

猜你喜欢

转载自blog.csdn.net/netyou/article/details/104511769