Silverlight 4 RichTextBox Control Extensions

I was recently building a text editor with the new Silverlight RichTextBox control and came across a scenario I’m sure many of us have hit: the need to provide a toolbar that lets a user format the text in the control. As I started building the toolbar I found the syntax of the RichTextBox API a bit cumbersome for doing basic, mundane tasks such as bolding text.

As a result, I built myself a little RichTextBoxExtensions class that’s forming the base of a new Silverlight control extensions library I plan to re-use across projects. (I thought extension methods were a good fit for a situation like this. Creating a derived control just to provide this functionality seemed like overkill.)

I found these extensions immensely useful when creating my toolbar. For example, when the bold button is clicked, I can call richTextBox.ToggleBold(). Likewise, whenever the selection (cursor position) in the text box changes, I can call methods like richTextBox.IsSelectionBolded() or richTextBox.GetSelectionFontFamily() to update the controls in my toolbar.

Here’s the code:

public static class RichTextBoxExtensions
{
    public static FontFamily GetSelectionFontFamily(this RichTextBox tb)
    {
        return tb.Selection.GetPropertyValue(Run.FontFamilyProperty) as FontFamily;
    }

    public static double GetSelectionFontSize(this RichTextBox tb)
    {
        return (tb.Selection.GetPropertyValue(
            Run.FontSizeProperty) as double?) ?? tb.FontSize;
    }

    public static void InsertTextAtInsertionPoint(this RichTextBox tb, string text)
    {
        if (tb.Selection.Start.IsAtInsertionPosition)
        {
            tb.Selection.Insert(new Run() { Text = text });
        }
        else
        {
            throw new InvalidOperationException(
                "Text cannot be inserted because current selection is not an insertion point.");
        }
    }

    public static bool IsSelectionBolded(this RichTextBox tb)
    {
        return ((tb.Selection.GetPropertyValue(
            Run.FontWeightProperty) as FontWeight?) == FontWeights.Bold);
    }

    public static bool IsSelectionItalicized(this RichTextBox tb)
    {
        return ((tb.Selection.GetPropertyValue(
            Run.FontStyleProperty) as FontStyle?) == FontStyles.Italic);
    }

    public static bool IsSelectionUnderlined(this RichTextBox tb)
    {
        return ((tb.Selection.GetPropertyValue(Run.TextDecorationsProperty)
            as TextDecorationCollection) == TextDecorations.Underline
        );
    }

    public static void SetSelectionFontFamily(this RichTextBox tb, FontFamily fontFamily)
    {
        tb.Selection.ApplyPropertyValue(Run.FontFamilyProperty, fontFamily);

        tb.Focus();
    }

    public static void SetSelectionFontSize(this RichTextBox tb, double sizeInPixels)
    {
        tb.Selection.ApplyPropertyValue(Run.FontSizeProperty, sizeInPixels);

        tb.Focus();
    }

    public static void ToggleBold(this RichTextBox tb)
    {
        if ((tb.Selection.GetPropertyValue(Run.FontWeightProperty)
            as FontWeight?) != FontWeights.Bold)
        {
            tb.Selection.ApplyPropertyValue(Run.FontWeightProperty, FontWeights.Bold);
        }
        else
        {
            tb.Selection.ApplyPropertyValue(Run.FontWeightProperty, FontWeights.Normal);
        }

        tb.Focus();
    }

    public static void ToggleItalic(this RichTextBox tb)
    {
        if ((tb.Selection.GetPropertyValue(Run.FontStyleProperty)
            as FontStyle?) != FontStyles.Italic)
        {
            tb.Selection.ApplyPropertyValue(Run.FontStyleProperty, FontStyles.Italic);
        }
        else
        {
            tb.Selection.ApplyPropertyValue(Run.FontStyleProperty, FontStyles.Normal);
        }

        tb.Focus();
    }

    public static void ToggleUnderline(this RichTextBox tb)
    {
        if ((tb.Selection.GetPropertyValue(Run.TextDecorationsProperty)
           as TextDecorationCollection) != TextDecorations.Underline)
        {
            tb.Selection.ApplyPropertyValue(
                Run.TextDecorationsProperty, TextDecorations.Underline);
        }
        else
        {
            tb.Selection.ApplyPropertyValue(Run.TextDecorationsProperty, null);
        }

        tb.Focus();
    }
}

It’s really a pretty simple class, but I’ve found it quite helpful so far, and it’s made me more productive by getting more done in less time.

The only thing that’s probably worth calling out are the calls to tb.Focus() that you’ll see in the methods I’d typically call from toolbar controls. The reason is after someone clicks the bold button in the toolbar, for example, I want the focus returned immediately to the last cursor position in the text box (similar to how Microsoft Word works). If you don’t need or want that, feel free to remove it or add a parameter to the method that makes it optional.