views:

2590

answers:

2

Hello, I am currently trying to figure out how to save the content from a RichTextbox into a stream(currently using FileStream) and do this alongside a bunch of other data. Then of course I want to be able to load from this file. I was currently trying to use something along the following lines.

FileStream stream = new FileStream(); //this is actually correctly defined.
ASCIIEncoding encoding = new ASCIIEncoding();

//write Title
byte[] array = encoding.GetBytes(Title);
stream.WriteByte(Convert.ToByte(array.Length));
stream.Write(array, 0, array.Length);

//save textRange
textRange.Save(stream, System.Windows.DataFormats.Rtf);

//write Subtitle
byte[] array = encoding.GetBytes(Subtitle);
stream.WriteByte(Convert.ToByte(array.Length));
stream.Write(array, 0, array.Length);
//ect...and something very similar for Loading a file.

This is basically what I am trying to do. I am actually saving 2 TextRanges and a bunch more Properties. So my problem is that TextRange.Load() reads to the end of the file...making it impossible for me to use that considering I have 2 TextRanges I need to save/load. So here I am trying to come up with another way to be able to save/load the content of a RichTextBox with other data. I dont have to use a Stream. I am pretty much open to any feasible solutions. Thanks in advance!

~Jasson

A: 

You could load/save to a MemoryStream to solve your issue with reading to the end of the file. It could look like this:

  • Load your file into memory
  • Load the section of that file that is the contents of the richtextbox into a MemoryStream
  • Load the richtextbox contents from that MemoryStream

Or are you wanting to know how you'd create and parse a file to contain different sections for the title, and the contents, and any other fields ?

Chris
How could I tell when how long the contents of the RichTextBox are? Would I write into the file some flag byte(s) right after writing the contents of the RichTextBox. So then I could tell how when the contents actually end and use the byte[] before the flag as the MemoryStream? That's my best guess.
Jasson
You don't need a flag. Since you are saving the various text ranges as RTF, all RTF docs start with '{\rtf' and end with a '}'. (See RTF Spec 1.9.1.) Of course, this means parsing your stream, but you were going to do that anyway to find your Title, Subtitle, etc., based on your code above.
Ants
I got it working. I didn't parse it looking for '{\rtf' and '}' however I'll look into that in the near future as it might be a better/safer way about doing it than what I am currently doing. Thanks for the help guys. I really appreciate it! :D
Jasson
A: 

I figured I should post my current solution. It seems to work perfectly fine. Thank you Chris and Ants to the hints on how to go about doing this.

/// <summary>
    /// Reads a TextRange (DataFormats.Rtf) from the stream.
    /// </summary>
    /// <param name="stream">The stream to be read from.</param>
    /// <returns>The TextRange (DataFormats.Rtf) that was read from the stream.</returns>
    public static TextRange ReadTextRange(FileStream stream)
    {
        long startPos = stream.Position;
        int length = -1;
        int count = 0;
        int previousByte = 0;
        int currentByte = 0;
        //set previousByte to give the current one something to compare to
        previousByte = stream.ReadByte();
        //parse the file counting the { and } to find the end of the rtf portion of the file.
        while (count > 0 || length < 1)
        {
            length++;
            stream.Position = startPos + length;
            currentByte = stream.ReadByte();
            if (previousByte != 92) // not '\' so check to see if '{' or '}' is currentByte
            {
                if (currentByte == 123) // '{' increase count
                    count++;
                else if (currentByte == 125) // '}' decrease count
                    count--;
            }
            previousByte = currentByte;
        }
        //save finish position to move to later
        long finishPos = stream.Position;
        //reset stream position to start at beginning of rtf
        stream.Position = startPos;
        //read the rtf portion of the file into a byte[]
        byte[] content = new byte[length];
        stream.Read(content, 0, length);
        //put the byte[] into a memory stream
        MemoryStream memStream = new MemoryStream(content);
        FlowDocument doc = new FlowDocument();
        TextRange range = new TextRange(doc.ContentStart, doc.ContentEnd);
        //have the TextRange read from the memorystream
        range.Load(memStream, System.Windows.DataFormats.Rtf);
        memStream.Close();
        //set the position to after the rtf portion of the file
        stream.Position = finishPos;
        return range;
    }

This ReadTextRange Method is in a StreamHelper class I defined for helping read from a FileStream. So all of this is to load a TextRange that is saved to the FileStream like this...

//save query (TextRange)
        Query.Save(stream, System.Windows.DataFormats.Rtf);

I hope that someone finds this useful if/when they come to a similar problem! :D

EDIT:

I used a profiler and found that this code was not very efficient so I have changed this code to be much more efficient in a few ways.

  1. Instead of using the TextRange and use a byte[] which holds the contents of MemoryStream memStream. This cuts out range.Load which consumes a lot of CPU.

  2. I took out the line stream.Position = startPos + length because I realized it was useless after the first run and also took up a decent amount of CPU. I placed stream.Position--; after the line previousByte = stream.ReadByte();

Also I realized I was being a bad coder and wasn't following MVC by having TextRange, UI element, inside of my data class. Now it has a byte[], which is MUCH better.

EDIT AGAIN:

After a few minutes of having the byte[] instead of the TextRange I realized I had the size of the byte[] so I didn't need to parse it. So instead I save write the byte[] size and then the byte[]. This makes it extremely fast and can read a very large file nearly instantly.

Jasson