Open棟梁Project - マイクロソフト系技術情報 Wiki

目次

概要

WPFの機能

サポートする機能

グラフィック

メディア

コントロール

レイアウト

パネルと呼ばれるレイアウト用コントロールを使用して、Windowsフォームのような座標レイアウト、HTMLのようなフロー レイアウト(ブロック化、格子分割)にも対応する。

アニメーション

イベント トリガ、トリガ アクション、ストーリ ボードを定義することで、UIコントロールなどの表示項目にタイムライン ベースのアニメーションの効果を付加できる。

デプロイ

ClickOnceXBAPなどのデプロイが可能。

相互運用性

P/InvokeやRCW呼出しなどWin32連携技術を使用した相互運用が可能である。また、WindowsFormsHost?、Hwndhost を使用することで、WindowsフォームやActiveXのUIコントロールを使用することも可能である(パフォーマンス的には不利)。

例えば、下記は、System.Windows.Forms名前空間のDataGrid?コントロールをホストするWPFの例である。

<Window x:Class="WpfApplication1.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"  
  Title="HostingWfInWpf" Height="150" Width="300" Loaded="Window_Loaded">
  <Canvas>
    <WindowsFormsHost Name="windowsFormsHost1" Height="100" Width="275">
      <wf:DataGrid x:Name="dataGrid1"/>
    </WindowsFormsHost>
  </Canvas>
</Window>

機能の制限事項

主に3Dグラフィックの高度な機能に関する制限であるので、リッチでインタラクティブな業務アプリケーションの開発に、大きな支障は無いと言える。

項番区分説明
パフォーマンスDirect3Dと比べるとオーバーヘッドがあり、GPU負荷が高くなる。また、WPFはDirect3D 9対応であり、Direct3D 10対応のグラフィックボードであってもパフォーマンスは向上しない。
シェーダWPFは内部的にシェーダを使用しているが、開発者がWPFからシェーダを使用することはできない。
スキニングWPFではスキニングができないので、滑らかな関節を持つアニメーションはできない(剛体アニメーションのみサポート)。
環境マッピングWPFでは環境マッピングが使用できないので、物体に周囲の景色が反射しているような効果は表現できない。
WPFではステンシル バッファ(3Dグラフィックにおいて、物体の重ね合わせなど、描画しなくても良い領域を効率よく判定するためのバッファ領域)や、深度バッファを使用できないためリアルな影を生成できない。
テクスチャ・フィルタリングポリゴンのテクスチャを構成するピクセルにテクセル(3Dグラフィックにおいて、テクスチャを構成する一つ一つのピクセル)が一対一に対応することはほとんどなく、
ポリゴンにテクスチャをマッピングする際には、テクスチャを拡大/縮小(変形)するフィルタが必要となるが、WPFから、このフィルタを指定することはできない。

注意事項

WPFアーキテクチャ

クラス階層

System.Object

System.Object

http://msdn.microsoft.com/ja-jp/library/system.object.aspx

WPFのプログラミング モデルを提供するフレームワークは、CLR上のマネージ コンポーネント(PresentationFramework?.dll、PresentationCore?.dll)として公開される。System.Objectは、マネージ コンポーネントが提供する全てのマネージ クラスの基本クラスであり、型階層のルートである。

マネージ コンポーネントには、開発の生産性と信頼性を高める多数の機能(メモリ管理、エラー処理、共通型システムなど)が用意されているが、性能など犠牲となるものもある。これに対し、アンマネージ コンポーネント(milcore.dll)はDirectXとの緊密な統合の実現(ハードウェア レンダリングおよびソフトウェア レンダリングの効率性を考え、WPF での表示はすべて DirectX エンジンによって実行されるようになっている。)、メモリ・実行の詳細な制御の要件による。

System.Threading.DispatcherObject?

http://msdn.microsoft.com/ja-jp/library/system.windows.threading.dispatcherobject.aspx

DispatcherObject?は、派生したCLRオブジェクトに、STAオブジェクトとしての動作を実装する基本抽象クラスである。

通常、WPFアプリケーションは、

の2つのスレッドを使用して実行される。

「レンダリング スレッド」は、「UIスレッド」がユーザ入力を受け取り、イベント処理・UI処理をしている間に、バック グラウンドで実行される。このため、ほとんどのWPFアプリケーションは、単一の「UIスレッド」で済むが、状況によっては、応答性を高める目的でバック グラウンド スレッドを使用する場合もある。WPFでは、バック グラウンド処理の実装を支援するDispatcherObject?を使用できる。

