如何使用长处理步骤向C#windows应用程序添加事件

如何使用长处理步骤向C#windows应用程序添加事件,c#,winforms,events,C#,Winforms,Events,我有一个windows窗体应用程序,它的主要任务是读取一个大文本文件中的每一行,并在对该行执行搜索和替换后将该行写入另一个文件。由于文件非常大>20 GB,因此该过程预计需要很长时间才能完成 读取、修改和写入每一行大型文本文件的过程都会非常耗时,因此我希望在此过程中通知用户应用程序的进度。只需计算文件中的行数,然后除以当前行号即可获得完成工作的百分比 我知道这正是事件和代表们的用武之地,但我必须承认,即使在阅读了许多文章之后,我仍然对如何利用这种模式感到困惑。任何关于向我展示如何将事件添加到此应

我有一个windows窗体应用程序,它的主要任务是读取一个大文本文件中的每一行,并在对该行执行搜索和替换后将该行写入另一个文件。由于文件非常大>20 GB,因此该过程预计需要很长时间才能完成

读取、修改和写入每一行大型文本文件的过程都会非常耗时,因此我希望在此过程中通知用户应用程序的进度。只需计算文件中的行数,然后除以当前行号即可获得完成工作的百分比

我知道这正是事件和代表们的用武之地,但我必须承认,即使在阅读了许多文章之后,我仍然对如何利用这种模式感到困惑。任何关于向我展示如何将事件添加到此应用程序的帮助都会很有帮助

下面是我试图做的一个例子。必须将文件中
t
的所有值替换为
x

WinForm类

namespace ParseFileTool
{
    public partial class FormMain : Form
    {
      public FormMain()
      {
          InitializeComponent();
      }

      private void btnStartProcessing_Click(object sender, EventArgs e)
      {
        ProcessFile pf = new ProcessFile();
        pf.InputFilePath = "PATH TO INPUT FILE";
        pf.OutputFilePath = "PATH TO OUTPUT FILE";
        pf.StartProcessing();
      }
    }
}
class ProcessFile
{
    public string InputFilePath { get; set; }
    public string OutputFilePath { get; set; }

    public void StartProcessing()
    {
      using (StreamReader sr = new StreamReader(this.InputFilePath))
      {
        string line;
        while ((line = sr.ReadLine()) != null)
        {
          string newLine = line.Replace("t","x");
          using (FileStream fs = new FileStream(this.OutputFilePath, FileMode.Append, FileAccess.Write))
          using (StreamWriter sw = new StreamWriter(fs))
          {
            sw.WriteLine(newLine);
          }    
        }
      }
    }
}
文件处理器类

namespace ParseFileTool
{
    public partial class FormMain : Form
    {
      public FormMain()
      {
          InitializeComponent();
      }

      private void btnStartProcessing_Click(object sender, EventArgs e)
      {
        ProcessFile pf = new ProcessFile();
        pf.InputFilePath = "PATH TO INPUT FILE";
        pf.OutputFilePath = "PATH TO OUTPUT FILE";
        pf.StartProcessing();
      }
    }
}
class ProcessFile
{
    public string InputFilePath { get; set; }
    public string OutputFilePath { get; set; }

    public void StartProcessing()
    {
      using (StreamReader sr = new StreamReader(this.InputFilePath))
      {
        string line;
        while ((line = sr.ReadLine()) != null)
        {
          string newLine = line.Replace("t","x");
          using (FileStream fs = new FileStream(this.OutputFilePath, FileMode.Append, FileAccess.Write))
          using (StreamWriter sw = new StreamWriter(fs))
          {
            sw.WriteLine(newLine);
          }    
        }
      }
    }
}
我还想知道我是否应该在一个单独的线程上启动
ProcessFile
类的实例,以便允许GUI正确更新,或者这是不必要的,因为要使用事件


谢谢你的帮助。我当然希望能够适应各种活动并订阅它们。我理解委托是什么,但我仍然不确定在何处声明它(winform类或
ProcessFile
类),以及如何处理向事件处理程序传回的数据(如已处理的行号)。

您需要使用后台线程。这里有许多选项,但最简单的是:

Task.Run(() => pf.StartProcessing());

我喜欢
WinForms
BackgroundWorker
,不过也有更新的
async/await
模型可以研究

BackgroundWorker
放到
FormMain
表单上,并设置
ReportsProgress=true
。订阅可用的活动,以便进行工作和报告进度

将不接触UI的任何内容移动到运行在单独线程上的
DoWork
事件中。修改您的循环,使其定期报告进度

public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
  var inputFilePath = "input file path";
  var outputFilePath = "output file Path";

  using (var sr = new StreamReader(inputFilePath))
  {
    int counter = 0;

    string line;
    while ((line = sr.ReadLine()) != null)
    {
      // do all the important stuff

      counter++;

      // report progress after every 100 lines
      // reporting too often can essentially lock the UI by continuously updating it
      if (counter % 100 == 0)
        backgroundWorker1.ReportProgress(counter);
    }
  }
}
ProgressChanged
事件在UI线程上运行,因此您可以接受传递给它的
DoWork
事件并将其显示给用户

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
  lblProgress.Text = string.Format("Processed {0} lines.", e.ProgressPercentage);
}
您可能也想订阅
RunWorkerCompleted
事件

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  lblProgress.Text = "Job finished!";
}
通过调用以下命令启动:

backgroundWorker1.RunWorkerAsync();

可能想调查一下这些——你需要的是一个幕后工作者!就在这里解释!您可以通知进度、取消工作等。。考虑使用观察者模式来封装背景进度条码。此模式将允许您轻松添加更多观察者,如日志或电子邮件。@RageComplex-如果我理解您所指的示例,它看起来像内置事件?对吗?无需声明委托或事件。这是真的吗?只需简单介绍一下.net事件的异步版本,就可以自动将异步添加到中,这样您就可以避免繁琐的后台工作程序,并使用推荐的任务库。感谢您提供了一个很好的示例!如何将文件路径名传递给
backgroundWorker1\u DoWork()
方法?而不是将它们硬编码在方法中。谢谢关于元组的提示。我调用了RunWorkerAsync方法,例如
bgWorker.RunWorkerAsync(Tuple.Create(inputFilePath,outputFilePath))
然后在
DoWork
方法中,我将
e.Argument
转换为Tuple的
var filepath=e.Argumente.ProgressPercentage如何知道完成百分比?似乎已完成的行数已传入。它如何知道文件中有多少行?我需要计算百分比并发送吗?