Reputation: 11019
I have a windows form application that will have a primary task of reading through each line in a large text file and writing that line out to another file after performing some search and replace on that line. Since the files are very large >20 GB the process is expected to take a long time to complete.
The process of reading, modifying, and writing each line of the large text files will be time consuming so I would like to notify the user of the application of the progress during the process. It could be nothing more than counting the number of lines in the file and then dividing the current line number to get a percentage of work completed.
I know that this is where events and delegates come in but I have to admit I remain confused, even after reading many articles, on how to make use of this pattern. Any help in showing me how to add events to this app would be helpful.
Below is an example of what I am attempting to do. Essential replacing all values of t
with x
in the file.
WinForm class
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();
}
}
}
File Processor Class
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);
}
}
}
}
}
I also wanted to know if I should be kicking off the instance of the ProcessFile
class on a separate thread in order to allow the GUI to update properly or is this not necessary because events are to be used.
Thanks for any assistance. I would sure like to become comfortable with events and subscribing to them. I understand what a delegate is but I remain unsure where to declare it (the winform class or the ProcessFile
class) and how to handle passing back data (like the line number processed) to the event handler.
Upvotes: 2
Views: 300
Reputation: 66469
I like the BackgroundWorker
for WinForms
, although there's also the newer async/await
model you can research as well.
Drop a BackgroundWorker
onto your FormMain
form and set ReportsProgress = true
. Subscribe to the available events, for doing work and reporting progress.
Move anything that doesn't touch the UI into the DoWork
event, which runs on a separate thread. Modify your loop so it reports progress at some regular interval.
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);
}
}
}
The ProgressChanged
event runs back on the UI thread, so you can accept whatever the DoWork
event passed to it and display that to the user.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lblProgress.Text = string.Format("Processed {0} lines.", e.ProgressPercentage);
}
You'll probably want to subscribe to the RunWorkerCompleted
event too...
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblProgress.Text = "Job finished!";
}
Start it by calling:
backgroundWorker1.RunWorkerAsync();
Upvotes: 1
Reputation: 1887
You need to use background thread. Many options are here, but most simple is:
Task.Run(() => pf.StartProcessing());
Upvotes: 0