チュートリアル : Win32 アプリケーションでの Windows Presentation Foundation コンテンツのホスト

更新 : 2007 年 11 月

Windows Presentation Foundation (WPF) は、アプリケーションの作成に適した環境を提供します。ただし、Win32 コードに対して行った投資が大きい場合は、元のコードを記述し直すよりも、WPF の機能をアプリケーションに追加する方が効率的である場合があります。WPF には、Win32 ウィンドウの WPF コンテンツをホストするための簡単なしくみが用意されています。

このチュートリアルでは、サンプル アプリケーション「Win32 ウィンドウでの Windows Presentation Foundation コンテンツのホストのサンプル」の記述方法を説明します。このサンプル アプリケーションでは、WPF コンテンツが Win32 ウィンドウでホストされます。このサンプルを拡張すると、任意の Win32 ウィンドウをホストできます。その場合は、マネージ コードとアンマネージ コードが混在するため、アプリケーションを C++/CLI で記述します。

このトピックには次のセクションが含まれています。

  • 要件
  • 基本手順
  • ホスト アプリケーションの実装
  • WPF ページの実装
  • 関連トピック

要件

このチュートリアルは、WPF および Win32 のプログラミングに関する基本的な知識があることを前提としています。WPF プログラミングの概要については、「概要 (WPF)」を参照してください。Win32 プログラミングの概要については多数の書籍が出版されているので、それらを参照してください。特に、『プログラミング Windows』(Charles Petzold 著) が参考になります。

このチュートリアルに含まれるサンプルは C++/CLI で実装されるため、このチュートリアルでは、C++ を使用した Win32 API のプログラミングの知識とマネージ コード プログラミングの知識があることも前提としています。C++/CLI の知識は役立ちますが、必須ではありません。

ms744829.alert_note(ja-jp,VS.90).gifメモ :

このチュートリアルには、関連するサンプルからのコード例が数多く含まれています。ただし、読みやすさのために、サンプル コード全体は含まれていません。サンプル コード全体については、「Win32 ウィンドウでの Windows Presentation Foundation コンテンツのホストのサンプル」を参照してください。

基本手順

ここでは、Win32 ウィンドウで WPF コンテンツをホストするための基本的な手順を説明します。残りのセクションでは、各手順の詳細について説明します。

Win32 ウィンドウで WPF コンテンツをホストする鍵となるのは、HwndSource クラスです。このクラスは Win32 ウィンドウの WPF コンテンツをラップし、そのコンテンツを子ウィンドウとして ユーザー インターフェイス (UI) に組み込むことができます。次の方法では、Win32 および WPF を単一のアプリケーションに統合します。

  1. WPF コンテンツをマネージ クラスとして実装します。

  2. Win32 アプリケーションを C++/CLI で実装します。既存のアプリケーションおよびアンマネージ C++ コードを利用する場合、通常は、/clr コンパイラ フラグが含まれるようにプロジェクトの設定を変更して、アプリケーションでマネージ コードを呼び出せるようにすることができます。

  3. スレッド モデルをシングル スレッド アパートメント (STA) に設定します。

  4. WM_CREATE 通知をウィンドウ プロシージャで処理し、次の処理を行います。

    1. parent パラメータとして親ウィンドウを持つ新しい HwndSource オブジェクトを作成します。

    2. WPF コンテンツ クラスのインスタンスを作成します。

    3. WPF コンテンツ オブジェクトへの参照を HwndSourceRootVisual プロパティに割り当てます。

    4. コンテンツの HWND を取得します。HwndSource オブジェクトの Handle プロパティにウィンドウ ハンドル (HWND) が格納されます。アプリケーションのアンマネージ部分で使用できる HWND を取得するには、Handle.ToPointer() を HWND にキャストします。

  5. WPF コンテンツへの参照を保持する静的フィールドを含むマネージ クラスを実装します。このクラスを使用すると、Win32 コードから WPF コンテンツへの参照を取得できます。

  6. 静的フィールドに WPF コンテンツを割り当てます。

  7. ハンドラを 1 つ以上の WPF イベントにアタッチして、WPF コンテンツから通知を受信します。

  8. 静的フィールドに格納した参照を使用してプロパティの設定などを行い、WPF コンテンツと通信します。

ms744829.alert_note(ja-jp,VS.90).gifメモ :

Extensible Application Markup Language (XAML) を使用して WPF コンテンツを実装することもできます。ただし、ダイナミック リンク ライブラリ (DLL) として別個にコンパイルし、その DLL を Win32 アプリケーションから参照する必要があります。手順の残りの部分は、前述の手順と同様です。

ホスト アプリケーションの実装

