RichTextBox for UWP
スタイルのオーバーライド
RichTextBox の使い方 > コンテンツの設定と書式設定 > スタイルのオーバーライド

 C1RichTextBox ドキュメントに変更を適用する方法は2つあります。C1TextRange を使用して基底のドキュメントの各部を変更する方法と、基底のドキュメントではなくビューだけを変更する方法です。ドキュメントではなくビューを変更する例としては、選択範囲を異なる前景色と背景色で強調表示します。このスタイルの変更は、ドキュメント自体ではなく現在のビューに属します。この方法は、構文の色指定や入力中スペルチェックにも見られます。

実際の動作は、SyntaxHighlight サンプルで確認できます。このサンプルは、マシン にインストールされています。

C1RichTextBox コントロールは、StyleOverrides プロパティを使用してこのようなシナリオをサポートします。このプロパティには、ビューにのみ適用される範囲とスタイルの変更を指定するためのオブジェクトのコレクションが含まれます。この方法には、スタイルの変更を C1TextRange オブジェクトに適用する方法に比べて2つの利点があります。

この方法には、ドキュメントフローに影響するスタイル要素をスタイルの変更に入れることができないという制限があります。スタイルのオーバーライドを使用して、背景や前景を変更したり、ドキュメントの一部に下線を引くことができます。ただし、フォントのサイズやスタイルの変更は、ドキュメントフローに影響する可能性があるので実行できません。

 以下のコード例は、SyntaxHighlight サンプルからの抜粋です。

スタイルのオーバーライドを使用してみるには、まず、C1RangeStyleCollection オブジェクト、C1RichTextBox オブジェクト、および C1RichTextBoxMenu オブジェクトを宣言する必要があります。また、ドキュメントの色指定に使用するスタイルを初期化し、Page_Loaded イベントを作成します。このイベントで、C1RangeStyleCollection をコントロールの StyleOverrides コレクションに追加します。

C# コードの書き方

C#
コードのコピー
public sealed partial class SyntaxHighlight : UserControl
    {
        C1RichTextBox _rtb;
        C1RichTextBoxMenu _menu;
        C1RangeStyleCollection _rangeStyles = new C1RangeStyleCollection();

        // HTML の解析に使用される正規表現を初期化します
        string tagPattern =
            @"</?(?<tagName>[a-zA-Z0-9_:\-]+)" +
            @"(\s+(?<attName>[a-zA-Z0-9_:\-]+)(?<attValue>(=""[^""]+"")?))*\s*/?>";

        // ドキュメントの色指定に使用されるスタイルを初期化します
        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);
            }
        }

次に、TextChanged イベントを設定します。このイベントでは、ドキュメントの変更箇所を検出して、UpdateSyntaxColoring メソッドをトリガします。

C# コードの書き方

C#
コードのコピー
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));
        }

UpdateSyntaxColoring メソッドは、1つのタグ全体を選択し、それに色指定することで、書式設定をビューに適用します。

C# コードの書き方

C#
コードのコピー
    // 構文を色指定します
            void UpdateSyntaxColoring(C1TextRange range)
            {
                // 以前の色指定を削除します
                _rangeStyles.RemoveRange(range);
   
                var input = range.Text;
   
                // 一致箇所を強調表示します           
                foreach (Match m in Regex.Matches(input, tagPattern))
                {
                    // タグ全体を選択して濃い青色にします
                    _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, m), brDarkBlue));
   
                    // タグ名を選択して濃い赤色にします
                    var tagName = m.Groups["tagName"];
                    _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, tagName), brDarkRed));
   
                    // 属性名を選択して明るい赤色にします
                    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));
                        }
                    }
                }
        }

最後の2つのメソッドは、オフセットに基づいて C1TextRange の開始と終了を取得し、サンプルに付属するリソースファイルを取得します。

C# コードの書き方

C#
コードのコピー
     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);
            }
   
            // ユーティリティ
            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();
                }
            }
        }
}

一般に、このトピックに示した方法は、変更を基底のドキュメントに直接適用するより数千倍高速な優れたパフォーマンスを備えています。