Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / WPF

Binding and styling text to a RichTextBox in WPF

Rate me:
Please Sign up or sign in to vote.
4.91/5 (7 votes)
20 Jan 2011CPOL2 min read 75.3K   7   9
Binding and styling text to a RichTextBox in WPF

The RichTextBox in WPF is a great tool for dealing with text that needs to be styled (such as syntax highlighting) but it's not so great when it comes to allowing us to bind and edit text on the fly.

I got a feature request from our product manager to change the colour of certain text within a TextBox to match a legacy application. It turns out it is not as straight forward as you might expect.

We have text in an SQL database like this:

this is a title:.\r\n\r\nThis is a normal line\r\n\r\nthis is another title:.\r\n

You can see that the titles end with “:.” so all we need to do is pull out these lines and make them (in my case) blue.

The problem is that this text is on an object that my previous TextBox was able to bind its Text property to with ease, but the RichTextBox has no text property as it displays a flow document. OK, I thought, I will just bind the text to the document as follows:

XML
<RichTextBox IsReadOnly="True" Document="{Binding ElementName=ViewType, 
	Path=SelectedItem.Tag />

This doesn't work because the Document property is not a Dependency Property and therefore cannot be bound to. So BindableRichTextBox here I come. I created a very simple control so that I could bind to the Document property. In doing this, I discovered that the Document property expects a FlowDocument as its content so I had to convert my text string to a flow document with the relevant parts highlighted. I used a converter to spit out the FlowDocument with the correctly formatted text on a per line basis.

C#
public object Convert(object value, Type targetType, 
	object parameter, System.Globalization.CultureInfo culture)
{
    FlowDocument doc = new FlowDocument();

    string s = value as string;
    if (s != null)
    {
        using (StringReader reader = new StringReader(s))
        {
            string newLine;
            while ((newLine = reader.ReadLine()) != null)
            {
                Paragraph paragraph = null;
                if (newLine.EndsWith(":."))
                {
                    paragraph = new Paragraph
				(new Run(newLine.Replace(":.", string.Empty)));
                    paragraph.Foreground = new SolidColorBrush(Colors.Blue);
                    paragraph.FontWeight = FontWeights.Bold;
                }
                else
                {
                    paragraph = new Paragraph(new Run(newLine));
                }

                doc.Blocks.Add(paragraph);
            }
        }
    }

    return doc;
}

This simply takes the string and reads it line by line. If we have the desired characters at the end of a line (“:.”), then we make the line blue and bold and remove the characters, otherwise we just add the text. Each line is added as a paragraph so to reduce the space between each one I added the following to the control declaration in the XAML:

XML
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0?/>
</Style>
</RichTextBox.Resources>

This simply removes the margin from each paragraph.

The final part of the puzzle is the control that allows binding. This is a simple case of adding a dependency property onto a new control that inherits from RichTextBox.

C#
public class BindableRichTextBox : RichTextBox
{
    public static readonly DependencyProperty DocumentProperty = 
		DependencyProperty.Register("Document", typeof(FlowDocument), 
		typeof(BindableRichTextBox), new FrameworkPropertyMetadata
		(null, new PropertyChangedCallback(OnDocumentChanged)));

    public new FlowDocument Document
    {
        get
        {
            return (FlowDocument)this.GetValue(DocumentProperty);
        }

        set
        {
            this.SetValue(DocumentProperty, value);
        }
    }

    public static void OnDocumentChanged(DependencyObject obj, 
		DependencyPropertyChangedEventArgs args)
    {
        RichTextBox rtb = (RichTextBox)obj;
        rtb.Document = (FlowDocument)args.NewValue;
    }
}

I hope you find a use for this – I simply need it written down somewhere so I don't forget!


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I am a developer currently working for a financial company in the UK with a focus on back end work using NServiceBus. Personally I also enjoy working with ASP.NET (MVC), WPF, ruby and investigating best practice using methods like TDD and bettering the quality of code.

Comments and Discussions

 
QuestionUpdating document property when text changed Pin
Member 158540603-Jan-23 22:42
Member 158540603-Jan-23 22:42 
Questiongreat article Pin
Host Jerry25-Nov-20 11:22
Host Jerry25-Nov-20 11:22 
QuestionDownload source Pin
Seuss3-Oct-17 8:09
Seuss3-Oct-17 8:09 
Where is the download source?

Thanks
Questionwhy datagrid in winform can binding rtf and view as richtextbox but wpf not? how to solove it? Pin
sting_lee8-Nov-15 18:11
sting_lee8-Nov-15 18:11 
QuestionGood example but how does two way binding work? Pin
peterxx24-Mar-12 3:49
peterxx24-Mar-12 3:49 
AnswerRe: Good example but how does two way binding work? Pin
jimenezmike1828-Mar-12 2:49
jimenezmike1828-Mar-12 2:49 
GeneralMy vote of 5 Pin
Steve Maier24-Jan-11 5:10
professionalSteve Maier24-Jan-11 5:10 
Generalthanks for sharing - have 5 Pin
Pranay Rana24-Jan-11 1:40
professionalPranay Rana24-Jan-11 1:40 
GeneralMy vote of 5 Pin
SledgeHammer0120-Jan-11 10:10
SledgeHammer0120-Jan-11 10:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.