C# 通过线程将文件添加到列表框?

C# 通过线程将文件添加到列表框?,c#,winforms,C#,Winforms,在我的应用程序中,我想将文件添加到我的列表框中。 如果我的文件不是pcap扩展名,我想将文件路径发送到我的类,并将其转换为pcap扩展名,然后将此文件添加到我的列表框中。 如果我选择添加namy文件,GUI在我的应用程序完成添加或转换该文件之前不会响应,我想知道如何添加选项以通过线程完成所有这些操作 private void btnAddfiles_Click(object sender, EventArgs e) { System.IO.Stream stream; OpenF

在我的应用程序中,我想将文件添加到我的列表框中。 如果我的文件不是pcap扩展名,我想将文件路径发送到我的类,并将其转换为pcap扩展名,然后将此文件添加到我的列表框中。 如果我选择添加namy文件,GUI在我的应用程序完成添加或转换该文件之前不会响应,我想知道如何添加选项以通过线程完成所有这些操作

private void btnAddfiles_Click(object sender, EventArgs e)
{
    System.IO.Stream stream;
    OpenFileDialog thisDialog = new OpenFileDialog();
    thisDialog.InitialDirectory = (lastPath.Length > 0 ? lastPath : "c:\\");
    thisDialog.Filter = "(*.snoop, *.pcap, *.cap, *.net, *.pcapng, *.5vw, *.bfr, *.erf, *.tr1)" +
        "|*.snoop; *.pcap; *.cap; *.net; *.pcapng; *.5vw; *.bfr; *.erf; *.tr1|" + "All files (*.*)|*.*";
    thisDialog.FilterIndex = 1;
    thisDialog.RestoreDirectory = false;
    thisDialog.Multiselect = true;
    thisDialog.Title = "Please Select Source File";

    if (thisDialog.ShowDialog() == DialogResult.OK)
    {
        if (thisDialog.FileNames.Length > 0)
        {
            lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
        }

        foreach (String file in thisDialog.FileNames)
        {
            try
            {
                if ((stream = thisDialog.OpenFile()) != null)
                {
                    using (stream)
                    {
                        string fileToAdd = string.Empty;
                        Editcap editcap = new Editcap();


                            BackgroundWorker backgroundWorker = new BackgroundWorker();
                            backgroundWorker.WorkerReportsProgress = true;
                            backgroundWorker.DoWork += new DoWorkEventHandler(
                            (s3, e3) =>
                            {
                                if (!editcap.isLibpcapFormat(file))
                                {
                                    fileToAdd = editcap.getNewFileName(file);
                                }
                                else
                                {
                                    listBoxFiles.Items.Add(file);
                                }
                            });

                            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
                                (s3, e3) =>
                                {
                                    listBoxFiles.Items.Add(fileToAdd);
                                });

                            backgroundWorker.RunWorkerAsync();

                        lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
                    }
                }
            }

            catch (Exception ex)
            {
                MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
            }
        }

    }
}

您的应用程序正在冻结,因为您在UI线程中做了大量工作。您需要将长时间运行的任务移动到后台线程,然后在UI线程中更新UI

为了做到这一点,您需要做的第一件事是将长期运行的任务与UI操作分离开来。目前,您正在将这两者混合在一起,这导致您对如何将其映射到
BackgroundWorker
感到困惑

只要您不需要迭代更新列表框,并且可以一次在末尾添加所有项目(这是我期望从列表框中得到的),您只需在一个位置执行文件IO,将结果添加到某种类型的集合中(
List
在这里可能合适),然后单独,您可以将列表中的所有项目添加到
列表框
(或使用数据绑定)

一旦你做出了这样的改变,使用
后台工作人员
就很容易了。填充
列表的IO工作进入
工作
,在后台运行,然后设置
结果
。然后,
RunWorkerCompleted
事件获取该列表并将项目添加到
ListBox

如果您迫切需要在运行时将这些项目添加到列表框中,因此随着时间的推移,您会看到一个项目,然后是下一个项目,等等,那么只需将其视为“报告进度”,并使用
BackgroundWorker
中内置的相关进度报告功能即可。更新循环内部的进度,并在进度报告的事件处理程序中获取给定的值并将其放入
列表框中

下面是一个实现:

