Home > C# > 目的別資料 > エディタ

AvalonEdit(WPFのテキスト・エディタ)を使用する

新規作成日 2018-05-17
最終更新日

原文:Using AvalonEdit (WPF Text Editor)

AvalonEditは、構文の強調表示と折り畳みをサポートする拡張可能なオープンソースのテキストエディタです。

AvalonEditの最新版は、SharpDevelopプロジェクトの一部として見つけることができます。

AvalonEditの詳細については、www.avalonedit.netを参照して下さい。

AvalonEditSample

概要

Introduction

ICSharpCode.AvalonEditは、私が、SharpDevelop 4.0のために記述した、WPFに基づいたテキスト・エディタです。それは、ICSharpCode.TextEditorの代わりとなることを示しています。しかし、以下のようである必要があります。:

  • 拡張可能
  • 簡単に使える
  • 大容量のファイルの処理に優れている

拡張可能なテキスト・エディタに、機能を追加できるSharpDevelopアドインが欲しかったことを示しています。例えば、アドインは、説明に画像を挿入できる必要があります。-このように、あなたは、ソース・コードに、右のクラス図のような物を配置することができます!

同時に、簡単に使用でき、私は、プログラミングAPIを参照しています。それは、実際に動作する必要があります。例えば、これは、あなたが、文書を変更する場合、エディタは、Invalidate()を呼ばずに、自動的に、再描画する必要があり、そして、あなたが、間違った何かを行う場合、あなたは、壊れた状態で後で無関係な位置でクラッシュするのではなく、意味がある例外を取得する必要があることを示しています。

エディタは、折り畳み(折りたたんでいるコード)のような機能が有効になっている場合でさえ、大きなファイルを処理できる必要があるため、大容量のファイル(例えば、mscorlib XMLドキュメント・ファイル、7MB、74100のLOC)を扱う方が良いことを示しています。

コードを使用する

Using the Code

エディタのメイン・クラスは、ICSharpCode.AvalonEdit.TextEditorです。あなたは、それを通常のWPFのTextBoxと似たように扱うことができます。:

<avalonEdit:TextEditor

    xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"

    Name="textEditor"

    FontFamily="Consolas"

    SyntaxHighlighting="C#"

    FontSize="10pt"/>

AvalonEditは、以下の組み込まれて定義された構文の強調表示を持っています。: ASP.NET、Boo、Coco / R文法、C ++、C#、HTML、Java、JavaScript、パッチファイル、PHP、TeX、VB、およびXML。

あなたが、あなたが、構文の強調表示を持つ単純なテキスト・ボックスと比べて、よりAvalonEditを必要とする場合、あなたは、まず、もっとAvalonEditの構造について、学ぶ必要があります。

構造

Architecture

AvalonEditの依存関係グラフ

この依存関係グラフでわかるように、AvalonEditは、きれいに切り離した仕事を持つ、いくつかのサブ-名前空間から構成されています。名前空間のほとんどは、一種の'main'クラスを持っています。

この依存関係グラフでわかるように、AvalonEditは、きれいに切り離した仕事を持つ、いくつかのサブ-名前空間から構成されています。名前空間のほとんどは、一種の'main'クラスを持っています。

  • ICSharpCode.AvalonEdit.Utils:さまざまなユーティリティ・クラス
  • ICSharpCode.AvalonEdit.Document: TextDocument テキスト・モデル
  • ICSharpCode.AvalonEdit.Rendering: TextView 文書上の拡張可能なView
  • ICSharpCode.AvalonEdit.Editing: TextAreaテキスト編集を制御する(例えば、キャレット、選択範囲、ユーザー入力を処理します)
  • ICSharpCode.AvalonEdit.Folding: FoldingManager コードの折りたたみを可能にする
  • ICSharpCode.AvalonEdit.Highlighting: HighlightingManager 強調表示エンジン
  • ICSharpCode.AvalonEdit.Highlighting.Xshd: HighlightingLoader XML構文の強調表示定義のサポート(.xshdファイル)
  • ICSharpCode.AvalonEdit.CodeCompletion: CompletionWindow コード補完機能のためのドロップダウン・リストを表示します
  • ICSharpCode.AvalonEdit: TextEditor メイン・コントロールは、全て一緒に、それを導入します。

ここに、TextEditorコントロールのビジュアル・ツリーがあります。

TextEditorコントロールのビジュアル・ツリー

