Open棟梁Project - マイクロソフト系技術情報 Wiki
パネルと呼ばれるレイアウト用コントロールを使用して、Windowsフォームのような座標レイアウト、HTMLのようなフロー レイアウト(ブロック化、格子分割)にも対応する。
イベント トリガ、トリガ アクション、ストーリ ボードを定義することで、UIコントロールなどの表示項目にタイムライン ベースのアニメーションの効果を付加できる。
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グラフィックの高度な機能に関する制限であるので、リッチでインタラクティブな業務アプリケーションの開発に、大きな支障は無いと言える。
項番 | 区分 | 説明 |
1 | パフォーマンス | Direct3Dと比べるとオーバーヘッドがあり、GPU負荷が高くなる。また、WPFはDirect3D 9対応であり、Direct3D 10対応のグラフィックボードであってもパフォーマンスは向上しない。 |
2 | シェーダ | WPFは内部的にシェーダを使用しているが、開発者がWPFからシェーダを使用することはできない。 |
3 | スキニング | WPFではスキニングができないので、滑らかな関節を持つアニメーションはできない(剛体アニメーションのみサポート)。 |
4 | 環境マッピング | WPFでは環境マッピングが使用できないので、物体に周囲の景色が反射しているような効果は表現できない。 |
5 | 影 | WPFではステンシル バッファ(3Dグラフィックにおいて、物体の重ね合わせなど、描画しなくても良い領域を効率よく判定するためのバッファ領域)や、深度バッファを使用できないためリアルな影を生成できない。 |
6 | テクスチャ・フィルタリング | ポリゴンのテクスチャを構成するピクセルにテクセル(3Dグラフィックにおいて、テクスチャを構成する一つ一つのピクセル)が一対一に対応することはほとんどなく、 ポリゴンにテクスチャをマッピングする際には、テクスチャを拡大/縮小(変形)するフィルタが必要となるが、WPFから、このフィルタを指定することはできない。 |
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 エンジンによって実行されるようになっている。)、メモリ・実行の詳細な制御の要件による。
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要素の操作処理)が、容易に実装可能となる。
このメソッドに指定されたデリゲートは、同期実行されるため、バック グラウンド スレッドが実行結果を表示する「UI変更処理」に利用することが多い。
これらのメソッドは、内部的には
これにより、
http://msdn.microsoft.com/ja-jp/library/system.windows.dependencyobject.aspx
DependencyObject?は、派生したCLRオブジェクトに「WPFプロパティ システム」を実装する。
WPFのアーキテクチャの理念は、「メソッドやイベント(コードビハインド)よりも、なるべくXAMLのマークアップによる宣言的プロパティを使用する」ことである。これを実現する「WPFプロパティ システム」を実装するDependencyObject?により、開発者は多数の宣言的プロパティを使用し、コントロールに開発者の意図を設定できる。このためWPFの開発元は、このXAMLのマークアップによる宣言的プロパティによる制御の範囲を拡大するために、「WPFプロパティ システム」の「依存関係プロパティ」が必要であると判断した。「依存関係プロパティ」は、プロパティの依存関係を把握し、双方向の接続と変更通知を実現する。具体的には、ソースとなるオブジェクトのプロパティの変更が通知された場合(必須ではないが、INotifyPropertyChanged?インターフェイスを使用すると、オブジェクトによる変更通知の発行が可能になる。)、ターゲットとなるオブジェクトのプロパティ値を自動的に検証、計算するなど、高度なオブジェクト プロパティ間の接続を実現する。
「依存関係プロパティ」の入力には、次のものがある。
http://msdn.microsoft.com/ja-jp/library/system.windows.media.visual.aspx
Visualは、WPF の中心的な機能である描画をサポートする基本抽象クラスである。
Visualは、マネージ コンポーネントとアンマネージ コンポーネントの2つのサブシステムの接続ポイントであり、Visualで定義されているマネージ データ(描画情報、描画方法など)から、「ビジュアル ツリー」(後述)と呼ばれるツリー構造のアンマネージ データを構成する。これを「レンダリング スレッド」がツリー構造の上から下にスキャンすることによって描画内容をレンダリングする。
┬Visual ├UIElement │└FrameworkElement ├ContainerVisual │├DrawingVisual │├HostVisual └Viewport3DVisual
http://msdn.microsoft.com/ja-jp/library/system.windows.uielement.aspx
UIElementは、WPFコア レベル実装の基本クラス
http://msdn.microsoft.com/ja-jp/library/system.windows.frameworkelement.aspx
UIElementに、「レイアウト」、「スタイル」を中心とした機能を追加する基本クラス。
http://msdn.microsoft.com/ja-jp/library/system.windows.controls.control.aspx
「テンプレート」を使用して外観を定義する UI 要素の基本クラス
Controlの最も重要な機能は、「テンプレート」である。この「テンプレート」により、コントロールの「外観」を宣言型マークアップでカスタマイズ可能になる(「テンプレート」を複数の子要素から構成する)。また、「イベント ハンドラ」や「イベント トリガ」などもこの「テンプレート」により定義可能で、「テンプレート」を「スタイル」化することで、任意の型のコントロールに、これらの「テンプレート」の定義の適用を強制できる。
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構文を「コンテンツ構文」と呼ぶ。
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を表示する。
<DockPanel> <ListBox Width="100"> <ListBoxItem>Dog</ListBoxItem> <ListBoxItem>Cat</ListBoxItem> <ListBoxItem>Fish</ListBoxItem> </ListBox> <Button Click="OnClick">OK</Button> </DockPanel>
DockPanel dp = new DockPanel(); ListBox lbx = new ListBox(); lbx.Width = 100; ListBoxItem lbxItem = null; lbxItem = new ListBoxItem(); lbxItem.Content = "Dog"; lbx.Items.Add(lbxItem); lbxItem = new ListBoxItem(); lbxItem.Content = "Cat"; lbx.Items.Add(lbxItem); lbxItem = new ListBoxItem(); lbxItem.Content = "Fish"; lbx.Items.Add(lbxItem); dp.Children.Add(lbx); Button btn = new Button(); btn.Content = "OK"; btn.Click += new RoutedEventHandler(this.OnClick); dp.Children.Add(btn); this.AddChild(dp);
「ビジュアル ツリー」は、「論理ツリー」と異なり、ビジュアル要素のツリーを表し、コントロールの「テンプレート」が展開される。このため、アプリケーションのUIで使用する、全てのVisualオブジェクト(描画内容)が含まれる。
MSDN > Windows Presentation Foundation > チュートリアル:
Win32アプリケーションでのビジュアル オブジェクトのホスト
http://msdn.microsoft.com/ja-jp/library/ms754039.aspx
実際に、「要素ツリー」の内容を確認したい場合は、以下のヘルパ クラスのGetChildren?メソッドを再帰的に使用して、各ツリーを出力すると良い。
上記のヘルパ クラスの使用例を、サンプルコードとして以下に示す。
private void PrintLogicalTree() { Debug.WriteLine("PrintLogicalTree"); PrintLogicalTree(0, this); } // 論理ツリーを出力する。 // DependencyObjectの場合は、子要素も再帰的に表示する private void PrintLogicalTree(int level, DependencyObject obj) { PrintObject(level, obj); foreach (var child in LogicalTreeHelper.GetChildren(obj)) { if (child is DependencyObject) { PrintLogicalTree(level + 1, (DependencyObject)child); } else { PrintObject(level + 1, child); } } }
private void PrintVisualTree() { Debug.WriteLine("PrintVisualTree"); PrintVisualTree(0, this); } // ビジュアル ツリーを表示する。 // DependencyObjectの場合はビジュアル ツリー上の子要素も再帰的に出力していく private void PrintVisualTree(int level, DependencyObject obj) { PrintObject(level, obj); foreach (var child in GetVisualChildren(obj)) { if (child is DependencyObject) { PrintVisualTree(level + 1, (DependencyObject)child); } else { PrintObject(level + 1, child); } } } // ビジュアル ツリーの子要素の列挙を返す private IEnumerable<object> GetVisualChildren(DependencyObject obj) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { yield return VisualTreeHelper.GetChild(obj, i); } }
// ToStringの結果をインデントつきで出力 private void PrintObject(int level, object obj) { Debug.WriteLine(new string('\t', level) + obj); }
本項では、DependencyObject?により実装される「WPFプロパティ システム」の
について説明する。
なお、それぞれのプロパティの定義方法などについても触れるが、実際にこれらのプロパティを独自に定義する必要がある場合は、カスタム コントロール作成時にあたる 。
「依存関係プロパティ」は、「WPFプロパティ システム」によってサポートされるプロパティで、WPFに管理される一種の辞書構造を用いて値を保持する。「依存関係プロパティ」では、変更監視・有効値検証などの機能が使用可能である。
// 依存関係プロパティを実装するクラス public class ConcreteDependencyObject : DependencyObject { // 依存関係プロパティ public static readonly DependencyProperty CaptionProperty; // 静的コンストラクタ static ConcreteDependencyObject(){ // 静的コンストラクタで依存関係プロパティを登録 CaptionProperty = DependencyProperty.Register( "Caption", // プロパティ名 typeof(string), // プロパティの型 typeof(ConcreteDependencyObject), // プロパティの所有者 new PropertyMetadata()); // 各種メタデータ } // 依存関係プロパティは設定・取得方法が特殊であるので、 // 以下のように、CLRプロパティでラップする。 public string Caption { get { return this.GetValue(ConcreteDependencyObject.CaptionProperty) as string; } set { this.SetValue(ConcreteDependencyObject.CaptionProperty, value); } } }
なお、以下それぞれの機能の説明と、DependencyProperty?.Registerメソッドを使用して「依存関係プロパティ」を登録する際の第4引数に指定するPropertyMetadata? クラスと、そのコールバックのコード例を示す。
new PropertyMetadata("DefaultValue")
new PropertyMetadata("DefaultValue", new PropertyChangedCallback(OnPropertyChangedCallback)) // プロパティ値の変更監視コールバック private static void OnPropertyChangedCallback( DependencyObject d, DependencyPropertyChangedEventArgs e) { // プロパティ値の変更監視の例: string oldValue = (string)e.OldValue; string newValue = (string)e.NewValue; DependencyProperty dp = e.Property; }
new PropertyMetadata("DefaultValue", new PropertyChangedCallback(OnPropertyChangedCallback), new CoerceValueCallback(OnCoerceValueCallBack)) // 依存関係プロパティ値の強制コールバック private static object OnCoerceValueCallBack(DependencyObject d, object value) { // プロパティ値の強制の例: // Captionプロパティ値に「"ChangeValue"」が設定されない限り // Captionプロパティ値は「"DefaultValue"」を強制する。 if (value.ToString() == "ChangeValue") return value; return "DefaultValue"; }
new PropertyMetadata("DefaultValue", new PropertyChangedCallback(OnPropertyChangedCallback), new CoerceValueCallback(OnCoerceValueCallBack), new ValidateValueCallback(OnValidateValueCallback)); // プロパティ値の有効値検証コールバック private static bool OnValidateValueCallback(object value) { // プロパティ値の有効値検証の例: // Captionプロパティ値に「" DengerousValue "」が設定されるとエラーになる。 if (value.ToString() == "DengerousValue") return false; return true; }
「添付プロパティ」は、「WPFプロパティ システム」によってサポートされるプロパティで、「添付プロパティ」のほとんどが「依存関係プロパティ」として実装される。
ここでは、
- 親要素として、Canvas要素
- 子要素として、TextBlock?要素
を例に挙げて説明する。
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(プロパティ名)
の名称付与基準に従った、専用の静的 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クラスが持つ主要なプロパティ
項番 | プロパティ | 説明 |
1 | Mode | データが反映される方向を、以下の列挙型から選択して指定する。 ・OneWay?:「バインディング ソース」変更時、「バインディング ターゲット」を更新する。 ・OneWayToSource?:「バインディング ターゲット」変更時、「バインディング ソース」を更新する。 ※ WPFのみで、「Silverlight」には無いモード ・TwoWay?:「バインディング ソース」、「バインディング ターゲット」が変更された場合、対応する「バインディング ターゲット」、「バインディング ソース」を更新する。 ・OneTime?:アプリケーション起動時のみ、「バインディング ターゲット」のデータを更新(StaticResource?に接続する場合などに使用する) ・Default:「バインディング ターゲット」の既定のModeとなる。 ※ WPFのみで、「Silverlight」には無いモード |
2 | Source | 「バインディング ソース」を指定する。 |
3 | Path | 「バインディング ソース」のプロパティ名を指定する(名前空間のフルパスで指定可)。 |
「データ バインディング」は、XAML上の要素のプロパティ値として「バインディングのマークアップ拡張」である「{Binding ・・・}」という記述を使用してBindingオブジェクトを生成・初期化することで実装する。
また、「データ バインディング」では、「バインディング ターゲット」と「バインディング ソース」でプロパティ型が異なる場合、暗黙的な型変換が行われる。
イベントを生成したUIコントロール上だけでなく、「ビジュアル ツリー」内の複数のリスナ上でイベント ハンドラを呼び出すことができるWPFのイベント。例えば、Buttonコントロールが「テンプレート」を使用し、複数のコントロールから構成される場合、下位のコントロールのイベントを上位のButtonコントロールでもハンドルできる。
「ルーティング イベント」は、3つのルーティング方法のいずれかを使用する。
項番 | 区分 | 機能 |
1 | トンネル | ツリーを下方向へ辿る。 最初に、「ビジュアル ツリー」のルートのイベント ハンドラが呼び出され、次に、イベントを発生させた要素に到達するまで、「経路」沿いの「トンネル ルーティング イベント」のイベント ハンドラを呼び出す。なお、「トンネル ルーティング イベント」のイベント名は、先頭に「Preview」を付与するルールとなっている。 |
2 | バブル | ツリーを上方向へ辿る。 最初に、イベントを発生させた要素のイベント ハンドラが呼び出され、次に、「ビジュアル ツリー」のルートに到達するまで、「経路」沿いの「バブル ルーティング イベント」のイベント ハンドラを呼び出す。 |
3 | 直接 | イベントを発生させた要素のみに、イベント ハンドラを呼び出す機会が与えられる。 これは、Windows フォームのイベントと似ている。ただし、標準のCLRイベントとは異なり、「クラス処理」をサポートする。 |
多くの場合、WPF から提供される入力イベントは、「トンネル ルーティング イベント」 / 「バブル ルーティング イベント」ペアとして実装される。
イベントを発生元の要素で処理する限り、「ルーティング イベント」の動作はあまり表面には見えないので、イベントが「ルーティング イベント」として実装されていることに気を配る必要はない。
これは、「ルーティング イベント」は、共通ハンドラを定義する場合や、カスタム コントロールを複合化する場合などの、「特定のシナリオ」で効果を発揮するためである。例えば、以下は、Buttonコントロールのイベントを発生元の要素で処理するイベント ハンドラを実装する例である。この場合、WPFのデザイナにButtonコントロールをD & Dし、これをダブル クリックすることで、発生元の要素で処理するButton.Clickイベント ハンドラを実装できる。
<Grid> <Button Height="23" Name="button1" Click="button1_Click">Button</Button> </Grid>
public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { // button1_Click } }
this.button1.AddHandler(Button.ClickEvent, new RoutedEventHandler(button1_Click));
this.button1.Click += new RoutedEventHandler(button1_Click);
下記のXAMLは、
の動作を確認するためのサンプル。
<StackPanel x:Name="stackPanel1" Height="100" Width="100" Orientation="Vertical" MouseDown="stackPanel1_MouseDown" PreviewMouseDown="stackPanel1_PreviewMouseDown"> <Border x:Name="border1" MouseDown="border1_MouseDown" PreviewMouseDown="border1_PreviewMouseDown"> <Rectangle x:Name="rect1" Height="100" Width="100" Fill="Black" MouseDown="rect1_MouseDown" PreviewMouseDown="rect1_PreviewMouseDown"> </Rectangle> </Border> </StackPanel>
/// <summary>stackPanel1のMouseDownイベント</summary> private void stackPanel1_MouseDown(object sender, MouseEventArgs e) { System.Diagnostics.Debug.WriteLine("→ stackPanel1_MouseDown"); } /// <summary>stackPanel1のPreviewMouseDownイベント</summary> private void stackPanel1_PreviewMouseDown(object sender, MouseEventArgs e) { System.Diagnostics.Debug.WriteLine("→ stackPanel1_PreviewMouseDown"); } /// <summary>border1のMouseDownイベント</summary> private void border1_MouseDown(object sender, MouseEventArgs e) { System.Diagnostics.Debug.WriteLine("→ border1_MouseDown"); } /// <summary>border1のPreviewMouseDownイベント</summary> private void border1_PreviewMouseDown(object sender, MouseEventArgs e) { System.Diagnostics.Debug.WriteLine("→ border1_PreviewMouseDown"); } /// <summary>rect1のMouseDownイベント</summary> private void rect1_MouseDown(object sender, MouseEventArgs e) { System.Diagnostics.Debug.WriteLine("→ rect1_MouseDown"); } /// <summary>rect1のPreviewMouseDownイベント</summary> private void rect1_PreviewMouseDown(object sender, MouseEventArgs e) { System.Diagnostics.Debug.WriteLine("→ rect1_PreviewMouseDown"); }
このコードを実装して、画面上のRectangleをクリックすると、以下のようなDebug出力を確認でき、この出力から「トンネル ルーティング イベント」 / 「バブル ルーティング イベント」ペアが正しく動作していることを確認できる。
stackPanel1_PreviewMouseDown → border1_PreviewMouseDown → rect1_PreviewMouseDown → rect1_MouseDown → border1_MouseDown → stackPanel1_MouseDown
なお、各イベント ハンドラで「e.Handled = true」を実行すると、ルーティングは停止する。例えば、「トンネル ルーティング イベント」でルーティングを停止すれば、「バブル ルーティング イベント」も発生しなくなる。
WPFは、次のような標準コントロールを備えている。
ボタンやリストボックスなどのコントロール類
一例
子要素の配置を決める。
一例
ベクタグラフィックスを描画
一例
静止画や動画などの表示
一例
文書整形
一例
ユーザ コントロールは、UI要素の部品化を目的としたもので、Windowsフォーム、ASP.NETアプリケーション(Webフォーム)などと同様に、ユーザ コントロールの開発・使用が可能である。
WPFでは、ユーザ コントロールのCLRプロパティを初期化する場合、XAML要素から指定可能であったり、ユーザ コントロール内の各コントロールに「データ バインディング」する場合は、ユーザ コントロールのDataContext?プロパティに「バインディング ソース」を指定可能であったり、また、「ツールチップ」の表示にユーザ コントロールを設定したりするなど、ユーザ コントロールをより柔軟に利用できる。
ユーザ コントロールのプロパティを以下の用途で使用する場合、それぞれ追加の実装が必要になるので注意する。