この例では、WPF スタンドアロン アプリケーションによってホストされる Windows Presentation Foundation (WPF) であるアドインを作成する方法を示します。
アドインは、WPF ユーザー コントロールである UI です。 ユーザー コントロールの内容は、クリックするとメッセージ ボックスを表示する 1 つのボタンです。 WPF スタンドアロン アプリケーションは、メイン アプリケーション ウィンドウのコンテンツとしてアドイン UI をホストします。
前提条件
この例では、このシナリオを有効にする .NET Framework アドイン モデルへの WPF 拡張機能を強調表示し、次のことを前提としています。
パイプライン、アドイン、ホスト開発など、.NET Framework アドイン モデルに関する知識。 これらの概念に慣れていない場合は、「アドインと機能拡張の」を参照してください。 パイプライン、アドイン、およびホスト アプリケーションの実装を示すチュートリアルについては、「チュートリアル: 拡張可能なアプリケーションの作成」を参照してください。
.NET Framework アドイン モデルに対する WPF 拡張機能に関する知識。 「WPF のアドインの概要」を参照してください。
例
WPF UI であるアドインを作成するには、パイプライン セグメント、アドイン、ホスト アプリケーションごとに特定のコードが必要です。
コントラクト パイプライン セグメントの実装
アドインが UI の場合、アドインのコントラクトは INativeHandleContractを実装する必要があります。 この例では、次のコードに示すように、IWPFAddInContract
INativeHandleContractを実装しています。
using System.AddIn.Contract;
using System.AddIn.Pipeline;
namespace Contracts
{
/// <summary>
/// Defines the services that an add-in will provide to a host application.
/// In this case, the add-in is a UI.
/// </summary>
[AddInContract]
public interface IWPFAddInContract : INativeHandleContract {}
}
Imports System.AddIn.Contract
Imports System.AddIn.Pipeline
Namespace Contracts
''' <summary>
''' Defines the services that an add-in will provide to a host application.
''' In this case, the add-in is a UI.
''' </summary>
<AddInContract>
Public Interface IWPFAddInContract
Inherits INativeHandleContract
Inherits IContract
End Interface
End Namespace
アドイン ビュー パイプライン セグメントの実装
アドインは FrameworkElement 型のサブクラスとして実装されるため、アドイン ビューは FrameworkElementサブクラス化する必要もあります。 次のコードは、WPFAddInView
クラスとして実装されたコントラクトのアドイン ビューを示しています。
using System.AddIn.Pipeline;
using System.Windows.Controls;
namespace AddInViews
{
/// <summary>
/// Defines the add-in's view of the contract.
/// </summary>
[AddInBase]
public class WPFAddInView : UserControl { }
}
Imports System.AddIn.Pipeline
Imports System.Windows.Controls
Namespace AddInViews
''' <summary>
''' Defines the add-in's view of the contract.
''' </summary>
<AddInBase>
Public Class WPFAddInView
Inherits UserControl
End Class
End Namespace
ここでは、アドイン ビューは UserControlから派生しています。 その結果、アドイン UI も UserControlから派生する必要があります。
Add-In-Side アダプター パイプライン セグメントの実装を行う
コントラクトは INativeHandleContractですが、アドインは FrameworkElement です (アドイン ビュー パイプライン セグメントで指定されています)。 したがって、分離境界を越える前に、FrameworkElement を INativeHandleContract に変換する必要があります。 この作業は、次のコードに示すように、ViewToContractAdapterを呼び出すことによってアドイン側アダプターによって実行されます。
using System;
using System.AddIn.Contract;
using System.AddIn.Pipeline;
using System.Security.Permissions;
using AddInViews;
using Contracts;
namespace AddInSideAdapters
{
/// <summary>
/// Adapts the add-in's view of the contract to the add-in contract
/// </summary>
[AddInAdapter]
public class WPFAddIn_ViewToContractAddInSideAdapter : ContractBase, IWPFAddInContract
{
WPFAddInView wpfAddInView;
public WPFAddIn_ViewToContractAddInSideAdapter(WPFAddInView wpfAddInView)
{
// Adapt the add-in view of the contract (WPFAddInView)
// to the contract (IWPFAddInContract)
this.wpfAddInView = wpfAddInView;
}
/// <summary>
/// ContractBase.QueryContract must be overridden to:
/// * Safely return a window handle for an add-in UI to the host
/// application's application.
/// * Enable tabbing between host application UI and add-in UI, in the
/// "add-in is a UI" scenario.
/// </summary>
public override IContract QueryContract(string contractIdentifier)
{
if (contractIdentifier.Equals(typeof(INativeHandleContract).AssemblyQualifiedName))
{
return FrameworkElementAdapters.ViewToContractAdapter(this.wpfAddInView);
}
return base.QueryContract(contractIdentifier);
}
/// <summary>
/// GetHandle is called by the WPF add-in model from the host application's
/// application ___domain to get the window handle for an add-in UI from the
/// add-in's application ___domain. GetHandle is called if a window handle isn't
/// returned by other means, that is, overriding ContractBase.QueryContract,
/// as shown above.
/// NOTE: This method requires UnmanagedCodePermission to be called
/// (full-trust by default), to prevent illegal window handle
/// access in partially trusted scenarios. If the add-in could
/// run in a partially trusted application ___domain
/// (eg AddInSecurityLevel.Internet), you can safely return a window
/// handle by overriding ContractBase.QueryContract, as shown above.
/// </summary>
public IntPtr GetHandle()
{
return FrameworkElementAdapters.ViewToContractAdapter(this.wpfAddInView).GetHandle();
}
}
}
Imports System
Imports System.AddIn.Contract
Imports System.AddIn.Pipeline
Imports System.Security.Permissions
Imports AddInViews
Imports Contracts
Namespace AddInSideAdapters
''' <summary>
''' Adapts the add-in's view of the contract to the add-in contract
''' </summary>
<AddInAdapter>
Public Class WPFAddIn_ViewToContractAddInSideAdapter
Inherits ContractBase
Implements IWPFAddInContract
Private wpfAddInView As WPFAddInView
Public Sub New(ByVal wpfAddInView As WPFAddInView)
' Adapt the add-in view of the contract (WPFAddInView)
' to the contract (IWPFAddInContract)
Me.wpfAddInView = wpfAddInView
End Sub
''' <summary>
''' ContractBase.QueryContract must be overridden to:
''' * Safely return a window handle for an add-in UI to the host
''' application's application.
''' * Enable tabbing between host application UI and add-in UI, in the
''' "add-in is a UI" scenario.
''' </summary>
Public Overrides Function QueryContract(ByVal contractIdentifier As String) As IContract
If contractIdentifier.Equals(GetType(INativeHandleContract).AssemblyQualifiedName) Then
Return FrameworkElementAdapters.ViewToContractAdapter(Me.wpfAddInView)
End If
Return MyBase.QueryContract(contractIdentifier)
End Function
''' <summary>
''' GetHandle is called by the WPF add-in model from the host application's
''' application ___domain to get the window handle for an add-in UI from the
''' add-in's application ___domain. GetHandle is called if a window handle isn't
''' returned by other means, that is, overriding ContractBase.QueryContract,
''' as shown above.
''' </summary>
Public Function GetHandle() As IntPtr Implements INativeHandleContract.GetHandle
Return FrameworkElementAdapters.ViewToContractAdapter(Me.wpfAddInView).GetHandle()
End Function
End Class
End Namespace
アドインが UI を返すアドイン モデル (「UIを返す Add-In を作成する」を参照) では、アドイン アダプターは FrameworkElementを呼び出して INativeHandleContract を ViewToContractAdapter に変換しました。 ViewToContractAdapter もこのモデルで呼び出す必要がありますが、呼び出すコードを記述するメソッドを実装する必要があります。 これを行うには、QueryContract をオーバーライドし、ViewToContractAdapter を呼び出しているコードが QueryContractを想定している場合に INativeHandleContract を呼び出すコードを実装します。 この場合、呼び出し元はホスト側アダプターになり、後続のサブセクションで説明します。
注
また、ホスト アプリケーション UI とアドイン UI の間でタブ移動を有効にするには、このモデルの QueryContract をオーバーライドする必要があります。 詳細については、「WPF Add-Ins の概要」の「WPF Add-In の制限事項」を参照してください。
アドイン側アダプターは INativeHandleContractから派生するインターフェイスを実装するため、GetHandleも実装する必要がありますが、QueryContract がオーバーライドされた場合は無視されます。
ホスト ビュー パイプライン セグメントの実装
このモデルでは、ホスト アプリケーションは通常、ホスト ビューが FrameworkElement サブクラスであると想定しています。 ホスト側アダプターは、INativeHandleContract が分離境界を越えた後、FrameworkElement を INativeHandleContract に変換する必要があります。
FrameworkElementを取得するためにホスト アプリケーションによってメソッドが呼び出されていないので、ホスト ビューは FrameworkElement を含めることで "戻す" 必要があります。 その結果、ホスト ビューは、FrameworkElementなどの他の UI を含むことができる UserControl のサブクラスから派生する必要があります。 次のコードは、WPFAddInHostView
クラスとして実装されたコントラクトのホスト ビューを示しています。
using System.Windows.Controls;
namespace HostViews
{
/// <summary>
/// Defines the host's view of the add-in
/// </summary>
public class WPFAddInHostView : UserControl { }
}
Imports System.Windows.Controls
Namespace HostViews
''' <summary>
''' Defines the host's view of the add-in
''' </summary>
Public Class WPFAddInHostView
Inherits UserControl
End Class
End Namespace
Host-Side アダプター パイプライン セグメントの実装
コントラクトは INativeHandleContractですが、ホスト アプリケーションは (ホスト ビューで指定された) UserControl を受け取ります。 したがって、INativeHandleContract は、分離境界を越えた後、ホスト ビュー (FrameworkElementから派生) のコンテンツとして設定される前に、UserControl に変換する必要があります。
この作業は、次のコードに示すように、ホスト側アダプターによって実行されます。
using System.AddIn.Contract;
using System.AddIn.Pipeline;
using System.Windows;
using Contracts;
using HostViews;
namespace HostSideAdapters
{
/// <summary>
/// Adapts the add-in contract to the host's view of the add-in
/// </summary>
[HostAdapter]
public class WPFAddIn_ContractToViewHostSideAdapter : WPFAddInHostView
{
IWPFAddInContract wpfAddInContract;
ContractHandle wpfAddInContractHandle;
public WPFAddIn_ContractToViewHostSideAdapter(IWPFAddInContract wpfAddInContract)
{
// Adapt the contract (IWPFAddInContract) to the host application's
// view of the contract (WPFAddInHostView)
this.wpfAddInContract = wpfAddInContract;
// Prevent the reference to the contract from being released while the
// host application uses the add-in
this.wpfAddInContractHandle = new ContractHandle(wpfAddInContract);
// Convert the INativeHandleContract for the add-in UI that was passed
// from the add-in side of the isolation boundary to a FrameworkElement
string aqn = typeof(INativeHandleContract).AssemblyQualifiedName;
INativeHandleContract inhc = (INativeHandleContract)wpfAddInContract.QueryContract(aqn);
FrameworkElement fe = (FrameworkElement)FrameworkElementAdapters.ContractToViewAdapter(inhc);
// Add FrameworkElement (which displays the UI provided by the add-in) as
// content of the view (a UserControl)
this.Content = fe;
}
}
}
Imports System.AddIn.Contract
Imports System.AddIn.Pipeline
Imports System.Windows
Imports Contracts
Imports HostViews
Namespace HostSideAdapters
''' <summary>
''' Adapts the add-in contract to the host's view of the add-in
''' </summary>
<HostAdapter>
Public Class WPFAddIn_ContractToViewHostSideAdapter
Inherits WPFAddInHostView
Private wpfAddInContract As IWPFAddInContract
Private wpfAddInContractHandle As ContractHandle
Public Sub New(ByVal wpfAddInContract As IWPFAddInContract)
' Adapt the contract (IWPFAddInContract) to the host application's
' view of the contract (WPFAddInHostView)
Me.wpfAddInContract = wpfAddInContract
' Prevent the reference to the contract from being released while the
' host application uses the add-in
Me.wpfAddInContractHandle = New ContractHandle(wpfAddInContract)
' Convert the INativeHandleContract for the add-in UI that was passed
' from the add-in side of the isolation boundary to a FrameworkElement
Dim aqn As String = GetType(INativeHandleContract).AssemblyQualifiedName
Dim inhc As INativeHandleContract = CType(wpfAddInContract.QueryContract(aqn), INativeHandleContract)
Dim fe As FrameworkElement = CType(FrameworkElementAdapters.ContractToViewAdapter(inhc), FrameworkElement)
' Add FrameworkElement (which displays the UI provided by the add-in) as
' content of the view (a UserControl)
Me.Content = fe
End Sub
End Class
End Namespace
ご覧のように、ホスト側アダプターは、アドイン側アダプターの INativeHandleContract メソッドを呼び出すことによって QueryContract を取得します (これは、INativeHandleContract が分離境界を越えるポイントです)。
その後、ホスト側アダプターは、INativeHandleContractを呼び出して FrameworkElement を ContractToViewAdapter に変換します。 最後に、FrameworkElement はホスト ビューのコンテンツとして設定されます。
Add-In の実装
アドイン側アダプターとアドイン ビューを配置すると、次のコードに示すように、アドイン ビューから派生してアドインを実装できます。
using System.AddIn;
using System.Windows;
using AddInViews;
namespace WPFAddIn1
{
/// <summary>
/// Implements the add-in by deriving from WPFAddInView
/// </summary>
[AddIn("WPF Add-In 1")]
public partial class AddInUI : WPFAddInView
{
public AddInUI()
{
InitializeComponent();
}
void clickMeButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hello from WPFAddIn1");
}
}
}
Imports System.AddIn
Imports System.Windows
Imports AddInViews
Namespace WPFAddIn1
''' <summary>
''' Implements the add-in by deriving from WPFAddInView
''' </summary>
<AddIn("WPF Add-In 1")>
Partial Public Class AddInUI
Inherits WPFAddInView
Public Sub New()
InitializeComponent()
End Sub
Private Sub clickMeButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
MessageBox.Show("Hello from WPFAddIn1")
End Sub
End Class
End Namespace
この例から、このモデルの興味深い利点の 1 つを確認できます。アドインの開発者は、アドイン クラスとアドイン UI の両方ではなく、(UI であるため) アドインを実装するだけで済みます。
ホスト アプリケーションの実装
ホスト側アダプターとホスト ビューを作成すると、ホスト アプリケーションは .NET Framework アドイン モデルを使用してパイプラインを開き、アドインのホスト ビューを取得できます。 これらの手順を次のコードに示します。
// Get add-in pipeline folder (the folder in which this application was launched from)
string appPath = Environment.CurrentDirectory;
// Rebuild visual add-in pipeline
string[] warnings = AddInStore.Rebuild(appPath);
if (warnings.Length > 0)
{
string msg = "Could not rebuild pipeline:";
foreach (string warning in warnings) msg += "\n" + warning;
MessageBox.Show(msg);
return;
}
// Activate add-in with Internet zone security isolation
Collection<AddInToken> addInTokens = AddInStore.FindAddIns(typeof(WPFAddInHostView), appPath);
AddInToken wpfAddInToken = addInTokens[0];
this.wpfAddInHostView = wpfAddInToken.Activate<WPFAddInHostView>(AddInSecurityLevel.Internet);
// Display add-in UI
this.addInUIHostGrid.Children.Add(this.wpfAddInHostView);
' Get add-in pipeline folder (the folder in which this application was launched from)
Dim appPath As String = Environment.CurrentDirectory
' Rebuild visual add-in pipeline
Dim warnings() As String = AddInStore.Rebuild(appPath)
If warnings.Length > 0 Then
Dim msg As String = "Could not rebuild pipeline:"
For Each warning As String In warnings
msg &= vbLf & warning
Next warning
MessageBox.Show(msg)
Return
End If
' Activate add-in with Internet zone security isolation
Dim addInTokens As Collection(Of AddInToken) = AddInStore.FindAddIns(GetType(WPFAddInHostView), appPath)
Dim wpfAddInToken As AddInToken = addInTokens(0)
Me.wpfAddInHostView = wpfAddInToken.Activate(Of WPFAddInHostView)(AddInSecurityLevel.Internet)
' Display add-in UI
Me.addInUIHostGrid.Children.Add(Me.wpfAddInHostView)
ホスト アプリケーションは、一般的な .NET Framework アドイン モデル コードを使用してアドインをアクティブ化し、ホスト ビューをホスト アプリケーションに暗黙的に返します。 その後、ホスト アプリケーションは、UserControlからホスト ビュー (Grid) を表示します。
アドイン UI との対話を処理するためのコードは、アドインのアプリケーション ドメインで実行されます。 これらの相互作用には、次のものが含まれます。
MessageBoxを表示しています。
このアクティビティは、ホスト アプリケーションから完全に分離されます。
こちらも参照ください
.NET Desktop feedback