DispatcherObject?.Invoke、BeginInvoke?メソッドを使用することにより、バック グラウンド処理からの「UI変更処理」(= UI要素の操作処理)が、容易に実装可能となる。

これらのメソッドは、内部的には

これにより、

System.Windows.DependencyObject?

http://msdn.microsoft.com/ja-jp/library/system.windows.dependencyobject.aspx

DependencyObject?は、派生したCLRオブジェクトに「WPFプロパティ システム」を実装する。

WPFのアーキテクチャの理念は、「メソッドやイベント(コードビハインド)よりも、なるべくXAMLのマークアップによる宣言的プロパティを使用する」ことである。これを実現する「WPFプロパティ システム」を実装するDependencyObject?により、開発者は多数の宣言的プロパティを使用し、コントロールに開発者の意図を設定できる。このためWPFの開発元は、このXAMLのマークアップによる宣言的プロパティによる制御の範囲を拡大するために、「WPFプロパティ システム」の「依存関係プロパティ」が必要であると判断した。「依存関係プロパティ」は、プロパティの依存関係を把握し、双方向の接続と変更通知を実現する。具体的には、ソースとなるオブジェクトのプロパティの変更が通知された場合(必須ではないが、INotifyPropertyChanged?インターフェイスを使用すると、オブジェクトによる変更通知の発行が可能になる。)、ターゲットとなるオブジェクトのプロパティ値を自動的に検証、計算するなど、高度なオブジェクト プロパティ間の接続を実現する。

「依存関係プロパティ」の入力には、次のものがある。

System.Windows.Media.Visual

http://msdn.microsoft.com/ja-jp/library/system.windows.media.visual.aspx

Visualは、WPF の中心的な機能である描画をサポートする基本抽象クラスである。

Visualは、マネージ コンポーネントとアンマネージ コンポーネントの2つのサブシステムの接続ポイントであり、Visualで定義されているマネージ データ(描画情報、描画方法など)から、「ビジュアル ツリー」(後述)と呼ばれるツリー構造のアンマネージ データを構成する。これを「レンダリング スレッド」がツリー構造の上から下にスキャンすることによって描画内容をレンダリングする。

  1. Children
  2. OpacityMask?
  3. Opacity
  4. BitmapEffect?
  5. ClipGeometry?
  6. GuidelineSet?
  7. Transform

System.Windows.UIElement

http://msdn.microsoft.com/ja-jp/library/system.windows.uielement.aspx

UIElementは、WPFコア レベル実装の基本クラス

System.Windows.FrameworkElement?

http://msdn.microsoft.com/ja-jp/library/system.windows.frameworkelement.aspx

UIElementに、「レイアウト」、「スタイル」を中心とした機能を追加する基本クラス。

System.Windows.Controls.Control

http://msdn.microsoft.com/ja-jp/library/system.windows.controls.control.aspx

「テンプレート」を使用して外観を定義する UI 要素の基本クラス

Controlの最も重要な機能は、「テンプレート」である。この「テンプレート」により、コントロールの「外観」を宣言型マークアップでカスタマイズ可能になる(「テンプレート」を複数の子要素から構成する)。また、「イベント ハンドラ」や「イベント トリガ」などもこの「テンプレート」により定義可能で、「テンプレート」を「スタイル」化することで、任意の型のコントロールに、これらの「テンプレート」の定義の適用を強制できる。

System.Windows.Controls.ContentControl?

http://msdn.microsoft.com/ja-jp/library/system.windows.controls.contentcontrol.aspx

ContentControl? は、Contentプロパティを持つコントロールであるContentControl?も、Contentプロパティの表示に特化した「テンプレート」を持つ。Contentプロパティには、文字列に限らず、様々な子要素を1つだけ設定可能である。代表的なContentControl?型の(ContentControl?クラスから派生した)コントロールには、Button、CheckBox?RadioButton?などがある。なお、プロパティへ子要素を設定するXAML構文を、

と呼び、この中で、特にContentプロパティに子要素を設定するXAML構文を「コンテンツ構文」と呼ぶ。

System.Windows.Controls.ItemsControl?

