共用方式為


HOW TO:建立本身為 UI 的增益集

本範例示範如何建立本身為 Windows Presentation Foundation (WPF) user interface (UI) 並裝載在 WPF 獨立應用程式的增益集 (Add-In)。

此增益集是本身為 WPF 使用者控制項的 UI。 這個使用者控制項的內容是單一按鈕,當按下時,會顯示訊息方塊。 WPF 獨立應用程式會將增益集 UI 裝載為主應用程式視窗的內容。

必要條件

這個範例的重點是 .NET Framework 增益集模型實現此案例的 WPF 擴充,並假設下列項目:

範例

若要建立本身為 WPF UI 的增益集,每個管線區段、增益集和主應用程式都需要特定的程式碼。

實作合約管線區段

當增益集是 UI 時,增益集的合約必須實作 INativeHandleContract。 範例中的 IWPFAddInContract 會實作 INativeHandleContract,如下列程式碼所示。


Imports System.AddIn.Contract ' INativeHandleContract
Imports 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
        Inherits INativeHandleContract
        Inherits IContract
    End Interface
End Namespace
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 類別。


Imports System.AddIn.Pipeline ' AddInBaseAttribute
Imports System.Windows.Controls ' UserControl

Namespace AddInViews
    ''' <summary>
    ''' Defines the add-in's view of the contract.
    ''' </summary>
    <AddInBase>
    Public Class WPFAddInView
        Inherits UserControl
    End Class
End Namespace
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 來執行的,如下列程式碼所示。


Imports System ' IntPtr
Imports System.AddIn.Contract ' INativeHandleContract
Imports System.AddIn.Pipeline ' AddInAdapterAttribute, FrameworkElementAdapters, ContractBase
Imports System.Security.Permissions

Imports AddInViews ' WPFAddInView
Imports 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
        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 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 Function GetHandle() As IntPtr Implements INativeHandleContract.GetHandle
            Return FrameworkElementAdapters.ViewToContractAdapter(Me.wpfAddInView).GetHandle()
        End Function

    End Class
End Namespace
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 的增益集模型 (請參閱 HOW TO:建立傳回 UI 的增益集) 中,增益集配接器是藉由呼叫 ViewToContractAdapter,將 FrameworkElement 轉換為 INativeHandleContract。 這個模型中也必須呼叫 ViewToContractAdapter,雖然您必須實作撰寫程式碼的方法以進行呼叫。 如果呼叫 QueryContract 的程式碼預期為 INativeHandleContract,您可以藉由覆寫 QueryContract 和實作呼叫 ViewToContractAdapter 的程式碼來完成這項作業。 在這個情況下,呼叫端是主應用程式端配接器,在接續的小節中涵蓋其說明。

注意事項注意事項

在這個模型中,您也需要覆寫 QueryContract,才能夠在主應用程式 UI 和增益集 UI 間進行切換。如需詳細資訊,請參閱 WPF 增益集概觀中的<WPF 增益集限制>。

因為增益集端配接器會實作衍生自 INativeHandleContract 的介面,您也需要實作 GetHandle,雖然這在覆寫 QueryContract 時會被忽略。

實作主應用程式檢視管線區段

在這個模型中,主應用程式通常會預期主應用程式檢視是 FrameworkElement 子類別。 在 INativeHandleContract 跨越隔離界限之後,主應用程式端配接器必須將 INativeHandleContract 轉換為 FrameworkElement。 因為主應用程式沒有要呼叫方法以取得 FrameworkElement,主應用程式檢視必須藉由包含 FrameworkElement 以「傳回」該項目。 因此,主應用程式檢視必須衍生自可以包含其他 UIs 的 FrameworkElement 的子類別,例如 UserControl。 下列程式碼顯示合約的主應用程式檢視,實作為 WPFAddInHostView 類別。


Imports System.Windows.Controls ' UserControl

Namespace HostViews
    ''' <summary>
    ''' Defines the host's view of the add-in
    ''' </summary>
    Public Class WPFAddInHostView
        Inherits UserControl
    End Class
End Namespace
using System.Windows.Controls; // UserControl

namespace HostViews
{
    /// <summary>
    /// Defines the host's view of the add-in
    /// </summary>
    public class WPFAddInHostView : UserControl { }
}

實作主應用程式端配接器管線區段

雖然合約是 INativeHandleContract,但主應用程式必須有 UserControl (如主應用程式檢視所指定)。 因此,在跨越隔離界限之後,在設定為主應用程式檢視 (衍生自 UserControl) 的內容之前,INativeHandleContract 必須轉換為 FrameworkElement

這個工作是由主應用程式端配接器來執行的,如下列程式碼所示。


Imports System.AddIn.Contract ' INativeHandleContract
Imports System.AddIn.Pipeline ' HostAdapterAttribute, FrameworkElementAdapters, ContractHandle
Imports System.Windows ' FrameworkElement

Imports Contracts ' IWPFAddInContract
Imports 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
        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
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;
        }
    }
}

如您所見,主應用程式端配接器藉由呼叫增益集端配接器的 QueryContract 方法 (這是 INativeHandleContract 跨越隔離界限的地方),取得 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>

Imports System.AddIn ' AddInAttribute
Imports System.Windows ' MessageBox, RoutedEventArgs

Imports AddInViews ' WPFAddInView

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
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)
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)
// 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 增益集模型程式碼以啟動增益集,該增益集會隱含傳回主應用程式檢視給主應用程式。 主應用程式接續會從 Grid 顯示主應用程式檢視 (為 UserControl)。

用於處理增益集 UI 互動的程式碼是在增益集的應用程式定義域中執行的。 這些互動包括下列項目:

這項活動完全與主應用程式隔離。

請參閱

概念

增益集和擴充性

WPF 增益集概觀