I'm writing an app that parses a very large logfile, so that the user can see the contents in a treeview format. I've used a BackGroundWorker to read the file, and as it parses each message, I use a BeginInvoke to get the GUI thread to add a node to my treeview. Unfortunately, there's two issues:
- The treeview is unresponsive to clicks or scrolls while the file is being parsed. I would like users to be able to examine (ie expand) nodes while the file is parsing, so that they don't have to wait for the whole file to finish parsing.
- The treeview flickers each time a new node is added.
Here's the code inside the form:
private void btnChangeDir_Click(object sender, EventArgs e)
{
OpenFileDialog browser = new OpenFileDialog();
if (browser.ShowDialog() == DialogResult.OK)
{
tbSearchDir.Text = browser.FileName;
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += (ob, evArgs) => ParseFile(tbSearchDir.Text);
bgw.RunWorkerAsync();
}
}
private void ParseFile(string inputfile)
{
FileStream logFileStream = new FileStream(inputfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader LogsFile = new StreamReader(logFileStream);
while (!LogsFile.EndOfStream)
{
string Msgtxt = LogsFile.ReadLine();
Message msg = new Message(Msgtxt.Substring(26)); //Reads the text into a class with appropriate members
AddTreeViewNode(msg);
}
}
private void AddTreeViewNode(Message msg)
{
TreeNode newNode = new TreeNode(msg.SeqNum);
BeginInvoke(new Action(() =>
{
treeView1.BeginUpdate();
treeView1.Nodes.Add(newNode);
treeView1.EndUpdate();
Refresh();
}
));
}
What needs to be changed?
EDIT
New code, to replace the last function above:
List<TreeNode> nodeQueue = new List<TreeNode>(1000);
private void AddTreeViewNode(Message msg)
{
TreeNode newNode = new TreeNode(msg.SeqNum);
nodeQueue.Add(newNode);
if (nodeQueue.Count == 1000)
{
var buffer = nodeQueue.ToArray();
nodeQueue.Clear();
BeginInvoke(new Action(() =>
{
treeView1.BeginUpdate();
treeView1.Nodes.AddRange(buffer);
treeView1.EndUpdate();
Refresh();
Application.DoEvents();
}
));
}
}
Not sure why I left the Refresh and the DoEvents in there. A bit of testing other comments...