AvalonEditが、3つの層を持つ複合のコントロールであることを理解することは、重要です。:TextEditor(メイン・コントロール)、TextArea(編集)、TextView(レンダリング)。メイン・コントロールは、ほとんどの高度な機能のための、共通のタスクのためのいくつかの便利なメソッドを提供しますが、あなたは、内部コントロールで、直接、作業する必要があります。あなたは、textEditor.TextAreaやtextEditor.TextArea.TextViewを用いて、アクセスすることができます。

Document(テキスト・モデル)

Document (The Text Model)

モデルのメイン・クラスは、ICSharpCode.AvalonEdit.Document.TextDocumentです。基本的に、文書は、イベントを持つStringBuilderです。しかしながら、また、Document名前空間には、テキストエディタを使用しているアプリケーションに役立つ、いくつかの機能が含まれています。

テキスト・エディタでは、3つの全てのコントロール(TextEditor、TextArea、TextView)は、TextDocumentインスタンスを指定するDocumentプロパティを持っています。あなたは、エディタを他の文書に結びつけるために、Documentプロパティを変更することができます。2つのエディタのインスタンスを、同じドキュメントに結合することが可能です。;あなたは、分割されたViewを作成するために、この機能を使用することができます。

ここに、TextDocumentの簡略化された定義があります。

public sealed class TextDocument : ITextSource
{
    public event EventHandler<DocumentChangeEventArgs> Changing;
    public event EventHandler<DocumentChangeEventArgs> Changed;
    public event EventHandler TextChanged;

    public IList<DocumentLine> Lines { get; }
    public DocumentLine GetLineByNumber(int number);
    public DocumentLine GetLineByOffset(int offset);
    public TextLocation GetLocation(int offset);
    public int GetOffset(int line, int column);

    public char GetCharAt(int offset);
    public string GetText(int offset, int length);

    public void Insert(int offset, string text);
    public void Remove(int offset, int length);
    public void Replace(int offset, int length, string text);

    public string Text { get; set; }
    public int LineCount { get; }
    public int TextLength { get; }
    public UndoStack UndoStack { get; }
}

AvalonEditでは、ドキュメントへのインデックスは、オフセットと呼ばれます。

オフセットは、通常、2つの文字の間の位置を表現します。ドキュメントの始点の最初のオフセットは、0です;ドキュメント内の最初のcharの後のオフセットは、1です。最後の有効なオフセットはdocument.TextLengthです。そして、文書の末端を表現します。これは、.NET StringやStringBuilderクラスのメソッドで使われる'index'パラメータと全く同じです。

オフセットは、使いやすいですが、ときどき、あなたは、その代わりにLine / Columnペアを必要とします。AvalonEditは、それらのためのTextLocationと呼ばれる構造体を定義します。

文書は、オフセットとTextLocationsの間で変換するために、メソッドGetLocationとGetOffsetを提供します。それらは、DocumentLineクラス上で構築される便利なメソッドです。

TextDocument.Linesコレクションには、文書内のすべての行のためのDocumentLineインスタンスが含まれています。このコレクションは、ユーザー・コードに対して読取専用です。そして、現在のドキュメントの内容を反映するために、自動的に更新されます。

レンダリング

Rendering

'Document'セクション全体では、拡張機能への言及は、ありませんでした。テキスト・レンダリングの基盤構造は、現在、完全な拡張可能性を補う必要があります。

ICSharpCode.AvalonEdit.Rendering.TextViewクラスは、AvalonEditの心臓部です。それは、文書を画面に表示する処理を行います。

これを拡張可能な方法で行うために、TextViewは、独自の種類のモデルを使用します。:VisualLine。目に見える線は、文書の見える部分だけに作成されます。

レンダリング工程は、次のようになります。:

AvalonEditのレンダリング工程

進行中の最後の手順は、1つ以上への転換です。

System.Windows.Media.TextFormatting.TextLineはインスタンスを作成します。 WPFは、続いて、実際のテキスト・レンダリングを処理します。

「要素ジェネレーター」、「線形変成器」と「背景レンダラー」は、拡張ポイントです。; それらのカスタム実装をTextViewに追加して、エディタに追加機能を実装することができます。

編集

Editing

TextAreaクラスは、ユーザー入力を処理して、適切な動作を実行します。 挿入記号と選択範囲は、TextAreaによって制御されます。

