如何:建立本身為 UI 的增益集
此範例顯示如何建立由 WPF 獨立應用程式裝載的 Windows Presentation Foundation (WPF) 增益集。
此增益集是一個 WPF 用戶控制項的 UI。 此使用者控制項的內容是單一按鈕,當按下時,會顯示訊息方塊。 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。
實作增益集端配接器管線區段
合約為 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 的增益集),增益集配接器透過呼叫 ViewToContractAdapter,將 FrameworkElement 轉換成 INativeHandleContract。 ViewToContractAdapter 也必須在此模型中呼叫,不過您需要實作方法,以便撰寫呼叫程式碼。 您可以覆寫 QueryContract並實作呼叫程式碼ViewToContractAdapter,如果程式碼呼叫 QueryContract 預期 INativeHandleContract。 在此情況下,呼叫端會是主應用程式端配接器,後續小節將進行說明。
注意
您也需要覆寫此模型中的 QueryContract,以啟用主應用程式 UI 和增益集 UI 間的索引標籤。 如需詳細資訊,請參閱 WPF 增益集概觀中的<WPF 增益集限制>。
由於增益集端配接器會實作衍生自 INativeHandleContract 的介面,因此您也需要實作 GetHandle,雖然覆寫 QueryContract 時已略過。
實作主應用程式檢視管線區段
在此模型中,主應用程式通常會預期主應用程式檢視是 FrameworkElement 子類別。 主應用程式端配接器必須在 INativeHandleContract 跨越隔離邊界後,將 INativeHandleContract 轉換成 FrameworkElement。 由於主應用程式未呼叫方法來取得 FrameworkElement,因此主應用程式檢視必須透過包含 FrameworkElement 來「傳回」。 因此,主應用程式檢視必須衍生自可包含其他 UI 的 FrameworkElement 子類別,例如 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
實作主應用程式端配接器管線區段
雖然合約是 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
如您所見,主應用程式端配接器會藉由呼叫增益集端配接器的 QueryContract 方法來取得 INativeHandleContract (這是 INativeHandleContract 跨越隔離邊界的點)。
然後,主應用程式端配接器會呼叫 ContractToViewAdapter,將 INativeHandleContract 轉換成 FrameworkElement。 最後,FrameworkElement 會設定為主應用程式檢視的內容。
實作增益集
增益集端配接器和增益集檢視就定位之後,增益集可以藉由衍生自增益集檢視來實作,如下列程式碼所示。
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
在此範例中,您會看到此模型一個令人關注的優點:那就是增益集開發人員只需要實作增益集 (因為它也是 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 增益集模型程式碼來啟動增益集,這會隱含地將主應用程式檢視傳回給主應用程式。 主應用程式隨後會顯示來自 Grid 的主應用程式檢視 (這是 UserControl)。
處理增益集 UI 互動的程式碼是在增益集的應用程式定義域中執行。 這些互動包括:
顯示 MessageBox。
此活動完全與主應用程式隔離。