WPF コントロールを Widnows フォームアプリケーションで使う

Last Updated 2011/09/21


Windows フォームコントロールを WPF アプリケーションで使うよりも WPF コントロールを Windows フォームアプリケーションで使いたいという希望は多いのではないかと思います。というのは、Windows フォームアプリケーションで扱えないアニメーションや 3D グラフィックスが使えるからです。


WPF は Microsoft がいうところのユーザーエクスペリエンスなユーザーインターフェースを構築する機能を提供します。フォームの背景を半透明やグラデーション表示したり、コントロールの外観を自由にデザインできるなどに加えて、アニメーションや 3D グラフィックスもサポートします。

ユーザーエクスペリエンスなユーザーインターフェースとはどんなものかはナカナカ難しい問題で、いくつかの試みはあるものの、実用的なアプリケーションとして公開されているものはいまだ見たことがありません。

某 WEB サイトをのぞいたところ、近い将来のアプリケーションを WPF で開発を予定している人は 15 % 程度との記述がありました。つまり、多くの人はユーザーエクスペリエンスなユーザーインターフェースとは何かについての答えを見出せていないということなのでしょう。

Note これぞユーザーエクスペリエンスなユーザーインターフェースというアプリケーションがあらわれれば一気に WPF 化を促進するのではないかと思います。それがいつなのかは時間の問題ではないでしょうか。

WPF を敬遠する理由の一つはプログラミングスタイルが従来のスタイルとはあまりに異なる点にあると思います。一方、チョットしたアニメーションや 3D グラフィックスには興味はあるといったところではないでしょうか。つまり、アプリケーションの開発は従来の .Net Framework の枠内で行い、一部に WPF コントロールを利用できれば都合がいいという人が多いのではないかと想像します。そこで、このページでは WPF コントロールを Windows フォームアプリケーションで利用する手順について説明します。


ElementHost クラス

Windows フォームコントロールを WPF アプリケーションで使う場合は、コントロールをホストするために、WindowsFormsHost クラスを使います。同様に、WPF コントロールを Windows フォームアプリケーションで使う場合は、コントロールをホストするために ElementHost クラスを使います。

私は現在 Visual Studio 2008/2010 を使っていますが、Windows フォームアプリケーションのプロジェクトを開くと、ツールボックスの中に [WPF 相互運用機能] というページがあって、その中に [ElementHost] があります。これをフォームに配置し、タスクウインドウを開いて [ホストするコンテンツの編集] を選択すると、下図のようにホストすべき WPF コントロールがリストアップされます。ここでは UserControl1 になっていますね。

ElementHost

リストの中から UserContol1 を選択すると、UserControl1 が表示されますので、配置した elementHost1 の位置とサイズを適当に設定してください。これで手続きは終了です。下図は、WPF で作成した UserControl1 を配置した Windows フォームアプリケーションを実行したところです。

ElementHostTest

UserContol1 は立方体をアニメーションするものです。

ところで、ElementHost コントロール内の WPF コントロールは ElementHost コントロールのサイズ一杯になるはずです。というのは、ElementoHost コントロールは内部的に DockPanel コントロールを使っているようで、これが正しいとすると DockPanel コントロールと同じような動作をするはずです。つまり、DockPanel コントロールに最後に追加した子要素は領域のサイズ一杯に拡大されます。ElementHost コントロールは子要素を一つしか持てませんので、WPF コントロールは常に最後に追加した子要素になります。したがって、WPF コントロールの位置とサイズは ElementHost コントロールのそれに影響されることになります。これで不都合な場合の対処法は次の項目の PropertyMap プロパティで説明します。

PropertyMap プロパティ

ElementHost クラスの中に PropertyMap プロパティがあります。このプロパティは、ElementHost オブジェクトがホストする WPF 要素のプロパティに Windows フォームのプロパティを割り当てる方法を指定するものです。

たとえば、WPF コントロールがアニメーションなどによってサイズが大きくなる場合、WPF コントロールはコンテナである ElementHost コントロールのサイズでクリップ(ElementHost コントロールの外側は描画されない)されてしまいます。そこで、Margin プロパティを設定して、WPF コントロール全体が表示されるようにしておかなければなりません。

つまり、WPF の FrameworkElement クラスの Margin プロパティは System.Windows.Thickness 型ですが、System.Windows.Forms.Control クラスの Margin プロパティは System.Windows.Forms.Padding 型です。WPF フォームアプリケーションにおいて、WPF 要素の Margin プロパティを設定するときに Padding 型で設定しようとすると、それを WPF 要素側では受け付けないので、Thickness 型に変換してから設定しなければなりません。この手順をこのプロパティの戻り値である PropertyMap オブジェクトを使って設定します。

private void Form1_Load(object sender, EventArgs e)
{
  ....

  elementHost1.PropertyMap.Add("Margin", new PropertyTranslator(this.MarginChange));
  elementHost1.Margin = new Padding(0, 10, 0, 10);

  ....
}

// PropertyTranslator デリゲート
private void MarginChange(object h, string propertyName, object value)
{
  ElementHost host = h as ElementHost;
  Padding p = (Padding)value;

  System.Windows.Thickness margin = new System.Windows.Thickness(p.Left, p.Top, p.Right, p.Bottom);

  UserControl1 control = elementHost1.Child as UserControl1;

  if (control != null)
    control.Margin = margin;
}

−以上−