あなたは、TextArea.DefaultInputHandlerを修正することによって、新しいものを追加することによって、あるいは、その中で、既存のWPFの入力結合を置き換えることで、テキストの領域をカスタマイズすることができます。あなたは、同様に、他のモードにテキストの領域を切り換えるために、TextArea.ActiveInputHandlerをデフォルト以外の別の何かに設定することができます。あなたは、「インクリメンタルサーチ」機能やVIエミュレータを実装するために、これを使用できます。

折り畳み

Folding

折り畳み(コードの折り畳み)は、エディタへの拡張機能として実装されています。それは、AvalonEditコードを修正する必要がない、別々のアセンブリで実装することができます。VisualLineElementGeneratorは、テキスト文書の折り畳まれたセクションを処理します。そして、 カスタム・マージンは、正負ボタンを描画します。

あなたは、別々に関連したクラスを使用できます。;しかし、それを少し使いやすくするために、静的FoldingManager.Installメソッドは、自動的に必要な部分を作成して、登録します。

あなたのために残されているのは、あなたが提供したい折り畳みのリストでFoldingManager.UpdateFoldingsを定期的に呼び出すことです。あなたは、自分で、そのリストを計算することができます。あるいは、あなたは、組み込みの折りたたみ戦略を使って、それを実行することができます。

ここに、折り畳みを有効にするために必要とされる完全なコードがあります。:

foldingManager = FoldingManager.Install(textEditor.TextArea);
foldingStrategy = new XmlFoldingStrategy();
foldingStrategy.UpdateFoldings(foldingManager, textEditor.Document);

あなたが、折り畳みの目印を更新したい場合、テキストが変更される時、あなたは、定期的にfoldingStrategy.UpdateFoldings呼び出しを繰り返す必要があります。

現在、AvalonEditには、XmlFoldingStrategyのみが組み込まれています。また、この記事のサンプルアプリケーションには、{と}を使用して、折り畳むBraceFoldingStrategyも含まれています。しかしながら、それは極めて単純な実装であるため、文字列やコメントの内部で{と}を正しく処理しません。

構文の強調表示

Syntax Highlighting

AvalonEditの強調表示エンジンは、クラスDocumentHighlighterで実装されています。強調表示は、色を行の別のセクションに割り当てることによって、DocumentLineを取得して、それに対するHighlightedLineインスタンスを構築するプロセスです。

HighlightingColorizerクラスは、強調表示とレンダリングの間の唯一のリンクです。それは、ライン・トランスレータを実装するDocumentHighlighterを使用します。それは、レンダリング工程の外観の行の強調表示に適用されます。

この一回の呼び出しを除いて、構文の強調表示は、rendering名前空間とは独立しています。強調表示エンジンの他の潜在的な使用法の役に立てるために、HighlightedLineクラスは、構文強調されたHTMLソース・コードを作成するために、メソッドToHtmlを持っています。

強調表示のための規則は、「拡張可能な構文の強調表示定義」(.xshd)ファイルを使用して定義されます。ここに、C#のサブセットのための完全な強調表示定義があります。

<SyntaxDefinition name="C#"

        xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
    <Color name="Comment" foreground="Green" />
    <Color name="String" foreground="Blue" />
    
    <!-- This is the main ruleset. -->
    <RuleSet>
        <Span color="Comment" begin="//" />
        <Span color="Comment" multiline="true" 

           begin="/\*" end="\*/" />
        
        <Span color="String">
            <Begin>"</Begin>
            <End>"</End>
            <RuleSet>
                <!-- nested span for escape sequences -->
                <Span begin="\\" end="." />
            </RuleSet>
        </Span>
        
        <Keywords fontWeight="bold" foreground="Blue">
            <Word>if</Word>
            <Word>else</Word>
            <!-- ... -->
        </Keywords>
        
        <!-- Digits -->
        <Rule foreground="DarkBlue">
            \b0[xX][0-9a-fA-F]+  # hex number
        |    \b
            (    \d+(\.[0-9]+)?   #number with optional floating point
            |    \.[0-9]+         #or just starting with floating point
            )
            ([eE][+-]?[0-9]+)? # optional exponent
        </Rule>
    </RuleSet>
</SyntaxDefinition>

強調表示エンジンは、それぞれに割り当てられた色を持つ"spans"と"rules"で機能します。XSHDフォーマットでは、色は、(color="Comment")あるいは、直接指定した(fontWeight="bold" foreground="Blue")の両方を参照することができます。

Spanは、2つの正規表現(begin+end)から構成されています。;規則は、単純に、色の付いた一つの正規表現です。<Keywords>要素は、単語の組み合わせと一致する強調規則を定義するための素晴らしい構文です。;内部的に、一つの正規表現は、キーワードリスト全体で使用されます。

強調表示エンジンは、最初にspansを分析することで動作します。:begin正規表現が、いくつかのテキストに一致するたびに、そのspanは、スタックにプッシュされます。現在のspanのend正規表現が、いくつかのテキストに一致するたびに、spanは、スタックからポップされます。

各々のspanは、既定では、空に関連付けられた入れ子になった規則設定を持っています。そういうわけで、キーワードは、コメントの中では強調されません。:spanの空のルール・セットは、そこでは、キーワード規則は、適用さないため、アクティブです。

この機能は、同様に、文字列spanでも使用されます。:入れ子になったspanは、入れ子になったspanの末端の正規表現によって、バックスラッシュに遭遇し、そして、バックスラッシュの後の文字が、利用されるとき、一致するでしょう。(.は、どんな文字にでも一致します)。これは、\"が、文字列のspanの末端を示さないことを確実にします。;しかし、\\"は、依然としてそうです。

強調表示エンジンの素晴らしい点は、オンデマンドでのみ強調し、段階的に動作し、そして、まだ 通常、大型のコード・ファイルでさえ、数KBのメモリだけを必要とします。

オンデマンドは、文書が開かれるとき、最初に表示された行だけが、強調表示されることを示しています。ユーザーが、下にスクロールするとき、強調表示は、最後に停止した場所から継続します。ユーザーが、速くスクロールする場合、そのため、最初に表示される行は、最後に強調された行のはるか下にあります。続いて、強調表示エンジンは、まだ、その間、処理するために、すべての行を持っています。-そこから、コメントが始まる場合があります。しかしながら、それは、spanスタック内で、変更するために、その領域を走査するだけです。;強調表示規則は、検証されません。

アクティブなスパンのスタックは、すべての行の開始時先頭に格納されます。ユーザーが、スクロールを元に戻す場合、見える行は、必要なコンテキスト(spanスタック)が、まだ、利用できるため、すぐに、強調することができます。

段階的に、文書が変更される場合でも、格納されたスパン・スタックは、できるだけ再利用されることを示しています。ユーザーが、/*を入力する場合、理論的には、全体のファイルの残りが、コメントの色で強調表示されます。しかしながら、エンジンが、要求に応じて動作するため、それは、現在表示される領域の中で、spanスタックだけを更新します。そして、'強調表示の状態が、行 Xと行 X+1の間で一貫していません'という通知を維持します。Xは、見える領域の最終行の場所です。次に、ユーザーが、下にスクロールする場合、強調表示の状態は更新されます。そして、'一貫性のない'通知が下に移動されます。しかし、通常、ユーザーは、入力を続け、数行後に */ と入力します。次に、見える領域の強調表示の状態は、通常の'メイン・ルール・セットだけが、アクティブなスパンのスタック'に戻ります。

ユーザーが、現在、'一貫性のない'目印で行で下にスクロールするとき、エンジンは、古いスタックと新しいスタックが同一であることを警告します。そして、'一貫性がない'目印を削除します。これは、ユーザーが入力した/*の前の、キャッシュされた格納されたspanスタックを再利用できます。

アクティブなスパンのスタックが、行内で、頻繁に変更されますが、それは、めったに、1つの行の始まりから、次の行の始まりに、変更しません。ほとんどの言語では、そのような変更は、複数行コメントの始点と終端でのみ発生します。強調表示エンジンは、spanスタックのリストを特別なデータ構造に格納することによって、このプロパティを利用します。(ICSharpCode.AvalonEdit.Utils.CompressingTreeList)。強調表示エンジンのメモリー使用状況は、spanスタックの変更の数に比例します。;行の合計数ではなく。これは、強調表示エンジンが、少しのメモリだけを使用して、大きなコード・ファイルのためのspanスタックを格納できます。特に、C#のような言語では、//や///のシーケンスが、/* */コメントより人気があります。

コード補完機能

Code Completion

AvalonEditには、コード補完ドロップ・ダウン・ウィンドウが付属しています。あなたが、ウィンドウを表示したいとき、あなたは、決定するテキストの入力イベントだけを処理する必要があります。;すべてのUIは、既に、あなたのために行われています。

ここに、あなたが、それをどのように使用するかがあります。:

    // in the constructor:
    // コンストラクタでは、
    textEditor.TextArea.TextEntering += textEditor_TextArea_TextEntering;
    textEditor.TextArea.TextEntered += textEditor_TextArea_TextEntered;
}

CompletionWindow completionWindow;

void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
{
    if (e.Text == ".") {
        // Open code completion after the user has pressed dot:
        // コード補完機能を開いた後、ユーザーは、点を押しました。:
        completionWindow = new CompletionWindow(textEditor.TextArea);
        IList<ICompletionData> data = completionWindow.CompletionList.CompletionData;
        data.Add(new MyCompletionData("Item1"));
        data.Add(new MyCompletionData("Item2"));
        data.Add(new MyCompletionData("Item3"));
        completionWindow.Show();
        completionWindow.Closed += delegate {
            completionWindow = null;
        };
    }
}

void textEditor_TextArea_TextEntering(object sender, TextCompositionEventArgs e)
{
    if (e.Text.Length > 0 && completionWindow != null) {
        if (!char.IsLetterOrDigit(e.Text[0])) {
            // Whenever a non-letter is typed while the completion window is open,
            // 補完ウィンドウが開いている間、英字以外が入力されるする時はいつでも、
            // insert the currently selected element.
             // 現在、選択された要素を挿入します。
            completionWindow.CompletionList.RequestInsertion(e);
        }
    }
    // Do not set e.Handled=true.
    // e.Handled = trueを設定しないでください。
    // We still want to insert the character that was typed.
    // 私たちは、まだ、入力された文字を挿入することを望みます。
}

'.'が押されるたびに、このコードは、コード補完機能ウィンドウを開きます。既定では、CompletionWindowは、現在選択された項目を挿入するために、TabやEnterのような、キー入力だけを処理します。また、それを完全に作成するために、'.' や ';'のようなキーが押されるとき、私たちは、他のハンドラをTextEnteringイベントに添付します。そして、選択した項目を挿入するために、ウィンドウの補完を伝えます。

CompletionWindowには、実際には、フォーカスがありません。-その代わりに、それは、テキストの領域上で、WPFのキーボード入力イベントを乗っ取ります。そして、それらをListBoxに渡します。 これにより、キーボードを使用して補完リストの項目を選択できます。そして、エディタでの通常の入力と同時に行います。

完全を期すために、ここに、上記のコードで使用されている、MyCompletionDataクラスの実装があります。

/// Implements AvalonEdit ICompletionData interface to provide the entries in the
/// completion drop down.
/// 補完ドロップ・ダウンで項目を提供するために、AvalonEdit ICompletionDataインターフェイスを実装します。
public class MyCompletionData : ICompletionData
{
    public MyCompletionData(string text)
    {
        this.Text = text;
    }
    
    public System.Windows.Media.ImageSource Image {
        get { return null; }
    }
    
    public string Text { get; private set; }
    
    // Use this property if you want to show a fancy UIElement in the list.
    // あなたが、リストで、意匠を凝らしたUIElementを表示したい場合、このプロパティを使用します。
    public object Content {
        get { return this.Text; }
    }
    
    public object Description {
        get { return "Description for " + this.Text; }
    }
    
    public void Complete(TextArea textArea, ISegment completionSegment,
        EventArgs insertionRequestEventArgs)
    {
        textArea.Document.Replace(completionSegment, this.Text);
    }
}

表示れるコンテンツと説明の両方は、カスタムUIElementを含む、WPFで受け入れ可能なコンテンツである場合があります。あなたが、単純に、テキストを挿入したい場合、また、あなたは、Completeメソッドでカスタム・ロジックを実装するかもしれません。insertionRequestEventArgsは、ユーザーが、どの種類の挿入を望むか、決定するために、役に立てることができます。-TextCompositionEventArgs、KeyEventArgsやMouseEventArgsのインスタンスで、挿入がどのようにトリガされたかに応じて、

このエントリーをはてなブックマークに追加

Home PC C# Illustration

Copyright (C) 2011 Horio Kazuhiko(kukekko) All Rights Reserved.
kukekko@gmail.com
ご連絡の際は、お問い合わせページのURLの明記をお願いします。
「掲載内容は私自身の見解であり、所属する組織を代表するものではありません。」