しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

痛 IDE の作り方(2)

さて、今回は IWpfTextView の特徴である Adornment について書く予定です。

Visual Studio のテキストエディタは WPF で書かれていると何回も言ってますが、内部では 1 枚の Canvas にべた書きとかではなくてしっかりとレイヤー構造を持ってます。そして IWpfTextView にはそのレイヤーを独自で追加するメソッドがしっかりと存在します。これを使って背景画像を実現するわけです。

このレイヤーの実体は IAdornmentLayer インターフェースを実装したクラスですが、作成する方法は MEF で定義しておいてインジェクションしてもらうしかないようです。ちなみに Adornment という名前から分かるように装飾用です。

// 新しく AdornmentLayer を作成してもらう
[Export]
[Order]
[Name("Background Adornment Layer")]
private AdornmentLayerDefinition BackgroundAdornmentLayer { get; set; }

こんな感じでプロパティを定義します。Name 属性で指定した名前は後で必要になるのでしっかりと付けておいてください。Export は MEF の属性ですが、Order ってのはどうもレイヤーの表示順みたいです。空っぽのままだと一番下に表示されるっぽいですが、よく調べていません。

[Export]
[Order(After = "Caret")]
[Name("Foo")]
private AdornmentLayerDefinition Foo { get; set; }

Order 属性には上のように引数に Before と After が用意されていて、これである程度表示順を制御できるっぽいです。これはキャレットの後ろに表示してくれという設定です。

では、実際に IAdornmentLayer に対して操作を行うわけですが、インスタンスの取得は IWpfTextView#GetAdornmentLayer メソッドで行います。

// textView には IWpfTextView が入っている
// 定義した IAdornmentLayer を取得する
var adornmentLayer = textView.GetAdornmentLayer("Background Adornment Layer");
// 何かする!

引数には Name 属性で指定したものと同じ名前を指定しないと例外が投げられます。

そして取得した IAdornmentLayer に対して操作を行います、長かった!

IAdornmentLayer はイメージ的には Canvas のようなもので、複数のビジュアル要素を持つことができます。要素の追加は IAdornmentLayer#AddAdornment メソッドで行います。

// とりあえず Image を作成する
var image = new Image
{
    Stretch = Stretch.Fill,
    Source = new BitmapImage(new Uri(...))
}

// 左上から (100,100) の位置に表示する
Canvas.SetLeft(image, 100);
Canvas.SetTop(image, 100);

// レイヤーに要素を追加する
adornmentLayer.AddAdornment(AdornmentPositioningBehavior.OwnerControlled, null, null, image, null);

AdornmentPositioningBehavior#OwnerControlled の部分を変えることでテキストからの相対位置やビューボックスからの相対位置など変更できます。そして IAdornmentLayer の実体は Canvas のようなので、Canvas.SetLeft/SetTop で位置も指定できます。

テキストからの相対位置で指定が可能なので、入力されたテキストの一部を某えもんからのメールみたいに黒く塗りつぶしたり出来ますね。

これでテキストエディタの背景画像を変えることはできますね!テキストエディタ編は次回で終了の予定です。