WPFコントロール(WPF要素、UIElement)をWindowsフォームに配置(ホスト)するには、ElementHostコントロールを使います。方法は、ElementHost.ChildプロパティにWPFコントロールを割り当てたElementHostコントロールをWindowsフォームに配置するだけです。
ElementHostコントロールはWindowsFormsIntegration.dll内にありますので、使用するにはWindowsFormsIntegration.dllを参照に追加する必要があります。また、WPFコントロールを使用するためにPresentationFramework.dll、PresentationCore.dll、WindowsBase.dllも参照に追加します。さらに.NET Framework 4.0以降では、System.Xaml.dllも追加します。なおDLLを参照に追加する方法は、「「○○○.dllを参照に追加します」の意味は?」をご覧ください。
まずは、Visual Studioのフォームデザイナを使わずに、コードのみで動的にコントロールを配置してみます。なお動的にコントロールを配置する方法については、「コントロールを実行時に作成する」で詳しく説明しています。
下に示す例では、WPFのButtonコントロールをWindowsフォームに配置しています。なおこのコードは、コントロールを配置するフォームクラス内に記述されているものとします。
'WPFコントロールをホストするElementHostコントロール Private elementHost1 As System.Windows.Forms.Integration.ElementHost 'フォームのLoadイベントハンドラ Private Sub Form1_Load(sender As Object, e As System.EventArgs) _ Handles MyBase.Load 'WPFのButtonコントロールを作成する Dim wpfButton As New System.Windows.Controls.Button() wpfButton.Content = "Push!" AddHandler wpfButton.Click, AddressOf wpfButton_Click 'ElementHostコントロールを作成する elementHost1 = New System.Windows.Forms.Integration.ElementHost() 'コントロールの位置と大きさを設定する elementHost1.SetBounds(20, 10, 100, 30) 'ElementHostのChildプロパティにWPFコントロールを設定する elementHost1.Child = wpfButton 'ElementHostをフォームに配置する Me.Controls.Add(elementHost1) End Sub 'WPFのButtonコントロールのClickイベントハンドラ Private Sub wpfButton_Click(sender As Object, _ e As System.Windows.RoutedEventArgs) System.Windows.Forms.MessageBox.Show("ボタンが押されました") End Sub
//WPFコントロールをホストするElementHostコントロール private System.Windows.Forms.Integration.ElementHost elementHost1; //フォームのLoadイベントハンドラ private void Form1_Load(object sender, System.EventArgs e) { //WPFのButtonコントロールを作成する System.Windows.Controls.Button wpfButton = new System.Windows.Controls.Button(); wpfButton.Content = "Push!"; wpfButton.Click += wpfButton_Click; //ElementHostコントロールを作成する elementHost1 = new System.Windows.Forms.Integration.ElementHost(); //コントロールの位置と大きさを設定する elementHost1.SetBounds(20, 10, 100, 30); //ElementHostのChildプロパティにWPFコントロールを設定する elementHost1.Child = wpfButton; //ElementHostをフォームに配置する this.Controls.Add(elementHost1); } //WPFのButtonコントロールのClickイベントハンドラ private void wpfButton_Click( object sender, System.Windows.RoutedEventArgs e) { System.Windows.Forms.MessageBox.Show("ボタンが押されました"); }
ElementHostコントロールによってホストされているWPFコントロールにアクセスするには、ElementHost.ChildプロパティからWPFコントロールを取得すればよいでしょう。しかし、ElementHostコントロールの「プロパティマッピング」という機能を使えば、ElementHostコントロールのプロパティ値を変更することで、WPFコントロールのプロパティ値を変更することができます。例えば、ElementHost.EnabledプロパティをFalseにすると、ホストされているWPFコントロールのIsEnabledプロパティがFalseになり、無効化されます。
このように、プロパティマッピングによってElementHostコントロールとWPFコントロールが連動しているプロパティには、以下の様なものがあります。
補足:このようなプロパティの一覧は、以下のようなコードで確認できます。
Dim host As New System.Windows.Forms.Integration.ElementHost() For Each pn As String In host.PropertyMap.Keys Console.WriteLine(pn) Next host.Dispose()
System.Windows.Forms.Integration.ElementHost host = new System.Windows.Forms.Integration.ElementHost(); foreach (string pn in host.PropertyMap.Keys) { Console.WriteLine(pn); } host.Dispose();
上記以外のプロパティであっても、自分で新たに設定を追加する事もできます。それには、ElementHost.PropertyMapプロパティからプロパティマップを取得して、PropertyMap.Addメソッドを使って設定を追加します。
PropertyMap.Addメソッドの1番目の引数には、ElementHostコントロールのプロパティの名前をString型で指定します。2番目の引数には、そのプロパティに値が設定された時に呼び出されるPropertyTranslatorデリゲートを指定します。
難しい話は置いておいて、具体例を示します。以下の例では、ElementHost.Textプロパティを変更すると、ホストしているButtonコントロールのContentプロパティも変更されるようにしています。
'WPFコントロールをホストするElementHostコントロール Private elementHost1 As System.Windows.Forms.Integration.ElementHost 'フォームのLoadイベントハンドラ Private Sub Form1_Load(sender As Object, e As System.EventArgs) _ Handles MyBase.Load 'ElementHostコントロールを作成する elementHost1 = New System.Windows.Forms.Integration.ElementHost() elementHost1.SetBounds(20, 10, 100, 30) elementHost1.Child = New System.Windows.Controls.Button() 'フォームにElementHostを配置する Me.Controls.Add(elementHost1) 'ElementHostのTextプロパティが変更された時に ' TextPropertyTranslatorメソッドが呼び出されるようにする elementHost1.PropertyMap.Add("Text", _ New System.Windows.Forms.Integration.PropertyTranslator( _ AddressOf Me.TextPropertyTranslator)) 'ElementHost.TextプロパティでButton.Contentプロパティを変更する elementHost1.Text = "押してね" End Sub 'ElementHost.Textプロパティが変更された時に、ホストしている ' ButtonコントロールのContentプロパティを変更するためのメソッド Private Sub TextPropertyTranslator( _ h As Object, propertyName As String, value As Object) 'ElementHostを取得する Dim host As System.Windows.Forms.Integration.ElementHost = _ DirectCast(h, System.Windows.Forms.Integration.ElementHost) If host IsNot Nothing AndAlso host.Child IsNot Nothing Then 'ホストしているButtonコントロールを取得する Dim wpfButton As System.Windows.Controls.Button = _ DirectCast(host.Child, System.Windows.Controls.Button) 'Contentプロパティを変更する wpfButton.Content = value End If End Sub
//WPFコントロールをホストするElementHostコントロール private System.Windows.Forms.Integration.ElementHost elementHost1; //フォームのLoadイベントハンドラ private void Form1_Load(object sender, System.EventArgs e) { //ElementHostコントロールを作成する elementHost1 = new System.Windows.Forms.Integration.ElementHost(); elementHost1.SetBounds(20, 10, 100, 30); elementHost1.Child = new System.Windows.Controls.Button(); //フォームにElementHostを配置する this.Controls.Add(elementHost1); //ElementHostのTextプロパティが変更された時に // TextPropertyTranslatorメソッドが呼び出されるようにする elementHost1.PropertyMap.Add("Text", new System.Windows.Forms.Integration.PropertyTranslator( this.TextPropertyTranslator)); //ElementHost.TextプロパティでButton.Contentプロパティを変更する elementHost1.Text = "押してね"; } //ElementHost.Textプロパティが変更された時に、ホストしている // ButtonコントロールのContentプロパティを変更するためのメソッド private void TextPropertyTranslator( object h, String propertyName, object value) { //ElementHostを取得する System.Windows.Forms.Integration.ElementHost host = (System.Windows.Forms.Integration.ElementHost)h; if (host != null && host.Child != null) { //ホストしているButtonコントロールを取得する System.Windows.Controls.Button wpfButton = (System.Windows.Controls.Button)host.Child; //Contentプロパティを変更する wpfButton.Content = value; } }
このようにマッピングできるプロパティは、ElementHostコントロールが持っているプロパティだけです。ElementHostコントロールにないプロパティ名を追加しようとすると、例外ArgumentExceptionがスローされます。
ElementHostコントロールが持っているプロパティならばどれでもプロパティマップに追加できますが、上の例のTextプロパティのように、プロパティ値を変更すると自動的にPropertyTranslatorデリゲートが実行されるプロパティは限られています。そのようなプロパティは、以下のプロパティだけのようです。
上記以外のプロパティ(プロパティ値を変更しても自動的にPropertyTranslatorデリゲートが実行されないプロパティ)の場合は、プロパティ値を変更した時に自分でPropertyMap.ApplyメソッドやPropertyMap.ApplyAllメソッドを呼び出すか、ElementHost.OnPropertyChangedメソッドを呼び出すことで、PropertyTranslatorデリゲートを実行することができます。
補足:PropertyMap.Addメソッドを呼び出した時は、自動的にPropertyMap.Applyメソッドが呼び出されます。
以下の例では、UseWaitCursorプロパティをプロパティマップに追加して、ElementHost.UseWaitCursorプロパティが変更された時に、ホストしているWPFコントロールのCursorプロパティが変更されるようにしています。ただしUseWaitCursorプロパティの値を変更した後でPropertyMap.Applyメソッドを呼び出さないとPropertyTranslatorは実行されません。そのコードは、Button1のClickイベントハンドラ(Button1_Clickメソッド)内に記述しています。
'WPFコントロールをホストするElementHostコントロール Private elementHost1 As System.Windows.Forms.Integration.ElementHost 'フォームのLoadイベントハンドラ Private Sub Form1_Load(sender As Object, e As System.EventArgs) _ Handles MyBase.Load 'ElementHostコントロールを作成する elementHost1 = New System.Windows.Forms.Integration.ElementHost() elementHost1.SetBounds(20, 10, 100, 30) elementHost1.Child = New System.Windows.Controls.Button() 'フォームにElementHostを配置する Me.Controls.Add(elementHost1) 'ElementHostのUseWaitCursorプロパティが変更された時に ' UseWaitCursorPropertyTranslatorメソッドが呼び出されるようにする elementHost1.PropertyMap.Add("UseWaitCursor", _ New System.Windows.Forms.Integration.PropertyTranslator( _ AddressOf Me.UseWaitCursorPropertyTranslator)) End Sub 'ElementHost.UseWaitCursorプロパティが変更された時に、ホストしている ' ButtonコントロールのCursorプロパティを変更するためのメソッド Private Sub UseWaitCursorPropertyTranslator( _ h As Object, propertyName As String, value As Object) 'ElementHostを取得する Dim host As System.Windows.Forms.Integration.ElementHost = _ DirectCast(h, System.Windows.Forms.Integration.ElementHost) If host IsNot Nothing AndAlso host.Child IsNot Nothing Then 'ホストしているButtonコントロールを取得する Dim wpfButton As System.Windows.Controls.Button = _ DirectCast(host.Child, System.Windows.Controls.Button) 'UseWaitCursorの値に応じて、カーソルを変更する If CBool(value) Then wpfButton.Cursor = System.Windows.Input.Cursors.Wait Else wpfButton.Cursor = Nothing End If End If End Sub 'Button1のClickイベントハンドラ Private Sub Button1_Click(sender As Object, e As System.EventArgs) _ Handles Button1.Click 'ElementHost.UseWaitCursorプロパティの値を変更する elementHost1.UseWaitCursor = Not elementHost1.UseWaitCursor 'UseWaitCursorプロパティが変更されたことをPropertyMapに知らせる elementHost1.PropertyMap.Apply("UseWaitCursor") 'または、次のようにすることもできる 'elementHost1.OnPropertyChanged("UseWaitCursor", _ ' elementHost1.UseWaitCursor) '次のようにすると、PropertyMapに登録されている ' すべてのPropertyTranslatorが実行される 'elementHost1.PropertyMap.ApplyAll() End Sub
//WPFコントロールをホストするElementHostコントロール private System.Windows.Forms.Integration.ElementHost elementHost1; //フォームのLoadイベントハンドラ private void Form1_Load(object sender, System.EventArgs e) { //ElementHostコントロールを作成する elementHost1 = new System.Windows.Forms.Integration.ElementHost(); elementHost1.SetBounds(20, 10, 100, 30); elementHost1.Child = new System.Windows.Controls.Button(); //フォームにElementHostを配置する this.Controls.Add(elementHost1); //ElementHostのUseWaitCursorプロパティが変更された時に // UseWaitCursorPropertyTranslatorメソッドが呼び出されるようにする elementHost1.PropertyMap.Add("UseWaitCursor", new System.Windows.Forms.Integration.PropertyTranslator( this.UseWaitCursorPropertyTranslator)); } //ElementHost.UseWaitCursorプロパティが変更された時に、ホストしている // ButtonコントロールのCursorプロパティを変更するためのメソッド private void UseWaitCursorPropertyTranslator( object h, String propertyName, object value) { //ElementHostを取得する System.Windows.Forms.Integration.ElementHost host = (System.Windows.Forms.Integration.ElementHost)h; if (host != null && host.Child != null) { //ホストしているButtonコントロールを取得する System.Windows.Controls.Button wpfButton = (System.Windows.Controls.Button)host.Child; //UseWaitCursorの値に応じて、カーソルを変更する if ((bool)value) { wpfButton.Cursor = System.Windows.Input.Cursors.Wait; } else { wpfButton.Cursor = null; } } } //Button1のClickイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { //ElementHost.UseWaitCursorプロパティの値を変更する elementHost1.UseWaitCursor = !elementHost1.UseWaitCursor; //UseWaitCursorプロパティが変更されたことをPropertyMapに知らせる elementHost1.PropertyMap.Apply("UseWaitCursor"); //または、次のようにすることもできる //elementHost1.OnPropertyChanged("UseWaitCursor", // elementHost1.UseWaitCursor); //次のようにすると、PropertyMapに登録されている // すべてのPropertyTranslatorが実行される //elementHost1.PropertyMap.ApplyAll(); }
同じソリューション内にあるWPFユーザーコントロールであれば、Visual Studioのフォームデザイナを使って簡単に配置することができます。その手順を紹介します。
同じソリューション内にあるWPFユーザーコントロールであれば上記のようにできますが、それ以外のWPFコントロールの場合は、フォームデザイナを使って配置することはできません。しかし、私が試した所、強引にフォームクラスのInitializeComponentメソッドを書き換えることによって、それらしいことはできました。ただし、正規の方法ではありませんし、InitializeComponentメソッドをコードエディタで書き換えることは危険ですので、本来はすべきではありません。
以下に私がやってみたことを一応書いておきます(Visual Studio 2012)。
Me.ElementHost1.Child = Nothing
this.elementHost1.Child = null;ここを書き換えて、ホストしたいWPFコントロールをElementHost.Childプロパティに割り当てます。例えば、WPFのButtonコントロールをホストする場合は、次のようにします。
Me.ElementHost1.Child = New System.Windows.Controls.Button()
this.elementHost1.Child = new System.Windows.Controls.Button();
Private Sub InitializeComponent() '(省略) Me.Button1 = New System.Windows.Controls.Button() '(省略) Me.ElementHost1.Child = Me.Button1 '(省略) End Sub '(省略) Friend Button1 As System.Windows.Controls.Button
private void InitializeComponent() { //(省略) this.Button1 = new System.Windows.Controls.Button(); //(省略) this.elementHost1.Child = this.Button1; //(省略) } //(省略) private System.Windows.Controls.Button Button1;