ここでは、基本的な Win32 アプリケーションで WPF コンテンツをホストする方法について説明します。コンテンツ自体は、C++/CLI でマネージ クラスとして実装されます。その大部分は、簡単な WPF プログラミングです。コンテンツ実装の主要な側面については、「WPF コンテンツの実装」を参照してください。

  • 基本アプリケーション

  • WPF コンテンツのホスト

  • WPF コンテンツへの参照の保存

  • WPF コンテンツとの通信

基本アプリケーション

ホスト アプリケーションは、Microsoft Visual Studio 2005 テンプレートの作成から開始しました。

  1. Visual Studio 2005 を開き、[ファイル] メニューの [新しいプロジェクト] を選択します。

  2. Visual C++ プロジェクトの種類の一覧から [Win32] を選択します。既定の言語が C++ 以外の場合、これらのプロジェクトの種類は [他の言語] の下に表示されます。

  3. [Win32 プロジェクト] テンプレートを選択し、プロジェクトに名前を割り当てて [OK] をクリックします。Win32 アプリケーション ウィザードが起動します。

  4. ウィザードの既定の設定を受け入れ、[完了] をクリックしてプロジェクトを開始します。

テンプレートによって、次のものが含まれた基本的な Win32 アプリケーションが作成されます。

  • アプリケーションのエントリ ポイント。

  • 関連付けられたウィンドウ プロシージャ (WndProc) を持つウィンドウ。

  • [ファイル] および [ヘルプ] の見出しを持つメニュー。[ファイル] メニューには、アプリケーションを閉じるための [終了] 項目があります。[ヘルプ] メニューには、単純なダイアログ ボックスを起動するための [バージョン情報] 項目があります。

WPF コンテンツをホストするコードの記述を開始する前に、この基本テンプレートに 2 つの変更を加える必要があります。

1 つ目は、プロジェクトのコンパイルをマネージ コードとして行うことです。既定では、プロジェクトはアンマネージ コードとしてコンパイルされます。しかし、WPF はマネージ コードで実装されるため、プロジェクトもそれに応じてコンパイルする必要があります。

  1. ソリューション エクスプローラでプロジェクト名を右クリックし、コンテキスト メニューから [プロパティ] を選択します。[プロパティ ページ] ダイアログ ボックスが表示されます。

  2. 左ペインのツリー ビューから [構成プロパティ] を選択します。

  3. 右ペインの [プロジェクトの既定値] の一覧から [共通言語ランタイム] サポートを選択します。

  4. ドロップダウン リスト ボックスから [共通言語ランタイム サポート (/clr)] を選択します。

ms744829.alert_note(ja-jp,VS.90).gifメモ :

このコンパイラ フラグを使用すると、アプリケーションでマネージ コードを使用できるようになりますが、アンマネージ コードも以前と同様にコンパイルされます。

WPF では、シングル スレッド アパートメント (STA) スレッド モデルが使用されます。WPF コンテンツ コードを適切に使用するためには、エントリ ポイントに属性を適用して、アプリケーションのスレッド モデルを STA に設定する必要があります。

WPF コンテンツのホスト

この WPF コンテンツは、簡単な住所入力アプリケーションです。ユーザー名や住所などを入力する複数の TextBox コントロールで構成されています。また、[OK] と [Cancel] の 2 つの Button コントロールがあります。ユーザーが [OK] をクリックすると、ボタンの Click イベント ハンドラが TextBox コントロールからデータを収集して対応するプロパティに割り当て、カスタム イベント OnButtonClicked を発生させます。ユーザーが [Cancel] をクリックすると、ハンドラは単に OnButtonClicked を発生させます。OnButtonClicked のイベント引数オブジェクトには、どのボタンがクリックされたかを示すブール値のフィールドが含まれています。

WPF コンテンツをホストするコードは、ホスト ウィンドウの WM_CREATE 通知のハンドラに実装されます。

GetHwnd メソッドは、サイズと位置の情報に加えて親ウィンドウ ハンドルを受け取り、ホストされた WPF コンテンツのウィンドウ ハンドルを返します。

ms744829.alert_note(ja-jp,VS.90).gifメモ :

System::Windows::Interop 名前空間に対する #using ディレクティブは使用できません。使用すると、その名前空間の MSG 構造体と、winuser.h で宣言されている MSG 構造体の間に、名前の競合が生じます。その名前空間のコンテンツにアクセスするには、代わりに完全修飾名を使用する必要があります。

WPF コンテンツをアプリケーション ウィンドウで直接ホストすることはできません。代わりに、まず、WPF コンテンツをラップする HwndSource オブジェクトを作成します。基本的にこのオブジェクトは、WPF コンテンツをホストするように設計されたウィンドウです。HwndSource オブジェクトを親ウィンドウでホストするには、それをアプリケーションの一部である Win32 ウィンドウの子として作成します。HwndSource コンストラクタ パラメータには、Win32 子ウィンドウの作成時に CreateWindow に渡す情報とほぼ同じ情報が含まれます。