private void btnAddfiles_Click(object sender, EventArgs e)
{
    System.IO.Stream stream;
    OpenFileDialog thisDialog = new OpenFileDialog();
    thisDialog.InitialDirectory = (lastPath.Length > 0 ? lastPath : "c:\\");
    thisDialog.Filter = "(*.snoop, *.pcap, *.cap, *.net, *.pcapng, *.5vw, *.bfr, *.erf, *.tr1)" +
        "|*.snoop; *.pcap; *.cap; *.net; *.pcapng; *.5vw; *.bfr; *.erf; *.tr1|" + "All files (*.*)|*.*";
    thisDialog.FilterIndex = 1;
    thisDialog.RestoreDirectory = false;
    thisDialog.Multiselect = true;
    thisDialog.Title = "Please Select Source File";

    if (thisDialog.ShowDialog() == DialogResult.OK)
    {
        if (thisDialog.FileNames.Length > 0)
        {
            lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
        }

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.DoWork +=
        (s3, e3) =>
        {
            //TODO consider moving everything inside of the `DoWork` handler to another method
            //it's a bit long for an anonymous method
            foreach (String file in thisDialog.FileNames)
            {
                try
                {
                    if ((stream = thisDialog.OpenFile()) != null)
                    {
                        using (stream)
                        {
                            Editcap editcap = new Editcap();
                            if (!editcap.isLibpcapFormat(file))
                            {
                                string fileToAdd = editcap.getNewFileName(file);
                                backgroundWorker.ReportProgress(0, fileToAdd);
                            }
                            else
                            {
                                backgroundWorker.ReportProgress(0, file);
                            }


                            lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
                        }
                    }
                }

                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
        };

        backgroundWorker.ProgressChanged +=
            (s3, arguments) =>
            {
                listBoxFiles.Items.Add(arguments.UserState);
            };

        backgroundWorker.RunWorkerAsync();

    }
}

您可以使用BackgroundWorker执行此操作: 通过工具箱将backgroundWorker添加到表单中

首先是:

backgroundWorker.RunWorkerAsync(new string[] {parm1, parm2});
将事件添加到backgroundWorker(属性窗口)


使用DoWork进行计算。然后使用RunWorkerCompleted应用设置。

您见过
BackgroundWorker
类吗?我知道BackgroundWorker类,并在我的应用程序中使用它,但我不知道如何使用它,也不知道如何在文件从转换器返回之前尝试添加文件如果您想访问另一个转换器中的WinForms元素,可能需要使用它线程。@AlvinWong解决这个问题根本不需要调用
Invoke
。如果可能的话,您应该(在这里)使用抽象,例如
BackgroundWorker
Task
,或者
wait
,在线程之间为您进行封送。请参阅我的更新,现在我正在使用BackgroundWorker,但GUi仍然冻结。您是否仔细检查了代码以查看它冻结的位置?为什么不加上:'editcap editcap=neweditcap();如果backgroundWorker中有(!editcap.isLibpcapFormat(file)),我会再次更新代码,现在代码不会冻结,但我认为最好的方法是实现类似Queuesee my update的功能。现在我使用backgroundWorker,但GUi仍然可用freeze@user1710944您的
BackgroundWorker
是一个范围中非常局部的一个。95%的代码应该在
DoWork
事件处理程序中。按钮单击处理程序除了显示对话框、创建BGW、分配事件处理程序并启动它之外,应该什么都不做。它不应该做任何实际的工作。我可以有一个代码示例,你想说什么?(我是一名新开发人员.)@user1710944您已经完成了大部分工作。正如我在文章中所说,您需要将数据收集和UI更新分离开来。您应该创建一个集合,例如
列表
,并将数据放入循环内的列表中。然后,您应该在循环结束后将该列表中的项目添加到
列表框中。完成此操作后,整个
foreach
循环可以进入
DoWork
处理程序,用于更新
列表框的整个循环可以进入
RunWorkerCompleted
处理程序。你是新来的,这就是为什么你需要自己去做。每次文件准备好后,把这个项目添加到我的列表框中是不是更好?例如,如果我需要添加许多大文件,我希望看到文件“正在”添加到我的列表框中,而不是等待循环完成(可能需要1分钟)-可能类似于队列?