目录
1.界面设计
在界面的设计上我使用了VS自带工具箱中的listView和Button控件。而在listView控件中我进行了“编辑列”操作为控件添加了标题行。
2.文件下载功能实现
我创建了一个新的项目来对文件下载进行编写
2.1定义成员变量
public int ThreadNum = 3;
List<Thread> list = new List<Thread>();
我使用了一个变量ThreadNum来确定线程池中同时运行的线程数量 。并使用一个列表来存储和管理多个线程对象。
2.2定义DownloadFile()构造函数
public DownLoadFile()
{
doSendMsg += Change;
}
创建DownloadFile类的对象时,将Change方法添加到doSendMsg事件的处理程序列表中。
2.3定义事件和委托
public delegate void dlgSendMsg(DownMsg msg);
public event dlgSendMsg doSendMsg;
定义了一个委托dlgSendMsg,用于发送DownMsg消息。
是一个事件doSendMsg,它基于dlgSendMsg委托。
2.4处理下载状态变化的方法
private void Change(DownMsg msg)
{
if (msg.Tag == DownStatus.Error || msg.Tag == DownStatus.End)
{
StartDown(1);
}
}
检查传入的DownMsg对象msg的Tag属性是否为DownStatus.Error或DownStatus.End。如果是,则调用StartDown(1)尝试启动一个新的下载线程。
2.5添加一个下载任务到线程列表中
public void AddDown(string DownUrl, string Dir, int Id = 0, string FileName = "")
{
Thread tsk = new Thread(() =>
{
download(DownUrl, Dir, FileName, Id);
});
list.Add(tsk);
}
1.创建一个新的线程实例tsk,并指定线程要执行的代码块(调用download方法)。
2.将新创建的线程实例tsk添加到线程列表list中。
2.6启动线程列表中的未启动或已挂起的线程。
public void StartDown(int StartNum = 3)
{
for (int i2 = 0; i2 < StartNum; i2++)
{
lock (list)
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].ThreadState == System.Threading.ThreadState.Unstarted || list[i].ThreadState == ThreadState.Suspended)
{
list[i].Start();
break;
}
}
}
}
}
1.循环StartNum次(默认为3)。
2.在每次循环中,使用lock语句确保线程安全地访问线程列表list。
3.遍历线程列表,找到第一个未启动或已挂起的线程,并启动它。
2.7执行下载操作
private void download(string path, string dir, string filename, int id = 0)
{
try
{
DownMsg msg = new DownMsg();
msg.Id = id;
msg.Tag = 0;
doSendMsg(msg);
FileDownloader loader = new FileDownloader(path, dir, filename, ThreadNum);
loader.data.Clear();
msg.Tag = DownStatus.Start;
msg.Length = (int)loader.getFileSize(); ;
doSendMsg(msg);
DownloadProgressListener linstenter = new DownloadProgressListener(msg);
linstenter.doSendMsg = new DownloadProgressListener.dlgSendMsg(doSendMsg);
loader.download(linstenter);
}
catch (Exception ex)
{
DownMsg msg = new DownMsg();
msg.Id = id;
msg.Length = 0;
msg.Tag = DownStatus.Error;
msg.ErrMessage = ex.Message;
doSendMsg(msg);
Console.WriteLine(ex.Message);
}
}
这段代码实现了文件下载功能,并可以在下载过程中通过事件通知订阅者关于下载的状态和进度。如果下载过程中发生错误,还会捕获异常。
3.界面功能实现
3.1Buttond的功能
private void btnTest_Click(object sender, EventArgs e)
{
string[] lines = File.ReadAllLines("软件下载1.txt");
for (int i = 0; i < lines.Length; i++)
{
string[] line = lines[i].Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
if (line.Length == 2)
{
string path = Uri.EscapeUriString(line[1]);
string filename = Path.GetFileName(path);
string dir = @"F:\test";
ListViewItem item = listView1.Items.Add(new ListViewItem(new string[] { (listView1.Items.Count + 1).ToString(), filename, "0", "0", "0%", "0", "0", DateTime.Now.ToString(), "等待中", line[1] }));
int id = item.Index;
dlf.AddDown(path, dir, id, id.ToString());
}
}
dlf.StartDown();
}
这段代码的目的是从文本文件中读取下载信息,并在listView控件中显示这些信息,然后启动下载过程。
3.2Windows窗体的设计
private void Form1_Load(object sender, EventArgs e)
{
dlf.ThreadNum = 3;//线程数,不设置默认为3
dlf.doSendMsg += SendMsgHander;//下载过程处理事件
}
这段代码在窗体加载时设置了下载管理器dlf
的线程数,并注册了一个事件处理程序来响应下载过程中的消息。这样,当窗体加载完成后,下载管理器就可以根据这些设置和事件处理程序来执行和管理下载任务了。
3.3事件处理
private void SendMsgHander(DownMsg msg)
{
switch (msg.Tag)
{
case DownStatus.Start:
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[8].Text = "开始下载";
listView1.Items[msg.Id].SubItems[7].Text = DateTime.Now.ToString();
});
break;
case DownStatus.GetLength:
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[3].Text = msg.LengthInfo;
listView1.Items[msg.Id].SubItems[8].Text = "连接成功";
});
break;
case DownStatus.End:
case DownStatus.DownLoad:
this.Invoke(new MethodInvoker(() =>
{
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[2].Text = msg.SizeInfo;
listView1.Items[msg.Id].SubItems[4].Text = msg.Progress.ToString() + "%";
listView1.Items[msg.Id].SubItems[5].Text = msg.SpeedInfo;
listView1.Items[msg.Id].SubItems[6].Text = msg.SurplusInfo;
if (msg.Tag == DownStatus.DownLoad)
{
listView1.Items[msg.Id].SubItems[8].Text = "下载中";
}
else
{
listView1.Items[msg.Id].SubItems[8].Text = "下载完成";
}
Application.DoEvents();
});
}));
break;
case DownStatus.Error:
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[6].Text = "失败";
listView1.Items[msg.Id].SubItems[8].Text = msg.ErrMessage;
Application.DoEvents();
});
break;
}
}
这段代码的主要作用是处理来自下载管理器的消息,并根据消息内容更新UI中的 listView
控件,以反映下载任务的当前状态、进度和其他相关信息。
4.成果展示
可以看到程序能够正确运行。需要提醒以下,在下载第四个文件时我故意给了一个错误的样例来验证如果下载出现错误时程序会如何运行。
5.完整代码
6.总结
本C#程序实现了一个功能全面的文件下载工具。该程序从文本文件中读取下载链接和文件名,并通过ListView控件将这些信息直观地展示给用户。通过点击“测试”按钮,用户能够触发多线程下载器开始下载文件。该程序允许用户自定义线程数,以适应不同的网络环境,并实时更新ListView中的下载进度信息,包括文件大小、下载速度、剩余时间等关键指标。若下载过程中发生错误,程序具备自动重试下载的功能,确保文件能够完整下载。该程序不仅展示了C#在多线程编程方面的强大能力,也体现了Windows窗体编程的便捷性和实用性。通过这个项目,开发者能够深入学习C#编程技巧,理解多线程编程的原理,并熟悉处理文件下载等常见应用场景的方法,为开发功能丰富的桌面应用程序打下坚实的基础。