ItemsControl?は、1つのコンテンツを設定するContentControl?に対し、複数のコンテンツを設定できるItemsコレクション プロパティを持つコントロールである。ItemsControl?も、Itemsコレクション プロパティの表示に特化した「テンプレート」を持つ。Itemsコレクション プロパティには、様々な子要素を複数設定可能である。代表的なItemsControl?型の(ItemsControl?クラスから派生した)コントロールには、ComboBox?ListBox?TabControl?TreeView?などがある。なお、同様にItemsコレクション プロパティに子要素を設定するXAML構文を「コンテンツ構文」と呼ぶ。

要素ツリー

WPFは、ビジュアル要素や、CLRオブジェクトによる「要素ツリー」を構築して、それを処理することでディスプレイへの表示を行う。

「要素ツリー」は、XAMLやプログラムにより、構築される。

例えばXAMLは、次の「プロパティ要素構文」の暗黙的、または明示的な記述で、ツリー構造のCLRオブジェクトをインスタンス化する。

<DockPanel>
  <!--implicit: <DockPanel.Children>--> → プロパティ要素構文(省略可能)
  <ListBox DockPanel.Dock="Top">
    <!--implicit: <ListBox.Items>--> → プロパティ要素構文(省略可能)
    <ListBoxItem>
      <TextBlock>Dog</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Cat</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Fish</TextBlock>
    </ListBoxItem>
    <!--implicit: </ListBox.Items>--> → プロパティ要素構文(省略可能)
  </ListBox>
  <Button Height="20" Width="100" DockPanel.Dock="Top">Buy a Pet</Button>
  <!--implicit: </DockPanel.Children>--> → プロパティ要素構文(省略可能)
</DockPanel>

ただし、「要素ツリー」は、実体そのものではない「メタファ」であるため、XML DOM のようなXMLツリー操作用のAPIを使用して直接操作することはない。これは、WPFのUIサブシステムのアーキテクチャ、UIフレームワークの構造を理解する上で役立つ。「要素ツリー」には、「論理ツリー」・「ビジュアル ツリー」の2つの解釈方法がある。

論理ツリー

「論理ツリー」は、CLRオブジェクトの要素のツリーを表し、「プロパティ継承」や、「ルーティング イベント」を理解する上で役立つ。

以下のXAMLとコードは、同じ「論理ツリー」(CLRオブジェクトの要素のツリー)を生成するため、同じUIを表示する。

ビジュアル ツリー

「ビジュアル ツリー」は、「論理ツリー」と異なり、ビジュアル要素のツリーを表し、コントロールの「テンプレート」が展開される。このため、アプリケーションのUIで使用する、全てのVisualオブジェクト(描画内容)が含まれる。

論理ツリーと、ビジュアル ツリーの確認方法

実際に、「要素ツリー」の内容を確認したい場合は、以下のヘルパ クラスのGetChildren?メソッドを再帰的に使用して、各ツリーを出力すると良い。

上記のヘルパ クラスの使用例を、サンプルコードとして以下に示す。

WPFプロパティ システム

本項では、DependencyObject?により実装される「WPFプロパティ システム」の

について説明する。

なお、それぞれのプロパティの定義方法などについても触れるが、実際にこれらのプロパティを独自に定義する必要がある場合は、カスタム コントロール作成時にあたる 。

依存関係プロパティ

「依存関係プロパティ」は、「WPFプロパティ システム」によってサポートされるプロパティで、WPFに管理される一種の辞書構造を用いて値を保持する。「依存関係プロパティ」では、変更監視・有効値検証などの機能が使用可能である。

なお、以下それぞれの機能の説明と、DependencyProperty?.Registerメソッドを使用して「依存関係プロパティ」を登録する際の第4引数に指定するPropertyMetadata? クラスと、そのコールバックのコード例を示す。

添付プロパティ

「添付プロパティ」は、「WPFプロパティ システム」によってサポートされるプロパティで、「添付プロパティ」のほとんどが「依存関係プロパティ」として実装される。

ここでは、

を例に挙げて説明する。

TextBlock?のレイアウトを制御するため、TextBlock?に(仮に)CanvasTop?CanvasLeft?プロパティを持たせた場合、TextBlock?が、Canvas要素の子要素ではない場合、無駄なデータを持つことになる。この問題を解決するため、「添付プロパティ」は、親要素が子要素の動作を制御するためのプロパティを子要素から指定可能な一種のグローバル プロパティとして実装する。以下は、レイアウトを制御するCanvas要素の「添付プロパティ」であるCanvas.Top、Canvas.Leftプロパティへ、XAMLからTextBlock?要素から値を設定する方法である。

