方法 : UI であるアドインを作成する
更新 : 2007 年 11 月
この例では、WPF スタンドアロン アプリケーションによってホストされる、Windows Presentation Foundation (WPF) ユーザー インターフェイス (UI) であるアドインを作成する方法を示します。
このアドインは、WPF ユーザー コントロールである UI です。ユーザー コントロールの中身は単一のボタンで、クリックするとメッセージ ボックスを表示します。WPF スタンドアロン アプリケーションは、メイン アプリケーション ウィンドウのコンテンツとして、アドイン UI をホストします。
必要条件
この例では、このシナリオを実現する .NET Framework アドイン モデルの WPF 拡張機能に焦点を当てます。前提条件は次のとおりです。
パイプライン、アドイン、ホスト開発など、.NET Framework アドイン モデルについて知っていること。これらの概念の詳細については、「アドインの概要」を参照してください。パイプライン、アドイン、およびホスト アプリケーションの実装例を示すチュートリアルについては、「チュートリアル : 拡張性のあるアプリケーションの作成」を参照してください。
.NET Framework アドイン モデルの WPF 拡張機能について知っていること。詳細については、「Windows Presentation Foundation のアドインの概要」を参照してください。
サンプル
このトピックに関連するサンプル全体については、「アドインが UI である場合のサンプル」を参照してください。
使用例
WPF UI であるアドインを作成するには、各パイプライン セグメント、アドイン、およびホスト アプリケーションに特定のコードが必要です。
コントラクト パイプライン セグメントの実装
アドインが UI の場合、アドインのコントラクトに INativeHandleContract を実装する必要があります。次のコード例に示すように、IWPFAddInContract が INativeHandleContract を実装します。
using System.AddIn.Contract; // INativeHandleContract
using System.AddIn.Pipeline; // AddInContractAttribute
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 {}
}
アドイン ビュー パイプライン セグメントの実装
アドインは FrameworkElement 型のサブクラスとして実装されるため、アドイン ビューも FrameworkElement のサブクラスである必要があります。次のコードでは、コントラクトのアドイン ビューが WPFAddInView クラスとして実装されています。
using System.AddIn.Pipeline; // AddInBaseAttribute
using System.Windows.Controls; // UserControl
namespace AddInViews
{
/// <summary>
/// Defines the add-in's view of the contract.
/// </summary>
[AddInBase]
public class WPFAddInView : UserControl { }
}
ここでは、アドイン ビューは UserControl から派生します。したがって、アドイン UI も UserControl から派生している必要があります。
アドイン側アダプタ パイプライン セグメントの実装
コントラクトは INativeHandleContract ですが、アドインは FrameworkElement です (アドイン ビュー パイプライン セグメントの指定のとおり)。したがって、FrameworkElement は、分離境界を越える前に INativeHandleContract に変換される必要があります。この処理は、次のコードに示すように、アドイン側のアダプタで ViewToContractAdapter を呼び出して実行されます。
using System; // IntPtr
using System.AddIn.Contract; // INativeHandleContract
using System.AddIn.Pipeline; // AddInAdapterAttribute, FrameworkElementAdapters, ContractBase
using System.Security.Permissions;
using AddInViews; // WPFAddInView
using Contracts; // IWPFAddInContract
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 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 ie 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>
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public IntPtr GetHandle()
{
return FrameworkElementAdapters.ViewToContractAdapter(this.wpfAddInView).GetHandle();
}
}
}
アドインが UI を返すアドイン モデル (「方法 : UI を返すアドインを作成する」を参照) では、アドイン アダプタは、FrameworkElement から INativeHandleContract に、ViewToContractAdapter 呼び出しによって変換されます。ViewToContractAdapter はこのモデルでも呼び出す必要がありますが、それを呼び出すためのコードを記述するメソッドを実装する必要があります。そのために、QueryContract を呼び出すコードが INativeHandleContract を必要とする場合は、QueryContract をオーバーライドし、ViewToContractAdapter を呼び出すコードを実装します。この場合、呼び出し元はホスト側のアダプタになります。これについては後で説明します。
![]() |
---|
また、このモデルでは QueryContract をオーバーライドして、ホスト アプリケーション UI とアドイン UI との間を Tab キーで移動できるようにする必要があります。詳細については、「Windows Presentation Foundation のアドインの概要」の「WPF アドインの制約」を参照してください。 |
アドイン側のアダプタは、INativeHandleContract から派生したインターフェイスを実装しているため、GetHandle を実装する必要がありますが、これは QueryContract がオーバーライドされた場合には無視されます。
ホスト ビュー パイプライン セグメントの実装
このモデルでは、通常、ホスト アプリケーションはホスト ビューが FrameworkElement サブクラスであることを必要とします。ホスト側のアダプタは、INativeHandleContract が分離境界を越えた後に、INativeHandleContract を FrameworkElement に変換する必要があります。メソッドが FrameworkElement の取得のために、ホスト アプリケーションで呼び出されていないため、ホスト ビューが FrameworkElement を格納して "返す" 必要があります。したがって、ホスト ビューは、UserControl など、他の UI を格納できる FrameworkElement のサブクラスの派生である必要があります。次のコードでは、コントラクトのホスト ビューが WPFAddInHostView クラスとして実装されています。
using System.Windows.Controls; // UserControl
namespace HostViews
{
/// <summary>
/// Defines the host's view of the add-in
/// </summary>
public class WPFAddInHostView : UserControl { }
}
ホスト側アダプタ パイプライン セグメントの実装
コントラクトは INativeHandleContract ですが、ホスト アプリケーションには UserControl が必要です (ホスト ビューの指定のとおり)。したがって、INativeHandleContract は、分離境界を越えた後、ホスト ビュー (UserControl から派生) のコンテンツとして設定される前に、FrameworkElement に変換される必要があります。
この処理は、次のコードに示されているように、ホスト側のアダプタで実行されます。
using System.AddIn.Contract; // INativeHandleContract
using System.AddIn.Pipeline; // HostAdapterAttribute, FrameworkElementAdapters, ContractHandle
using System.Windows; // FrameworkElement
using Contracts; // IWPFAddInContract
using HostViews; // WPFAddInHostView
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;
}
}
}
このことからもわかるように、ホスト側のアダプタは、INativeHandleContract を取得するのに、アドイン側のアダプタの QueryContract メソッドを呼び出します (ここが INativeHandleContract が分離境界を越えるポイントです)。
次に、ホスト側のアダプタが、ContractToViewAdapter を呼び出して、INativeHandleContract を FrameworkElement に変換します。最後に、FrameworkElement がホスト ビューの内容として設定されます。
アドインの実装
アドイン側のアダプタとアドイン ビューが用意できると、次のコードに示すように、アドインをアドイン ビューから派生することで実装できます。
<addInViews:WPFAddInView
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:addInViews="clr-namespace:AddInViews;assembly=AddInViews"
x:Class="WPFAddIn1.AddInUI">
<Grid>
<Button Click="clickMeButton_Click" Content="Click Me!" />
</Grid>
</addInViews:WPFAddInView>
using System.AddIn; // AddInAttribute
using System.Windows; // MessageBox, RoutedEventArgs
using AddInViews; // WPFAddInView
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");
}
}
}
この例から、アドイン開発者はアドインのみを実装し (これが 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);
このホスト アプリケーションでは、典型的な .NET Framework アドイン モデル コードを使用して、アドインをアクティブ化します。このアドインは、暗黙的に、ホスト アプリケーションにホスト ビューを返します。ホスト アプリケーションは、その後、ホスト ビュー (UserControl) を Grid で表示します。
アドイン UI との対話処理を行うコードは、アドインのアプリケーション ドメインで実行されます。該当する対話処理は次のとおりです。
MessageBox の表示。
この動作はホスト アプリケーションから完全に分離されています。