views:

3381

answers:

6

I need to load a ~ 10MB range text file into a WPF RichTextBox, but my current code is freezing up the UI. I tried making a background worker do the loading, but that doesnt seem to work too well either.

Here's my loading code. Is there any way to improve its performance? Thanks.

    //works well for small files only
    private void LoadTextDocument(string fileName, RichTextBox rtb)
    {
        System.IO.StreamReader objReader = new StreamReader(fileName);

        if (File.Exists(fileName))
        {
                rtb.AppendText(objReader.ReadToEnd());
        }
        else rtb.AppendText("ERROR: File not found!");
        objReader.Close();
    }






    //background worker version. doesnt work well
    private void LoadBigTextDocument(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        System.IO.StreamReader objReader = new StreamReader(   ((string[])e.Argument)[0]  );
        StringBuilder sB = new StringBuilder("For performance reasons, only the first 1500 lines are displayed. If you need to view the entire output, use an external program.\n", 5000);

            int bigcount = 0;
            int count = 1;
            while (objReader.Peek() > -1)
            {
                sB.Append(objReader.ReadLine()).Append("\n");
                count++;
                if (count % 100 == 0 && bigcount < 15)
                {
                    worker.ReportProgress(bigcount, sB.ToString());

                    bigcount++;
                    sB.Length = 0;
                }
            }
        objReader.Close();
        e.Result = "Done";
    }
+1  A: 

Why don't you add to a string variable (or perhaps even use StringBuilder) then assign the value to the .Text property when you're done parsing?

AlaaShaker
are you talking about the first block of code or the second?
A: 

Have you considered trying to make the app multi-threaded?

How much of the text file do you need to see at once? You may want to look into lazy-loading in .NET or in your case C#

PSU_Kardi
A: 

This is James...my browser crashed so now I can't comment directly.

Isn't the appending incremental text with stringbuilder what my second block of code does? Why doesn't it work well (takes forever to load)?

James Reever
You're absolutely right - I didn't read your code correctly. Sorry for the unnecessary answer! - Mike
Mike
+2  A: 

Graphical controls just isn't designed to handle that kind of data, simply because it would become unworkable. Even if the control could handle the large string, what's visible in the control is so little compared to the entire text that the scroll bars would become practically useless. To locate a specific line in the text you would have to move the slider to the closest position that it could specify, then scroll a line at a time for minutes...

Instead of submitting your users to something useless like that, you should rethink how you display the data, so that you can do it in a way that would actually be possible to use.

Guffa
It's not an unreasonable thing to ask. I can open multi-GB files in UltraEdit without issue. It's not RTF, sure, but plenty of commercial IDEs handle files in the 10MB range reasonably well -- and keep in mind they have to actually parse the language syntax to get the pretty colors, not just read pre-rendered formatting.
Richard Berg
@Richard: Yes, there are programs that can handle large files, but no simple GUI control. Editing such a large string means that you copy 20 GB of data for every little change, so the control would get extremely sluggish. Programs that handle large files uses a different way of representing the data in memory.
Guffa
A: 

I have notice using RichTextboxes that as you add more "lines" it starts to slow down. If you can do it without appending the '/n' it will speed up for you. Remember each '/n' is a new paragraph object block for the RichTextbox.

This is my method for loading a 10 MB file. It takes about to 30 seconds to load. I use a progress bar dialog box to let my user know it is going to take time to load.

// Get Stream of the file
fileReader = new StreamReader(File.Open(this.FileName, FileMode.Open));

FileInfo fileInfo = new FileInfo(this.FileName);

long bytesRead = 0;

// Change the 75 for performance.  Find a number that suits your application best
int bufferLength = 1024 * 75;

while (!fileReader.EndOfStream)
{
    double completePercent = ((double)bytesRead / (double)fileInfo.Length);

    // I am using my own Progress Bar Dialog I left in here to show an example
    this.ProgressBar.UpdateProgressBar(completePercent);

    int readLength = bufferLength;

    if ((fileInfo.Length - bytesRead) < readLength)
    {
        // There is less in the file than the lenght I am going to read so change it to the 
        // smaller value
        readLength = (int)(fileInfo.Length - bytesRead);
    }

    char[] buffer = new char[readLength];

    // GEt the next chunk of the file
    bytesRead += (long)(fileReader.Read(buffer, 0, readLength));

    // This will help the file load much faster
    string currentLine = new string(buffer).Replace("\n", string.Empty);

    // Load in background
    this.Dispatcher.BeginInvoke(new Action(() =>
        {
            TextRange range = new TextRange(textBox.Document.ContentEnd, textBox.Document.ContentEnd);
            range.Text = currentLine;

        }), DispatcherPriority.Normal);
}
David Basarab
+1  A: 

I'm working on a very similar project.

The project entails loading a large text file (max size approx: 120MB but we want to go higher) and then constructing an outline of the text file in a tree. Clicking on a node in the tree will scroll the user to that portion of the text file.

After talking to a lot of people I think the best solution is to create a sort of "sliding window" viewer where you only load as much text as the user can see at a time into the rtb.Text.

So.. say load the entire file into a List but only put 100 of those lines into rtb.Text. If the user scrolls up remove the bottom line and add a line of text to the top. If they scroll down remove the top line and add a line of text to the bottom. I get pretty good performance with this solution. (50s to load a 120MB file)