2.7 統合されたUI要素
2.7.1 描画要素
これまでの Windows アプリケーション開発では、高度な描画機能を実装しようと考えた場合は、必ず同じ障害にたどり着きました。それは、デバイスコンテキストとコントロールがまったく異なるオブジェクトとして扱われていたことです。
古くは Win32 API における HWND 型のハンドルと HDC 型のハンドルにまでさかのぼります。.NET Framework 2.0 までの Windows Forms アプリケーションもまた、古い Win32 API の HWDN や HDC をラッピングしているだけの API だったため、設計上これらの問題は残されたままでした。
コントロールと描画処理はまったく異なる次元であったために、コントロールと描画のプロセスは、それぞれ異なるロジックで管理しなければなりませんでした。しかし、WPF では、コントロールも描画オブジェクトも、全ては System.Windows.UIElement クラスに統合されています。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement
public class UIElement : Visual, IAnimatable, IInputElement
コントロールであれ、図形であれ、最終的に視覚的な形でプレゼンテーションされることを目的とするオブジェクトは、UIElement クラスをルートクラスとして派生しています。よって、あらゆる視覚的なオブジェクトを UIElement 型として統合して操作することができます。
ContentControl クラスの Content プロパティには、文字列以外にこの UIElement 型のオブジェクトを設定することができました。よって、あらゆるプレゼンテーション可能なオブジェクトはコントロールのコンテンツとして視覚化することができます。ラベル、ボタン、チェックボックスなどは、任意の UIElement を表示することができますし、同時にそれ自身もまた UIElement であることから、別のコントロールのコンテンツになることもできます。
さらに、WPF では描画要素も全て UIElement として統合されています。矩形、楕円、線などは、直接コントロール上に描画されるのではなく、これらもまた UIElement オブジェクトとしてインスタンス化し、コンテンツとして設定することができます。これは、コントロールや図形などの描画要素を自由に組み合わせることができることを意味しています。
例えば、ゲームアプリケーションのようなものを開発しようとした場合、従来の API では、様々な描画要素を抽象するレイヤを表すクラスを作成し、レイヤの重ね合わせを管理しながら、独特な形のメッセージウィンドウや、キャラクターアイコン、感情アイコン、背景、演出などを組み合わせるという方法がありました。
開発シナリオにもよりますが、恐らく WPF では、そうした高度な描画の設計を最初から行う必要はないでしょう。演出や画面管理のロジックは自分で構築する必要がありますが、基本的な設計と描画関連のオブジェクトは WPF 基底のクラスを流用し、そのまま統合することができるため、より容易に高度なゲームアプリケーションを開発することができるはずです。
例えば、次のようなコードを実行してみましょう。各種コントロールや図形要素に関しては今後解説するので、この場では WPF を利用することでこうしたことが実現できるということだけを感じ取ってください。この場では、コードの内容を深く理解する必要はありません。
using System; using System.Windows; using System.Windows.Media; using System.Windows.Controls; using System.Windows.Shapes; class Test { [STAThread] public static void Main() { CheckBox checkBox = new CheckBox(); checkBox.Content = "巻きますか?"; checkBox.FontSize = 20; Ellipse ellipse = new Ellipse(); ellipse.Width = 200; ellipse.Height = 100; ellipse.Fill = Brushes.Red; Label label = new Label(); label.FontSize = 30; label.Content = "I'm never junk !"; StackPanel panel = new StackPanel(); panel.Children.Add(checkBox); panel.Children.Add(ellipse); panel.Children.Add(label); Button button = new Button(); button.Content = panel; Canvas canvas = new Canvas(); canvas.Children.Add(button); Window wnd = new Window(); wnd.Content = canvas; Application app = new Application(); app.Run(wnd); } }
コード1を実行すると、ウィンドウ上にボタンが 1 つ表示されます。このボタンは、内部のコンテンツにチェックボックス、赤い楕円、ラベルが含まれています。つまり、ボタンの中にさらに異なるコンテンツが含まれています。楕円のような図形要素とチェックボックスのようなコントロールも、すべて UIElement をルートとしているため、問題なくコンテンツとなることができます。
もちろん、レイアウトも自由です。文字列、イメージ、図形、コントロールなどを自由に組み合わせたり、重ね合わせることができ、従来の API のようなレイアウト上の制限というものがほとんどありません。こうした高い柔軟性は、全ての描画要素が UIElement に統合されているためです。