<Canvas>
  <TextBlock Canvas.Top="10" Canvas.Left="20">
    Hello World!
  </TextBlock>
</Canvas>

子要素から親要素への「添付プロパティ」設定の際、内部的には「添付プロパティ」のアクセッサ メソッド(後述)のキーに子要素が指定される。

また、「添付プロパティ」はCLRプロパティ ラッパではなく、

の名称付与基準に従った、専用の静的 get、setアクセッサ メソッドを実装する必要がある。

public static void Setプロパティ変数名(UIElement element, Boolean value) {
  element.SetValue(this.プロパティ変数, value);
}
public static Boolean Getプロパティ変数名(UIElement element) {
  return (Boolean)element.GetValue(this.プロパティ変数);
}

これらのアクセッサ メソッドは、XAMLリーダが「添付プロパティ」をXAMLの属性として認識し、適切な型を解決できるようにするために必要となる。コードビハインドから、これらのアクセッサ メソッドを使用して「添付プロパティ」を設定する際は、キーに子要素を指定する。

<Canvas x:Name="myCanvas">
</Canvas>

TextBlock textBlock = new TextBlock();
textBlock.Text = "Hello World!";

// 添付プロパティを設定
Canvas.SetTop(textBlock, 10);
Canvas.SetLeft(textBlock, 20);

this.myCanvas.Children.Add(textBlock);

プロパティ継承(包含継承)

「WPFプロパティ システム」では、包含継承(HTMLコードと同様に、親要素に書いた属性値が子要素に継承される。HTMLでは、例えば、body要素に対してstyle属性やfont属性でフォント・サイズを指定すると、body要素内のすべての要素のフォント・サイズが変化する。※ 注:オブジェクト指向プログラミングにおける継承(派生クラスがその基本クラスからメンバ定義を継承する)とは異なる概念。)をサポートしている。

これを実現するのが「プロパティ値の継承」の機能である。「プロパティ値の継承」がされる一部のプロパティは、「要素ツリー」の最も近い親要素から特定のプロパティの値を継承し、親要素のプロパティ値が変更された場合、自動的に子要素に反映する。この動作は、子要素から親要素のプロパティを設定する「添付プロパティ」と逆の働きであるため、「プロパティ値の継承」の仕組みも、「添付プロパティ」と同様の方式で実装されていることが分かる(そのため、「添付プロパティ」と同様、プロパティの登録にDependencyProperty?.RegisterAttached?メソッドを使用する)。

なお、「プロパティ値の継承」のブロッキング境界として、Frameなどのコントロールを使用できる。

データ バインディング

WPFでは、ビュー(XAML:データの表示)とモデル(コードビハインド:チェック処理や、データアクセス処理などアプリケーションの処理)を分離するための仕組みとして、「データ バインディング」という機能を提供している。これには、前述の「依存関係プロパティ」を使用している。

このため、「データ バインディング」の機能は、

の3つの要素によって提供されると言って良い。

「データ バインディング」のプロパティ

Bindingクラスが持つ主要なプロパティ

項番プロパティ説明
Modeデータが反映される方向を、以下の列挙型から選択して指定する。
OneWay?:「バインディング ソース」変更時、「バインディング ターゲット」を更新する。
OneWayToSource?:「バインディング ターゲット」変更時、「バインディング ソース」を更新する。
 ※ WPFのみで、「Silverlight」には無いモード
TwoWay?:「バインディング ソース」、「バインディング ターゲット」が変更された場合、対応する「バインディング ターゲット」、「バインディング ソース」を更新する。
OneTime?:アプリケーション起動時のみ、「バインディング ターゲット」のデータを更新(StaticResource?に接続する場合などに使用する)
・Default:「バインディング ターゲット」の既定のModeとなる。
 ※ WPFのみで、「Silverlight」には無いモード
Source「バインディング ソース」を指定する。
Path「バインディング ソース」のプロパティ名を指定する(名前空間のフルパスで指定可)。

XAMLで「データ バインディング」を実装

「データ バインディング」は、XAML上の要素のプロパティ値として「バインディングのマークアップ拡張」である「{Binding ・・・}」という記述を使用してBindingオブジェクトを生成・初期化することで実装する。

DataContext?の使用

暗黙的な型変換

