WPF(Windows Presentation Foundation)は、.NET Framework 3.0 で登場した Windows デスクトップアプリケーション向けの UI フレームワークです。
現在から見ると、WPF は「XAML で画面を作るフレームワーク」と説明されることが多いですが、登場当時の意味はそれだけではありませんでした。WPF は、Windows アプリケーション開発でバラバラに使われていた複数の技術を、ひとつのプログラミングモデルにまとめようとした大きな転換点でした。
この記事では、WPF がなぜ必要とされたのかを次の 4 つの観点から整理します。
- 多様な API の統合
- XAML による関心の分離
- 最適化された描画モデル
- 複雑な UI プログラミングの簡略化
多様な API の統合
WPF 以前の Windows アプリケーション開発では、実現したい機能ごとに異なる API や技術を組み合わせる必要がありました。
たとえば、一般的な業務アプリケーションのようにボタン、テキストボックス、リストなどのコントロールを備えたウィンドウを作る場合は、主に Windows Forms を使いました。
Button button = new Button();
button.Text = "OK";
button.Left = 20;
button.Top = 20;
Windows Forms は扱いやすいフレームワークでしたが、基本的には従来の Windows コントロールを .NET から使いやすく包んだものでした。標準的なフォームアプリケーションには向いていましたが、柔軟なレイアウト、アニメーション、独自の見た目を持つ UI を作ろうとすると、途端に難しくなります。
一方、2D グラフィックスを描画する場合は GDI+、つまり System.Drawing.dll を使いました。
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawEllipse(Pens.Blue, 10, 10, 100, 50);
}
3D グラフィックスが必要であれば DirectX API を使う必要がありました。動画を再生する場合は Windows Media Player API などを利用します。さらに、文書の表示や印刷を扱う場合は、PDF ファイルを外部コンポーネントで操作したり、独自のドキュメント表示機能を実装したりすることもありました。
つまり、WPF 以前は次のように役割ごとに技術が分かれていました。
| 目的 | 主に使われていた技術 |
|---|---|
| コントロールを備えたウィンドウの作成 | Windows Forms |
| 2D グラフィックス | GDI+ / System.Drawing.dll |
| 3D グラフィックス | DirectX API |
| ストリーミングビデオ | Windows Media Player API |
| フロースタイルのドキュメント | PDF 関連 API や専用コンポーネント |
これらを組み合わせれば多くのことは実現できました。しかし、開発者はそれぞれの API の考え方、描画モデル、座標系、イベント処理、リソース管理を個別に理解する必要がありました。
WPF は、この分断を解消するために設計されました。
.NET 3.0 以降では、WPF の中で次のような機能を同じ枠組みで扱えるようになりました。
- 通常のボタンやテキストボックスなどの UI コントロール
- 図形、線、塗りつぶし、ブラシなどの 2D グラフィックス
Viewport3Dを使った 3D 表示MediaElementを使った動画や音声の再生FlowDocumentやFixedDocumentによるドキュメント表示- アニメーション、データバインディング、スタイル、テンプレート
WPF の重要な点は、これらが別々の世界ではなく、同じ UI ツリーの中に配置できることです。
たとえば、ボタンの背景にグラデーションを使ったり、リストの項目に画像や図形を含めたり、画面の一部に動画を配置したりできます。従来であれば異なる API をまたいで実装していた表現を、WPF では統一されたオブジェクトモデルの中で扱えるようになりました。
なお、WPF は PDF ファイルそのものを編集するための専用 API ではありません。ここでいうドキュメントサポートとは、アプリケーション内でフロー型または固定レイアウト型の文書を表示・印刷・組版できる仕組みを提供した、という意味です。
XAML による関心の分離
WPF のもうひとつの大きな特徴が XAML(Extensible Application Markup Language)です。
Windows Forms でもデザイナーを使って画面を作ることはできましたが、最終的には InitializeComponent の中に C# コードとして UI 生成処理が書かれます。画面の構造、見た目、イベントの接続、初期化処理がコード側に寄りやすく、画面が複雑になるほど見通しが悪くなりがちでした。
WPF では、画面の構造を XAML で宣言的に記述できます。
<StackPanel Margin="20">
<TextBlock Text="ユーザー名" />
<TextBox Width="200" />
<Button Content="ログイン" Width="100" />
</StackPanel>
このように書くと、「どのコントロールを、どのような親子関係で配置するか」がコードではなくマークアップとして表現されます。
そして、ボタンが押されたときの処理や、データの取得、入力値の検証といったロジックは C# 側に書きます。
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
// ログイン処理
}
この分離によって、UI の見た目とアプリケーションの振る舞いを分けて考えやすくなりました。
さらに WPF では、XAML とデータバインディングを組み合わせることで、画面とデータの同期を手作業で書く量を大きく減らせます。
<TextBox Text="{Binding UserName}" />
このように書けば、TextBox の入力値と UserName プロパティを関連付けられます。WPF ではこの仕組みを土台にして、後に広く使われる MVVM(Model-View-ViewModel)パターンが発展していきました。
XAML の目的は、単に XML で画面を書けるようにすることではありません。UI の構造、見た目、データとの接続を宣言的に表現し、コードビハインドに処理が集中しすぎる問題を緩和することにあります。
最適化された描画モデル
Windows Forms や GDI+ では、基本的に「必要になったら描く」という考え方で描画します。たとえば OnPaint の中で Graphics オブジェクトを使って線や図形を描画します。
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawLine(Pens.Black, 0, 0, 100, 100);
}
この方式では、開発者が描画処理を手続き的に書きます。画面が再描画されるたびに、何をどの順番で描くかをコードで指定します。
WPF の描画モデルはこれとは大きく異なります。
WPF では、開発者は「画面に何を存在させるか」をオブジェクトとして定義します。たとえば、円を表示したい場合は Ellipse を配置します。
<Ellipse Width="100"
Height="60"
Fill="LightBlue"
Stroke="Navy" />
これは単なる描画命令ではなく、WPF のビジュアルツリー上に存在するオブジェクトです。サイズ、色、位置、変形、透明度などのプロパティを持ち、必要に応じて WPF が再描画します。
WPF の描画は内部的に DirectX を利用しており、ハードウェアアクセラレーションを前提に設計されています。そのため、透明度、拡大縮小、回転、アニメーション、合成といった表現を、従来よりも自然に扱えるようになりました。
また、WPF は解像度非依存の単位を使います。従来のピクセル単位の UI では、ディスプレイの DPI が変わると表示サイズや文字の見え方に影響が出やすくなります。WPF では 1 単位を 1/96 インチとして扱うため、高 DPI 環境でも UI をスケーリングしやすい設計になっています。
この描画モデルにより、WPF では次のような表現が扱いやすくなりました。
- コントロールの半透明表示
- 拡大・縮小・回転などの変形
- 滑らかなアニメーション
- ベクターグラフィックスによる拡大に強い描画
- 2D と 3D、動画、テキストの合成
WPF は「描画命令を書く」よりも「表示されるオブジェクトを定義し、そのプロパティを変化させる」方向に寄せたフレームワークだと言えます。
複雑な UI プログラミングの簡略化
WPF が目指したもうひとつの大きな目的は、複雑な UI を作るためのコード量を減らすことです。
Windows Forms では、見た目を細かく変更したい場合、コントロールを継承したり、独自描画を実装したりすることがよくありました。たとえば、ボタンの見た目を大きく変えるだけでも、描画処理やマウス状態の管理を自分で書く必要が出てきます。
WPF では、コントロールの機能と見た目が分離されています。
ボタンは「クリックできる」という機能を持ちますが、その見た目はテンプレートやスタイルで差し替えられます。
<Button Content="保存"
Background="SteelBlue"
Foreground="White"
Padding="16,8" />
単純な見た目の変更であれば、プロパティを設定するだけで済みます。さらに本格的に見た目を変えたい場合は、Style や ControlTemplate を使って、コントロールの外観を定義できます。
また、WPF にはレイアウトを自動化するためのパネルが用意されています。
| パネル | 役割 |
|---|---|
StackPanel |
縦または横に要素を並べる |
Grid |
行と列で画面を分割する |
DockPanel |
上下左右に要素を固定する |
WrapPanel |
折り返しながら要素を並べる |
Canvas |
座標を指定して配置する |
Windows Forms でもレイアウト機能はありますが、複雑な画面では座標やサイズを手作業で調整する場面が多くなりがちでした。WPF ではレイアウトシステムが強力で、ウィンドウサイズの変更、文字列の長さ、DPI、ローカライズなどに対応しやすくなっています。
さらに、WPF には UI プログラミングを支える機能がまとめて用意されています。
- データバインディング
- コマンド
- リソース
- スタイル
- テンプレート
- アニメーション
- 依存関係プロパティ
- ルーティングイベント
これらは最初は少し難しく見えます。しかし、それぞれは複雑な UI を保守しやすくするために導入された仕組みです。
たとえば、メニュー項目、ツールバーのボタン、ショートカットキーから同じ「保存」処理を呼び出したい場合、WPF では コマンド を使って処理を共通化できます。
また、同じ見た目を複数のボタンに適用したい場合は、スタイル をリソースとして定義できます。
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Padding" Value="16,8" />
<Setter Property="Margin" Value="4" />
</Style>
</Window.Resources>
このように WPF は、画面が大きくなったときに問題になりやすい重複、状態管理、見た目のばらつき、イベント処理の散らばりを抑えるための仕組みを持っています。
まとめ
WPF が登場した背景には、Windows アプリケーション開発の複雑化がありました。
WPF 以前は、ウィンドウやコントロールは Windows Forms、2D 描画は GDI+、3D は DirectX、動画は Windows Media Player API、文書表示は PDF 関連 API や専用コンポーネントというように、目的ごとに異なる技術を使い分ける必要がありました。
WPF はそれらを、XAML、ビジュアルツリー、データバインディング、スタイル、テンプレート、DirectX ベースの描画モデルという形で統合しました。
もちろん、WPF がすべての問題を自動的に解決するわけではありません。XAML、依存関係プロパティ、ルーティングイベント、MVVM など、学ぶべき概念も多くあります。
しかし、WPF は単なる Windows Forms の後継ではなく、リッチなデスクトップ UI を統一的に作るために設計されたフレームワークです。
次回以降は、WPF の基本構造である XAML、コントロール、レイアウト、イベント、データバインディングなどを順に見ていきます。