RichTextBox for UWP | ComponentOne
Working with RichTextBox for UWP / Setting and Formatting Content / Overriding Styles
In This Topic
    Overriding Styles
    In This Topic

    There are two different ways you can apply changes to your C1RichTextBox document. You can modify parts of an underlying document using C1TextRange, or you can modify only the view and not the underlying document. A good example of modifying the view and not the document is highlighting a selection with different foreground and background colors. The style change doesn't belong to the document itself; it belongs to the current view. You can also see this in syntax coloring and as-you-type spell-checking.

    You can see this behavior in action in the SyntaxHighlight sample, installed on your machine.

    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:

    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.

     The code examples in the following text are taken from the SyntaxHighlight sample.

    In demonstrating the use of style overrides, first, we need to declare a C1RangeStyleCollection object, a C1RichTextBox object, and a C1RichTextBoxMenu object. We'll also initialize the styles used to color the document, and create a Page_Loaded event. Within that event, we will add the C1RangeStyleCollection to the control's StyleOverrides collection.

    C#
    Copy Code
    public sealed partial class SyntaxHighlight : UserControl
                    {
                        C1RichTextBox _rtb;
                        C1RichTextBoxMenu _menu;
                        C1RangeStyleCollection _rangeStyles = new C1RangeStyleCollection();
            // initialize regular expression used to parse HTML
                        string tagPattern =
                            @"</?(?<tagName>[a-zA-Z0-9_:\-]+)" +
                            @"(\s+(?<attName>[a-zA-Z0-9_:\-]+)(?<attValue>(=""[^""]+"")?))*\s*/?>";
            // initialize styles used to color the document
                        C1TextElementStyle brDarkBlue = new C1TextElementStyle
                        {
                            { C1TextElement.ForegroundProperty, new SolidColorBrush(Color.FromArgb(255, 0, 0, 180)) }
                        };
                        C1TextElementStyle brDarkRed = new C1TextElementStyle
                        {
                            { C1TextElement.ForegroundProperty, new SolidColorBrush(Color.FromArgb(255, 180, 0, 0)) }
                        };
                        C1TextElementStyle brLightRed = new C1TextElementStyle
                        {
                            { C1TextElement.ForegroundProperty, new SolidColorBrush(Colors.Red) }
                        };
            public SyntaxHighlight()
                        {
                            InitializeComponent();
                Loaded += SyntaxHighlight_Loaded;
                        }
            void SyntaxHighlight_Loaded(object sender, RoutedEventArgs e)
                        {
                            if (_rtb == null)
                            {
                                _rtb = new C1RichTextBox
                                {
                                    ReturnMode = ReturnMode.SoftLineBreak,
                                    TextWrapping = TextWrapping.NoWrap,
                                    IsReadOnly = false,
                                    Document = new C1Document
                                    {
                                        Background = new SolidColorBrush(Colors.White),
                                        FontFamily = new FontFamily("Courier New"),
                                        FontSize = 16,
                                        Blocks =
                                    {
                                        new C1Paragraph
                                        {
                                            Children =
                                            {
                                                new C1Run
                                                {
                                                    Text = GetStringResource("w3c.htm")
                                                },
                                            },
                                        }
                                    }
                                    },
                                    StyleOverrides = { _rangeStyles }
                                };
                                if (_menu == null)
                                {
                                    _menu = new C1RichTextBoxMenu();
                                }
                    LayoutRoot.Children.Add(_rtb);
                                _menu.RichTextBox = _rtb;
                                LayoutRoot.Children.Add(_menu);
                    _rtb.TextChanged += tb_TextChanged;
                                UpdateSyntaxColoring(_rtb.Document.ContentRange);
                            }
                        }
    

    Next, we'll set the TextChanged event. In this event, you will detect any changes in the document and trigger the UpdateSyntaxColoring method.

    C#
    Copy Code
    void tb_TextChanged(object sender, C1TextChangedEventArgs e)
            {
                var start = e.Range.Start.Enumerate(LogicalDirection.Backward)
                                         .FirstOrDefault(p => p.Symbol.Equals('\n'));
                if (start != null)
                {
                    start = start.GetPositionAtOffset(1);
                }
                var end = e.Range.End.Enumerate().FirstOrDefault(p => p.Symbol.Equals('\n'));
                var doc = e.Range.Start.Element.Root;
                UpdateSyntaxColoring(new C1TextRange(start ?? doc.ContentStart, end ?? doc.ContentEnd));
            }
    

    The UpdateSyntaxColoring method applies formatting to the view by selecting an entire tag and coloring it.

    C#
    Copy Code
     // perform syntax coloring
                 void UpdateSyntaxColoring(C1TextRange range)
                 {
                     // remove old coloring
                     _rangeStyles.RemoveRange(range);
       
                     var input = range.Text;
       
                     // highlight the matches          
                     foreach (Match m in Regex.Matches(input, tagPattern))
                     {
                         // select whole tag, make it dark blue
                         _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, m), brDarkBlue));
       
                         // select tag name, make it dark red
                         var tagName = m.Groups["tagName"];
                         _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, tagName), brDarkRed));
       
                         // select attribute names, make them light red
                         var attGroup = m.Groups["attName"];
                         if (attGroup != null)
                         {
                             var atts = attGroup.Captures;
                             for (int i = 0; i < atts.Count; i++)
                             {
                                 var att = atts[i];
                                 _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, att), brLightRed));
                             }
                         }
                     }
             }
    

    The last two methods get the start and end of the C1TextRange with an offset, and then get the resource file attached to the sample:

    C#
    Copy Code
    C1TextRange GetRangeAtTextOffset(C1TextPointer pos, Capture capture)
                 {
                     var start = pos.GetPositionAtOffset(capture.Index, C1TextRange.TextTagFilter);
                     var end = start.GetPositionAtOffset(capture.Length, C1TextRange.TextTagFilter);
                     return new C1TextRange(start, end);
                 }
       
                 // utility
                 static string GetStringResource(string resourceName)
                 {
                     Assembly asm = typeof(SyntaxHighlight).GetTypeInfo().Assembly;
                     Stream stream = asm.GetManifestResourceStream(String.Format("RichTextBoxSamples.Resources.{0}", resourceName));
                     using (var sr = new StreamReader(stream))
                     {
                         return sr.ReadToEnd();
                     }
                 }
             }
     }
    

    In general, the method outlined in this topic will provide excellent performance, thousands of times faster than applying changes directly to the underlying document.