次に、WPF コンテンツ オブジェクトのインスタンスを作成します。この場合、WPF コンテンツは別のクラス WPFPage として、C++/CLI を使用して実装されます。XAML を使用して WPF コンテンツを実装することもできます。ただし、そのためには、別のプロジェクトを設定し、WPF コンテンツを DLL として構築する必要があります。その DLL への参照をプロジェクトに追加し、その参照を使用して WPF コンテンツのインスタンスを作成できます。

WPF コンテンツを子ウィンドウに表示するには、WPF コンテンツへの参照を HwndSourceRootVisual プロパティに割り当てます。

次のコード行では、イベント ハンドラ WPFButtonClicked を WPF コンテンツの OnButtonClicked イベントにアタッチします。このハンドラは、ユーザーが [OK] または [Cancel] ボタンをクリックしたときに呼び出されます。このイベント ハンドラの詳細については、「WPF コンテンツとの通信」を参照してください。

上記のコードの最終行では、HwndSource オブジェクトに関連付けられたウィンドウ ハンドル (HWND) を返します。サンプルでは行っていませんが、このハンドルを Win32 コードから使用すると、ホストされたウィンドウにメッセージを送信できます。HwndSource オブジェクトは、メッセージを受信するたびにイベントを発生させます。メッセージを処理するには、AddHook メソッドを呼び出してメッセージ ハンドラをアタッチしてから、そのハンドラ内でメッセージを処理します。

WPF コンテンツへの参照の保存

多くのアプリケーションでは、後から WPF のコンテンツと通信することがあります。たとえば、WPF コンテンツ プロパティを変更したり、HwndSource オブジェクトで他の WPF コンテンツをホストしたりする可能性があります。そのためには、HwndSource オブジェクトまたは WPF コンテンツへの参照が必要です。HwndSource オブジェクトとそれに関連付けられた WPF コンテンツは、ウィンドウ ハンドルが破棄されるまでメモリ内に残ります。ただし、HwndSource オブジェクトに割り当てた変数は、ウィンドウ プロシージャから戻ると直ちにスコープの外に出ます。Win32 アプリケーションでこの問題を処理する通常の方法は、静的変数またはグローバル変数を使用することです。しかし、これらの種類の変数にマネージ オブジェクトを割り当てることはできません。HwndSource オブジェクトに関連付けられたウィンドウ ハンドルをグローバル変数または静的変数に割り当てることはできますが、それによってオブジェクト自体へのアクセスは提供されません。

この問題の最も簡単な解決策は、アクセスが必要なマネージ オブジェクトへの参照を保持する静的フィールドのセットを含むマネージ クラスを実装することです。サンプルでは WPFPageHost クラスを使用し、WPF コンテンツへの参照に加えて、その複数のプロパティの初期値を保持します。これらの初期値は後でユーザーが変更できます。これは、ヘッダーに定義されます。

GetHwnd 関数の後半では、後で myPage がスコープ内にある間に使用する値を、それらのフィールドに割り当てます。

WPF コンテンツとの通信

WPF コンテンツとの通信には 2 種類あります。ユーザーが [OK] または [Cancel] ボタンをクリックすると、アプリケーションは WPF コンテンツから情報を受け取ります。また、アプリケーションの UI をユーザーが使用すると、背景色や既定のフォント サイズなど、さまざまな WPF コンテンツ プロパティを変更できます。

前述のように、ユーザーがいずれかのボタンをクリックすると、WPF コンテンツが OnButtonClicked イベントを発生させます。アプリケーションは、このイベントにハンドラをアタッチして、これらの通知を受信します。[OK] ボタンがクリックされると、ハンドラが WPF コンテンツからユーザー情報を取得し、静的コントロールのセットに表示します。

ハンドラは、WPF コンテンツのカスタム イベント引数オブジェクト MyPageEventArgs を受け取ります。[OK] ボタンがクリックされた場合はオブジェクトの IsOK プロパティが true に、[Cancel] ボタンがクリックされた場合は false に、それぞれ設定されます。

[OK] ボタンがクリックされると、ハンドラが WPF コンテンツへの参照をコンテナ クラスから取得します。次に、関連付けられた WPF コンテンツ プロパティに保持されているユーザー情報を収集し、静的コントロールを使用してその情報を親ウィンドウに表示します。WPF コンテンツ データはマネージ文字列形式であるため、Win32 コントロールで使用するためにはマーシャリングが必要です。[Cancel] ボタンがクリックされると、ハンドラが静的コントロールからデータを消去します。

アプリケーションの UI が提供するラジオ ボタンのセットを使用すると、ユーザーは WPF コンテンツの背景色と、いくつかのフォント関連プロパティを変更できます。次の例は、アプリケーションのウィンドウ プロシージャ (WndProc) と、そのメッセージ処理の一部です。メッセージ処理では、背景色など、複数のメッセージのさまざまなプロパティを設定します。他の部分は同様であるため表示していません。詳細とコンテキストについては、サンプル全体を参照してください。

