MVVM: Data Binding Rich Text to the Infragistics XamRichTextEditor

Brian Lagunas / Thursday, May 14, 2015

The Infragistics xamRichTextEditor control is a highly customizable rich text editing control that provides functionality modeled after the features and behavior of Microsoft Word.  You can easily create and edit Microsoft Word documents using the xamRichTextEditor.  Here’s the thing though… not every app that uses rich text uses Word, or even deals with a document at all.  Sometimes you just have a string stored in a database somewhere that holds all the rich text information as RTF or even HTML.  So, if you’re using MVVM, and populating a property with this string of rich text data, how do you data bind it to the xamRichTextEditor control?  Easy!  Use a document adapter.

Binding to Visual Elements

Let’s say that I have a xamRichTextEditor control and I want to data bind the rich text being generated by the control to another element in my view.  Let’s say a TextBlock.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    Grid.ColumnDefinitions>
    
    <ig:XamRichTextEditor x:Name="_rte" Grid.Column="0" />
    
    <TextBlock Grid.Column="1" />
Grid>

Your first thought might be to data bind directly to a property of the xamRichTextEditor.  Well, you would be wrong.  We actually need to use a “middle man” called a document adapter.  Since the xamRichTextEditor supports PlainText, HTML, and RTF formats, you’ll want to choose which format you need.  Heck, you may want to support all of them.  That’s fine, no problem.  Either way, you need to add a reference to the document format you will be using.  I will be using HTML in this post.

xamRichTextEditor document formats

Once you have added the formats you need, we need can create a document adapter in XAML.  In this case, I’ll be using the HtmlDocumentAdapter.

      
      <ig:HtmlDocumentAdapter x:Name="_html" Document="{Binding ElementName=_rte, Path=Document}" />

As you can see, when I defined the HtmlDocumentAdapter, I data bound the Document property to the xamRichTextEditor.Document property.  This is how you make the connection between the two controls.  Now the next step is to data bind the Text property of the TextBlock in our sample to the HtmlDocumentAdapter so that we can visualize the HTML being generated as we create rich text in the xamRichTextEditor.

      
      <TextBlock Grid.Column="1" Text="{Binding ElementName=_html, Path=Value}" />

That’s it!  We are now data bound.  Run the application and let’s see what we get.

image

Perfect!  The HTML generated by the xamRichTextEditor is data bound and being rendered by the TextBlock control.  If you started typing into the xamRichTextEditor, you will notice that the HTML isn’t updated as you type.  This is because by default, the source doesn’t update until the control has lost focus.  Now, you may think, “oh, I’ll just use the UpdateSourceTrigger on the binding to have it update on any key stroke”.  Well, once again you, would be wrong!  You actually have to use a property that exists on the document adapter called RefreshTrigger.

image

You will notice four options; Delayed, ContentChanged, Explicit, and LostFocus.  Half of those are self-explanatory.  ContentChanged is like property changed.  It will update the Value every time the content in the xamRichTextEditor is updated.  For very large documents, this could cause some performance issues.  When using Delayed, you have two additional properties to help control the behavior of the update; DelayAfterFirstEdit and DelayAfterLastEdit.

DelayAfterFirstEdit is a timespan that allows you to define how long to wait after you first start typing in the xamRichTextEditor to update the binding.

DelayAfterLastEdit is a time span that allows you to define how long to wait after you stop typing in the xamRichTextEditor to update the binding.

You can even use them together if you like.


<ig:HtmlDocumentAdapter x:Name="_html" Document="{Binding ElementName=_rte, Path=Document}"
                        RefreshTrigger="Delayed"
                        DelayAfterFirstEdit="00:00:02:00"
                        DelayAfterLastEdit="00:00:02:00" />

Here is our final XAML to create the data binding between the xamRichTextEditor, and the TextBlock.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    Grid.ColumnDefinitions>
    
    <ig:HtmlDocumentAdapter x:Name="_html" Document="{Binding ElementName=_rte, Path=Document}"
                            RefreshTrigger="Delayed"
                            DelayAfterFirstEdit="00:00:02:00"
                            DelayAfterLastEdit="00:00:02:00" />
    
    <ig:XamRichTextEditor x:Name="_rte" Grid.Column="0" />
    
    <TextBlock Grid.Column="1" Text="{Binding ElementName=_html, Path=Value}" />
Grid>

 

Binding to a Property in a ViewModel

So what if we want to data bind to a property in our ViewModel.  We are using MVVM after all!  Well that would require just a slight modification.  Let’s say I have a ViewModel that looks like this:

public class MainWindowViewModel : INotifyPropertyChanged
{
    private string _htmlText;
    public string HtmlText
    {
        get { return _htmlText; }
        set
        {
            _htmlText = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Well, with a small modification to our XAML, we can create a binding between the xamRichTextEditor, and the property on underlying ViewModel.

<ig:HtmlDocumentAdapter x:Name="_html" Document="{Binding ElementName=_rte, Path=Document}"
                        RefreshTrigger="Delayed"
                        DelayAfterFirstEdit="00:00:02:00"
                        DelayAfterLastEdit="00:00:02:00"
                        Value="{Binding HtmlText}"/>

Assuming your View’s DataContext is properly set, like mine is; this would now update your property depending on your RefreshTrigger settings.  You can now serialize this property any way you like.

Be sure to check out the source code, and start playing with it.  As always, feel free contact me on my blog, connect with me on Twitter (@brianlagunas), or leave a comment below for any questions or comments you may have.