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.