Winforms:避免应用程序冻结

3

我正在对一个“大”文件(约4 MB)进行一些操作。

我这样做:

  1. 从目录中获取所有文件并将它们放入一个列表中,MyInfoClass有以下属性:名称、扩展名、完整路径、创建日期和内容部分。
  2. 我使用Linq查询来仅获取一些特定类型的扩展名。
  3. 我循环遍历Linq查询结果,并为每个文件打开文件并执行一些操作(获取值),然后将结果放入MyFileIno.ContentPart中。

30个文件,需要14秒时间。

这个方法是有效的。

问题在于,当我从UI运行我的库时,当我点击按钮时,窗口在操作期间会冻结。我想要:

  1. 不冻结窗体的解决方案。
  2. 查看进度操作。

您能否为我提供解决此类问题的最佳实践?

谢谢。

代码:

public class FileManager 
{
    public string CurrentFileName { get; set; }
    public void Validation(string path)
    {
        IList<InfoFile> listFile = GetListFile(path);
        foreach (InfoFile item in listFile)
        {
            CurrentFileName = item.Name;
            .....
        }
    }
}


private void button1_Click(object sender, EventArgs e)
{
    var worker = new BackgroundWorker();
    worker.DoWork += (s, args) =>
    {
        int percentProgress = 6;
        FileManager fileManager = new FileManager();
        fileManager.Validation(@"C:.....");
        ((BackgroundWorker)s).ReportProgress(percentProgress, fileManager.CurrentFileName);
    };

    worker.ProgressChanged += (s, args) =>
    {
        var currentFilename = (string)args.UserState;
        label1.Text = currentFilename;
        progressBar1.Value = args.ProgressPercentage;
    };

    worker.RunWorkerCompleted += (s, args) =>
    {
        progressBar1.Value = 0;
    };
    worker.RunWorkerAsync();
}
1个回答

20
应用程序冻结是因为您正在主线程中执行文件解析。您可以使用BackgroundWorker在新线程上执行操作。以下是一些伪代码,可能会帮助您入门:
private void button1_Click(object sender, EventArgs e)
{
    var worker = new BackgroundWorker();
    worker.DoWork += (s, args) =>
    {
        // Here you perform the operation and report progress:
        int percentProgress = ...
        string currentFilename = ...
        ((BackgroundWorker)s).ReportProgress(percentProgress, currentFilename);
        // Remark: Don't modify the GUI here because this runs on a different thread
    };
    worker.ProgressChanged += (s, args) =>
    {
        var currentFilename = (string)args.UserState;
        // TODO: show the current filename somewhere on the UI and update progress
        progressBar1.Value = args.ProgressPercentage;
    };
    worker.RunWorkerCompleted += (s, args) =>
    {
        // Remark: This runs when the DoWork method completes or throws an exception
        // If args.Error != null report to user that something went wrong
        progressBar1.Value = 0;
    };
    worker.RunWorkerAsync();
}

谢谢。对于GUI的好评,我遇到了异常... :) 我想在处理中使用文件名而不是percentProgress,为什么不加上百分比呢? - TheBoubou
你可以使用 BackgroundWorker.ReportProgress(int, object),其中第二个参数是自定义用户状态对象,你可以在 ProgressChanged 回调函数中通过 args.UserState 使用它。 - Darin Dimitrov
在我的业务类中,在循环中,我设置了一个属性来表示当前正在处理的文件。但是我从未传递“ProgressChanged”。 - TheBoubou
@Kris,每次调用ReportProgress方法时都会调用ProgressChanged回调函数,因此您需要向业务对象传递一个委托,该委托将指向ReportProgress方法,并定期调用它。 - Darin Dimitrov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接