また、「データ バインディング」では、「バインディング ターゲット」と「バインディング ソース」でプロパティ型が異なる場合、暗黙的な型変換が行われる。

色々なデータ バインディングのパターン

ルーティング イベント

イベントを生成したUIコントロール上だけでなく、「ビジュアル ツリー」内の複数のリスナ上でイベント ハンドラを呼び出すことができるWPFのイベント。例えば、Buttonコントロールが「テンプレート」を使用し、複数のコントロールから構成される場合、下位のコントロールのイベントを上位のButtonコントロールでもハンドルできる。

ルーティング方法

「ルーティング イベント」は、3つのルーティング方法のいずれかを使用する。

項番区分機能
トンネルツリーを下方向へ辿る。
最初に、「ビジュアル ツリー」のルートのイベント ハンドラが呼び出され、次に、イベントを発生させた要素に到達するまで、「経路」沿いの「トンネル ルーティング イベント」のイベント ハンドラを呼び出す。なお、「トンネル ルーティング イベント」のイベント名は、先頭に「Preview」を付与するルールとなっている。
バブルツリーを上方向へ辿る。
最初に、イベントを発生させた要素のイベント ハンドラが呼び出され、次に、「ビジュアル ツリー」のルートに到達するまで、「経路」沿いの「バブル ルーティング イベント」のイベント ハンドラを呼び出す。
直接イベントを発生させた要素のみに、イベント ハンドラを呼び出す機会が与えられる。 これは、Windows フォームのイベントと似ている。ただし、標準のCLRイベントとは異なり、「クラス処理」をサポートする。

多くの場合、WPF から提供される入力イベントは、「トンネル ルーティング イベント」 / 「バブル ルーティング イベント」ペアとして実装される。

通常の使い方

イベントを発生元の要素で処理する限り、「ルーティング イベント」の動作はあまり表面には見えないので、イベントが「ルーティング イベント」として実装されていることに気を配る必要はない。

これは、「ルーティング イベント」は、共通ハンドラを定義する場合や、カスタム コントロールを複合化する場合などの、「特定のシナリオ」で効果を発揮するためである。例えば、以下は、Buttonコントロールのイベントを発生元の要素で処理するイベント ハンドラを実装する例である。この場合、WPFのデザイナにButtonコントロールをD & Dし、これをダブル クリックすることで、発生元の要素で処理するButton.Clickイベント ハンドラを実装できる。

イベントの追加方法

サンプル

下記のXAMLは、

の動作を確認するためのサンプル。

このコードを実装して、画面上のRectangleをクリックすると、以下のようなDebug出力を確認でき、この出力から「トンネル ルーティング イベント」 / 「バブル ルーティング イベント」ペアが正しく動作していることを確認できる。

stackPanel1_PreviewMouseDown
→ border1_PreviewMouseDown
→ rect1_PreviewMouseDown
→ rect1_MouseDown
→ border1_MouseDown
→ stackPanel1_MouseDown

なお、各イベント ハンドラで「e.Handled = true」を実行すると、ルーティングは停止する。例えば、「トンネル ルーティング イベント」でルーティングを停止すれば、「バブル ルーティング イベント」も発生しなくなる。

WPFのコントロール

標準コントロール

WPFは、次のような標準コントロールを備えている。

コントロール(Control)

ボタンやリストボックスなどのコントロール類

一例

パネル(Panel)

子要素の配置を決める。

一例

シェイプ(Shape)

ベクタグラフィックスを描画

一例

メディア(FrameworkElement?

静止画や動画などの表示

一例

ドキュメント(TextBoxBase?

文書整形

一例

ユーザ コントロール

ユーザ コントロールは、UI要素の部品化を目的としたもので、Windowsフォーム、ASP.NETアプリケーション(Webフォーム)などと同様に、ユーザ コントロールの開発・使用が可能である。

WPFでは、ユーザ コントロールのCLRプロパティを初期化する場合、XAML要素から指定可能であったり、ユーザ コントロール内の各コントロールに「データ バインディング」する場合は、ユーザ コントロールのDataContext?プロパティに「バインディング ソース」を指定可能であったり、また、「ツールチップ」の表示にユーザ コントロールを設定したりするなど、ユーザ コントロールをより柔軟に利用できる。

画面遷移での利用

部品の汎用化

ユーザ コントロールのプロパティを以下の用途で使用する場合、それぞれ追加の実装が必要になるので注意する。

XAMLの書き方?


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS