Some tricks for working with WPF’s RichTextBox

I’ve been playing around with the RichTextBox, and as Richard points out, the free spell checking on WPF TextBoxes is really cool – especially for the “spelling challenged” such as myself.

 

The programming model for the WPF RichTextBox is quite a bit richer than the Windows Forms wrapper around the rich edit control – however it’s a bit more complex to work with.  I’m still working out all the details of how to work with it, but I thought I’d write down what I’ve figured out:

 

To get to your actual content, you’ve gotta work with a Document object.  This is the place where the actual text content can be manipulated.  From here you can get what’s currently selected through the RichTextBox.Document.Selection property, or access “blocks” of text through the RichTextBox.Document.Blocks property.

 

 

Adding content to your RichTextBox in XAML:

In XAML. this is really easy… just add paragraphs and insert your formatting as you go.

 

<RichTextBox IsSpellCheckEnabled="True">
<FlowDocument>
<Paragraph>
This is a richTextBox. I can 
<Bold>Bold</Bold><Italic>Italicize</Italic><Hyperlink>Hyperlink stuff</Hyperlink> right in my document.
</Paragraph>

</FlowDocument>
</RichTextBox>

Shrinking down the space between paragraphs:

Every time you have a newline in your text, this adds a new paragraph to your document.  By default, there is a margin separating out the paragraphs, so it feels as if there is a blank line between paragraphs (I guess this keeps with the “P” html tag versus the “BR”?)

 

<RichTextBox>
    
<FlowDocument>
        
<Paragraph>
            This is my first paragraph... see how there is...
        
</Paragraph>
        
<Paragraph>
            a huge amount of space between it and the second paragraph?
        
</Paragraph>
    
</FlowDocument>
</RichTextBox>

 

Anyways, in order to shrink down your spacing between paragraphs, you can make use of the styling features in WPF.  You can say, “hey whenever I see a paragraph created within this rich textbox, reset its margins to 0”.  The magic is done by a “Style” XAML tag, and by not specifying a particular key, and specifying a TargetType of Paragraph, all Paragraphs that are a child of the RichTextBox get the appropriate style.  For more info on styles, the styles chapter of Chris Sells and Ian Griffith’s book is online.

<RichTextBox>
    
<RichTextBox.Resources>
        
<Style TargetType="{x:Type Paragraph}">
            
<Setter Property="Margin" Value="0"/>
        
</Style>
    
</RichTextBox.Resources>
    
<FlowDocument>
        
<Paragraph>
            This is my first paragraph... see how there is...
        
</Paragraph>
        
<Paragraph>
            a no space anymore between it and the second paragraph?
        
</Paragraph>
    
</FlowDocument>
</RichTextBox>

So then the next question is how do I put plain text into the RichTextBox?    

 

Unless you want to replace the currently selected text (RichTextBox.Document.Selection.Text), you will need to access the RichTextBox.Document.Blocks collection to add more text to the textbox.

 

Here’s a quick code sample for replacing all text in a RichTextBox:

 

private void LoadTextFile(RichTextBox richTextBox, string filename)
{
    richTextBox.Document.Blocks.Clear();
    
using (StreamReader streamReader =
 File.OpenText(filename))
    
{
        Paragraph paragraph 
= new
 Paragraph();
        paragraph.Text 
=
 streamReader.ReadToEnd();
        richTextBox.Document.Blocks.Add(paragraph);
    }

}

 

How do I get Text back out of the RichTextBox?

To get the text back out of the RichTextBox, you need to use the TextRange class:

private string GetText(RichTextBox richTextBox)
{
    
// use a TextRange to fish out the Text from the Document

    TextRange textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
    
return
 textRange.Text;
}

How do I put RTF (rich text format) into a RichTextBox?

It turns out the support for this seems to be through the TextRange class.  TextRange offers a "Load" method which takes a string representing a data format (which you can pick from the DataFormats class) and a stream.  If you currently have a string of RTF formatting, you can stuff the string in a MemoryStream, then pass it to the TextRange.  Here's a handy helper function for it:

private static void LoadRTF(string rtf, RichTextBox richTextBox)
{
    
if (string
.IsNullOrEmpty(rtf))
    
{
        
throw new
 ArgumentNullException();
    }


    TextRange textRange 
= new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
    
    
//Create a MemoryStream of the Rtf content

    using (MemoryStream rtfMemoryStream = new MemoryStream())
    
{
        
using (StreamWriter rtfStreamWriter = new
 StreamWriter(rtfMemoryStream))
        
{
            rtfStreamWriter.Write(rtf);
            rtfStreamWriter.Flush();
            rtfMemoryStream.Seek(
0
, SeekOrigin.Begin);

            
//Load the MemoryStream into TextRange ranging from start to end of RichTextBox.

            textRange.Load(rtfMemoryStream, DataFormats.Rtf);
        }

    }

}

How do I load a file with "rich" content in it?


Here again, you can use the TextRange class to load, but you pass in a FileStream instead of a MemoryStream.  

 

private static void LoadFile(string filename, RichTextBox richTextBox)
{
    
if (string
.IsNullOrEmpty(filename))
    
{
        
throw new
 ArgumentNullException();
    }

    
if (!File.Exists(filename))
    
{
        
throw new
 FileNotFoundException();
    }


    
// open the file for reading
    using (FileStream stream = File.OpenRead(filename))
    
{
        
// create a TextRange around the entire document

        TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);

        
// sniff out what data format you've got

        string dataFormat = DataFormats.Text;
        
string ext =
 System.IO.Path.GetExtension(filename);
        
if (String.Compare(ext, ".xaml"true== 0
)
        
{
            dataFormat 
=
 DataFormats.Xaml;
        }

        
else if (String.Compare(ext, ".rtf"true== 0)
        
{
            dataFormat 
=
 DataFormats.Rtf;
        }

        documentTextRange.Load(stream, dataFormat);
    }

}

How do I Save a file with "rich" content in it?
TextRange also has a Save!  Here's the code.

private static void SaveFile(string filename, RichTextBox richTextBox)
{
    
if (string
.IsNullOrEmpty(filename))
    
...
{
        
throw new
 ArgumentNullException();
    }



    
// open the file for reading
    using (FileStream stream = File.OpenWrite(filename))
    
{
        
// create a TextRange around the entire document

        TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);

        
// sniff out what data format you've got

        string dataFormat = DataFormats.Text;
        
string ext =
 System.IO.Path.GetExtension(filename);
        
if (String.Compare(ext, ".xaml"true== 0
)
        
{
            dataFormat 
=
 DataFormats.Xaml;
        }

        
else if (String.Compare(ext, ".rtf"true== 0)
        
{
            dataFormat 
=
 DataFormats.Rtf;
        }

        documentTextRange.Save(stream, dataFormat);
    }

}

Putting it all together - a very simple editor

Replacing the contents of the Window with this DockPanel, adding the SaveFile, LoadFile helper methods and the event handlers for open and save, we can get a very simple rich text editor.

<!-- Window1.xaml -->
  
<DockPanel>
    
<Menu DockPanel.Dock="Top">
      
<MenuItem Header="_File">
        
<MenuItem Header="_Open File" Click="OnOpenFile"/>
        
<MenuItem Header="_Save" Click="OnSaveFile"/>
        
<Separator/>
        
<MenuItem Header="E_xit" Click="OnExit"/>
      
</MenuItem>      
    
</Menu>

    
<RichTextBox Name="richTextBox1"></RichTextBox>     
  
</DockPanel>

 

// Window1.xaml.cs
private void OnExit(object sender, EventArgs e)
{
    
this
.Close();
}

private void OnOpenFile(object sender, EventArgs e)
{
    Microsoft.Win32.OpenFileDialog ofd 
= new
 Microsoft.Win32.OpenFileDialog();
    ofd.Filter 
= "Text Files (*.txt; *.xaml; *.rtf)|*.txt;*.xaml;*.rtf"
;
    ofd.Multiselect 
= false
;
    
if (ofd.ShowDialog() == true
)
    
{
        LoadFile(ofd.SafeFileName, richTextBox1);
    }


}

private void OnSaveFile(object sender, EventArgs e)
{
    Microsoft.Win32.SaveFileDialog sfd 
= new
 Microsoft.Win32.SaveFileDialog();
    sfd.Filter 
= "Text Files (*.txt; *.xaml; *.rtf)|*.txt;*.xaml;*.rtf"
;
    
if (sfd.ShowDialog() == true
)
    
{
        SaveFile(sfd.SafeFileName, richTextBox1);
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章