背景色を設定するには、WPFPageHost から WPF コンテンツ (hostedPage) への参照を取得し、背景色プロパティに適切な色を設定します。サンプルでは 3 とおりの色オプション、つまり、元の色、ライト グリーン、およびライト サーモンを使用します。元の背景色は、WPFPageHost クラスに静的フィールドとして格納します。他の 2 つを設定するには、新しい SolidColorBrush オブジェクトを作成し、Colors オブジェクトの静的な色の値をコンストラクタに渡します。

WPF ページの実装

実際の実装について知らなくても、WPF コンテンツをホストし、使用できます。WPF コンテンツが別の DLL にパッケージ化されていた場合、構築に使用された言語としては、すべての 共通言語ランタイム (CLR) 言語に可能性があります。サンプルで使用される C++/CLI 実装の簡単なチュートリアルを次に示します。ここでは、次の項目について説明します。

  • レイアウト

  • ホスト ウィンドウへのデータの返却

  • WPF プロパティの設定

レイアウト

WPF コンテンツ内の UI 要素は 5 つの TextBox コントロールで構成され、Name、Address、City、State、および Zip という値をそれぞれ持つ Label コントロールが関連付けられます。また、[OK] と [Cancel] の 2 つの Button コントロールがあります。

WPF コンテンツは WPFPage クラスに実装されます。レイアウトは、Grid レイアウト要素で処理されます。クラスは Grid を継承し、実質的にこれを WPF コンテンツのルート要素とします。

WPF コンテンツのコンストラクタは、要求された幅と高さを受け取り、それに従って Grid のサイズを設定します。次に、ColumnDefinition オブジェクトと RowDefinition オブジェクトのセットを作成し、これらを Grid オブジェクト ベースの ColumnDefinitions コレクションと RowDefinitions コレクションにそれぞれ追加することによって、基本的なレイアウトを定義します。これによって 5 行 7 列のグリッドが定義され、セルの内容によって寸法が決まります。

次にコンストラクタは、Grid に UI 要素を追加します。最初の要素はタイトル テキストです。これは、グリッドの 1 行目で中央揃えにされる Label コントロールです。

次の行には、Name Label コントロールと、それに関連付けられた TextBox コントロールが含まれます。ラベルとテキスト ボックスの各ペアに対して同じコードが使用されるため、このコードがプライベート メソッドのペア内に配置され、ラベルとテキスト ボックスの 5 組のペアすべてに対して使用されます。これらのメソッドは、適切なコントロールを作成し、Grid クラスの静的な SetColumn メソッドと SetRow メソッドを呼び出して、コントロールを適切なセルに配置します。コントロールの作成後、このサンプルでは Add メソッドを GridChildren プロパティに対して呼び出して、コントロールをグリッドに追加します。ラベルとテキスト ボックスの残りのペアを追加するコードも同様です。詳細については、サンプル コードを参照してください。

2 つのメソッドの実装は次のとおりです。

最後に、サンプルでは [OK] および [Cancel] ボタンを追加し、それらの Click イベントにイベント ハンドラをアタッチします。

ホスト ウィンドウへのデータの返却

どちらかのボタンがクリックされると、その Click イベントが発生します。ホスト ウィンドウは、単にこれらのイベントにハンドラをアタッチし、TextBox コントロールから直接データを取得することもできます。サンプルでは、これよりも直接的でない方法を使用しています。WPF コンテンツ内の Click を処理し、続いてカスタム イベント OnButtonClicked を発生させ、WPF コンテンツに通知します。これにより、WPF コンテンツは、ホストへの通知前にパラメータの検証を実行できます。ハンドラが TextBox コントロールからテキストを取得してパブリック プロパティに割り当て、ホストはここから情報を取得できます。

WPFPage.h のイベント宣言は次のとおりです。

WPFPage.cpp の Click イベント ハンドラは次のとおりです。

WPF プロパティの設定

Win32 ホストでは、いくつかの WPF コンテンツ プロパティをユーザーが変更できます。Win32 の側から見ると、これは単なるプロパティの変更です。すべてのコントロールのフォントを制御する単一のグローバル プロパティが存在しないため、WPF コンテンツ クラスでの実装は多少複雑です。その代わり、各コントロールに適したプロパティは、プロパティの set アクセサで変更されます。DefaultFontFamily プロパティのコードを次の例に示します。このプロパティを設定するとプライベート メソッドが呼び出され、このメソッドにより、さまざまなコントロールの FontFamily プロパティが設定されます。

WPFPage.h からの抜粋を次に示します。

WPFPage.cpp からの抜粋を次に示します。

参照

概念

WPF と Win32 の相互運用性に関する概要

参照

HwndSource