Working with WPF RichTextBox / Overriding Styles
Overriding Styles

The Syntax Coloring section described how you can use C1TextRange objects to modify the style of parts of a document without moving the selection. In some cases, however, you may want to modify only the view, and not the document itself.

For example, the current selection is highlighted with different foreground and background colors. This style change does not belong to the document itself; it belongs to the view. Other examples are syntax coloring and as-you-type spell-checking.

The C1RichTextBox control supports these scenarios with the StyleOverrides property. This property contains a collection of objects that specify ranges and style modifications to be applied to the view only. This approach has two advantages over applying style modifications to C1TextRange objects as you did in the previous section:

The limitation of this approach is that the style changes cannot involve style elements that affect the document flow. You can use style overrides to change the background, foreground, and to underline parts of the document. But you cannot change the font size or style, for example, since that would affect the document flow.

In the following section learn how to implement Overriding Styles in RichTextBox .NET and .NET Framework versions.

Let us demonstrate the use of style overrides by modifying the previous syntax coloring example.

First, we need to declare a C1RangeStyleCollection object and add that to the control's StyleOverrides collection. Once that is done, any overrides added to our collection will be applied to the control. We will later populate the collection with the syntax-colored parts of the document.

Visual Basic
Copy Code
Private _rangeStyles As New C1RangeStyleCollection()
Public Sub New()
    InitializeComponent()
    _rtb = New C1RichTextBox()
    LayoutRoot.Children.Add(_rtb)
    AddHandler _rtb.TextChanged, AddressOf tb_TextChanged
    _rtb.FontFamily = New FontFamily("Courier New")
    _rtb.FontSize = 16
    _rtb.Text = GetStringResource("w3c.htm")
    ' Add our C1RangeStyleCollection to the control's
    ' StyleOverrides collection
    _rtb.StyleOverrides.Add(_rangeStyles)
End Sub
C#
Copy Code
C1RangeStyleCollection _rangeStyles = new C1RangeStyleCollection();
public MainPage()
{
  InitializeComponent();
  _rtb = new C1RichTextBox();
  LayoutRoot.Children.Add(_rtb);
  _rtb.TextChanged += tb_TextChanged;
  _rtb.FontFamily = new FontFamily("Courier New");
  _rtb.FontSize = 16;
  _rtb.Text = GetStringResource("w3c.htm");
  // Add our C1RangeStyleCollection to the control's
  // StyleOverrides collection
  _rtb.StyleOverrides.Add(_rangeStyles);
}

Now, all we need to do is modify the UpdateSyntaxColoring method shown earlier and have it populate our collection of range styles (instead of applying the coloring to the document as we did before):

Visual Basic
Copy Code
' Perform syntax coloring using StyleOverrides collection
' (takes a fraction of a second to highlight the default document)
Private  Sub UpdateSyntaxColoring(ByVal rtb As C1RichTextBox)
  ' Initialize regular expression used to parse HTML
  String pattern =
    "</?(?<tagName>[a-zA-Z0-9_:\-]+)" +
    "(\s+(?<attName>[a-zA-Z0-9_:\-]+)" +
    (?<attValue>(\s*=\s*""(^"")+"")?))*\s*/?>"
  ' Initialize styles used to color the document
  Dim key As var =  C1TextElement.ForegroundProperty
  Dim brDarkBlue As var =  New C1TextElementStyle()
  brDarkBlue(key) = New SolidColorBrush(Color.FromArgb(255, 0, 0, 180))
  Dim brDarkRed As var =  New C1TextElementStyle()
  brDarkRed(key)  = New SolidColorBrush(Color.FromArgb(255, 180, 0, 0))
  Dim brLightRed As var =  New C1TextElementStyle()
  brLightRed(key) = New SolidColorBrush(Colors.Red)
  ' Remove old coloring
  _rangeStyles.Clear()
  ' Highlight the matches
  Dim input As var =  rtb.Text
  Dim m As Match
  For Each m In Regex.Matches(input,pattern)
    ' Select whole tag, make it dark blue
    Dim range As var =  rtb.GetTextRange(m.Index,m.Length)
    _rangeStyles.Add(New C1RangeStyle(range,brDarkBlue))
    ' Select tag name, make it dark red
    Dim tagName As var =  m.Groups("tagName")
    range = rtb.GetTextRange(tagName.Index, tagName.Length)
    _rangeStyles.Add(New C1RangeStyle(range,brDarkRed))
    ' Select attribute names, make them light red
    Dim attGroup As var =  m.Groups("attName")
    If Not attGroup Is Nothing Then
      Dim att As Capture
      For Each att In attGroup.Captures
        range = rtb.GetTextRange(att.Index, att.Length)
        _rangeStyles.Add(New C1RangeStyle(range,brLightRed))
      Next
    End If
  Next
End Sub
C#
Copy Code
// Perform syntax coloring using StyleOverrides collection
// (takes a fraction of a second to highlight the default document)
void UpdateSyntaxColoring(C1RichTextBox rtb)
{
  // Initialize regular expression used to parse HTML
  string pattern =
    @"</?(?<tagName>[a-zA-Z0-9_:\-]+)" +
    @"(\s+(?<attName>[a-zA-Z0-9_:\-]+)" +
    (?<attValue>(\s*=\s*""[^""]+"")?))*\s*/?>";
  // Initialize styles used to color the document
  var key = C1TextElement.ForegroundProperty;
  var brDarkBlue  = new C1TextElementStyle();
  brDarkBlue[key] = new SolidColorBrush(Color.FromArgb(255, 0, 0, 180));
  var brDarkRed   = new C1TextElementStyle();
  brDarkRed[key]  = new SolidColorBrush(Color.FromArgb(255, 180, 0, 0));
  var brLightRed  = new C1TextElementStyle();
  brLightRed[key] = new SolidColorBrush(Colors.Red);
  // Remove old coloring
  _rangeStyles.Clear();
  // Highlight the matches
  var input = rtb.Text;
  foreach (Match m in Regex.Matches(input, pattern))
  {
    // Select whole tag, make it dark blue
    var range = rtb.GetTextRange(m.Index, m.Length);
    _rangeStyles.Add(new C1RangeStyle(range, brDarkBlue));
    // Select tag name, make it dark red
    var tagName = m.Groups["tagName"];
    range = rtb.GetTextRange(tagName.Index, tagName.Length);
    _rangeStyles.Add(new C1RangeStyle(range, brDarkRed));
    // Select attribute names, make them light red
    var attGroup = m.Groups["attName"];
    if (attGroup != null)
    {
      foreach (Capture att in attGroup.Captures)
      {
        range = rtb.GetTextRange(att.Index, att.Length);
        _rangeStyles.Add(new C1RangeStyle(range, brLightRed));
      }
    }
  }
}

The revised code is very similar to the original. Instead of creating brushes to color the document, it creates C1TextElementStyle objects that contain an override for the foreground property. The code starts by clearing the override collection, then uses a regular expression to locate each HTML tag in the document, and finally populates the overrides collection with C1RangeStyle objects that associate ranges with C1TextElementStyle objects.

If you run this new version of the code, you should notice the dramatic performance increase. The new version is thousands of times faster than the original.