こんにちは、情熱開発部プログラム課の廣江です!
9月も半ばに入り、もうそろそろ涼しくなってくるでしょうか。
私は暑いのが苦手なので、早く涼しくならないかなと思いながら日々過ごしています。
さて、今回はWPFのOnRenderを使用して図形の描画を試してみようと思います。
業務でWPFを触り始めてしばらく経ちましたが、OnRender周りはあまり触ったことがなかったので、これを機会に色々試してみよう!ということでこちらのテーマにしました。
OnRenderとは
OnRenderはコントロールの描画に関することを自前で実装できるメソッドです。
メソッドの定義はUIElementにあります。
protected virtual void OnRender(DrawingContext drawingContext);
UIElementにはデフォルトの実装はなく、各派生クラスで実装する必要があります。
また、OnRender内ではDrawingContextに対して描画情報を設定する処理を実装します。
描画に関してはOnRender内で実行されるのではなく、遅延実行されるのが特徴です。
OnRenderはどういった場面で使用するべきか?
OnRender を実装すると、コントロールのレイアウトにかかる時間を削減できるため、パフォーマンスの向上が期待できます。
しかし、すべてのコントロールに対して OnRender を実装することには多大なコストがかかり、また、XAMLでの見た目の調整ができなくなるというデメリットもあります。
そのため、OnRender の使用は、実装コストとパフォーマンスの費用対効果を慎重に検討する必要があります。
一つ具体例を挙げます。
例えば、高頻度で更新されるテキストを表示する場合、TextBlock を使用するとテキストが更新されるたびにレイアウト処理が発生し、UIの動作が重くなります。
しかし、これを OnRender で実装すれば、レイアウト処理を回避できるため、パフォーマンスの改善が見込めます。
一方で、更新頻度の低いテキストを表示する場合は、TextBlock を使用してもレイアウト処理がほとんど発生しないため、OnRender をわざわざ実装する必要はありません。
このようにOnRenderを実装することで、大きくパフォーマンスの向上が期待できる状況で使用していくことをお勧めします。
OnRenderを実装してみる
それではOnRenderメソッドの実装について説明していきます。
まずはOnRenderを実装するためのカスタムコントロールを作成する必要があります。
カスタムコントロール
WPFでカスタムコントロールを作成する場合は、WPFに実装されているコントロールクラスや、その基底クラスを派生して作成します。
基底クラスの選択肢は様々で、用途によって適切に選択することでより効率的に実装することが可能です。
改めてですが、今回の目的はOnRenderを実装することです。
TextBoxやButtonなどのコントロールクラスから派生しても実装はできますが、これらのクラスには当然ですが不要な機能が多分に含まれています。
ドキュメントによるとOnRenderを実装する場合は、FrameworkElementから派生するのがよさそうでしたので、今回はこのクラスから派生して作成します。
public class SimpleRectangle : FrameworkElement
{
public SimpleRectangle()
{
}
}
OnRenderメソッド
次はOnRenderをオーバーライドして実装します。
まずはOnRenderの実装例を見てみましょう。
protected override void OnRender(DrawingContext drawingContext)
{
var pen = new Pen(Brushes.Blue, 10);
var rect = new Rect(10, 10, 500, 500);
drawingContext.DrawRectangle(Brushes.LimeGreen, pen, rect);
}
こちらはRectangle(矩形)を描画する例です。
とてもシンプルですね。
それでは一つずつ説明していきます。
Penは図形のアウトラインの描画を指定するためのオブジェクトです。
今回は青色で太さ10のアウトラインを設定しています。
Rectを使用して図形の描画位置、大きさを指定しています。
左から、「X座標・Y座標・幅・高さ」です。
最後にDrawingContextに対して描画情報を設定します。
DrawRectangleは図形の色と先ほど作成したPenとRectを指定します。
以上でOnRenderの実装は完了です。
後はDrawingContextに設定した描画情報をAPIがうまい具合に処理して描画してくれます。
こちらを使用すると以下のような図形が描画されます。
もう少し実践的な実装に向けて
ここまでは図形を表示する実装まで説明してきました。
しかし、実際に使用する場合は、図形を表示するだけということは殆どないと思います。
ここからは図形の回転と描画情報の更新について説明していきます。
図形の回転
WPFではTransformを使用することで、コントロールの移動、回転、拡縮などを設定できます。
OnRenderの場合はDrawingContextにPushTransformというメソッドがあるのでこれを使用します。
drawingContext.PushTransform(new RotateTransform(45, 250, 250));
RotateTransformはTransformの派生クラスで、回転行列を簡単に作成できます。
引数で指定しているのは、左から回転角度、回転の中心座標X、Yです。
コントロールの原点は左上です。
コントロールの中心を起点に回転させたい場合は、回転の中心座標を指定する必要があります。
以下は自作ツールで図形の中心を起点に45度回転させたものです。
描画情報の更新
OnRenderは描画情報を更新する必要がある際に実行されます。
何もしなければ一度しかOnRenderは実行されません。
では、いつ描画情報が更新されるのか?
それはFrameworkPropertyMetadataOptions.AffectsRenderを指定している依存関係プロパティが変更されたタイミングです。
依存関係プロパティはWPFプロパティシステムでサポートされているプロパティのことを指します。
XAMLでバインドするTextBlock.Text、CheckBox.IsCheckedなどのプロパティがこれに当たります。
話がそれるので詳細な説明は省きますが、興味がある方はドキュメントの方をご覧ください。
参考: 依存関係プロパティの概要 (WPF .NET)
FrameworkPropertyMetadataOptions.AffectsRenderはプロパティが描画に影響を与えるかを指定できるオプションです。
これを指定しているプロパティが変更された際に、描画情報が更新されます。
以下はRotationAngleというプロパティにFrameworkPropertyMetadataOptions.AffectsRenderを指定して、表示を更新するようにしたものです。
最後に
今回はWPFのOnRender関係について紹介させていただきました。
最初の方でも触れましたが、OnRenderは実装コストとパフォーマンスの費用対効果を考慮して使用する必要があります。
また、WPFのレイアウト、レンダリング周りについて理解することでより効果的にOnRenderを実装できると思います。
このあたり興味ある方は是非調べてみてください。
参考
【免責事項】
本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。