Delen via


Procedure: een Add-In maken die een gebruikersinterface is

In dit voorbeeld ziet u hoe u een invoegtoepassing maakt die een WPF (Windows Presentation Foundation) is die wordt gehost door een zelfstandige WPF-toepassing.

De invoegtoepassing is een UI die een WPF-gebruikersbesturingselement is. De inhoud van het gebruikersbeheer is één knop die, wanneer erop wordt geklikt, een berichtvak weergeeft. De zelfstandige WPF-toepassing host de gebruikersinterface van de invoegtoepassing als inhoud van het hoofdvenster.

vereisten

In dit voorbeeld komen de WPF-extensies van het .NET Framework-invoegtoepassingsmodel naar voren die dit scenario mogelijk maken, en wordt ervan uitgegaan dat het volgende van toepassing is:

Voorbeeld

Voor het maken van een invoegtoepassing die een WPF-gebruikersinterface is, is specifieke code vereist voor elk pijplijnsegment, de invoegtoepassing en de hosttoepassing.

Het contractpijplijnsegment implementeren

Wanneer een invoegtoepassing een gebruikersinterface is, moet het contract voor de invoegtoepassing INativeHandleContractimplementeren. In het voorbeeld implementeert IWPFAddInContractINativeHandleContract, zoals wordt weergegeven in de volgende code.

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

Het Add-In-kijkpijplijnsegment implementeren

Omdat de invoegtoepassing als een subklasse van het type FrameworkElement is geïmplementeerd, moet de invoegtoepassingsweergave ook een subklasse van FrameworkElementzijn. De volgende code toont de invoegtoepassingsweergave van het contract, geïmplementeerd als de WPFAddInView-klasse.

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

Hier is de invoegtoepassingsinterface afgeleid van UserControl. Daarom moet de gebruikersinterface van de invoegtoepassing ook worden afgeleid van UserControl.

Het implementeren van het pijplijnsegment 'Add-In-Side Adapter'

Hoewel het contract een INativeHandleContractis, is de invoegtoepassing een FrameworkElement (zoals opgegeven door het pijplijnsegment van de invoegtoepassingsweergave). Daarom moet de FrameworkElement worden geconverteerd naar een INativeHandleContract voordat de isolatiegrens wordt overschreden. Dit werk wordt uitgevoerd door de invoegtoepassingsadapter door ViewToContractAdapteraan te roepen, zoals wordt weergegeven in de volgende code.

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

In het invoegtoepassingsmodel waarin een invoegtoepassing een gebruikersinterface retourneert (zie Een Add-In maken die een ui-retourneert), heeft de invoegtoepassingsadapter de FrameworkElement geconverteerd naar een INativeHandleContract door ViewToContractAdapteraan te roepen. ViewToContractAdapter moet ook in dit model worden aangeroepen, hoewel u een methode moet implementeren waaruit de code moet worden geschreven om deze aan te roepen. U doet dit door QueryContract te overschrijven en de code te implementeren die ViewToContractAdapter aanroept als de code die QueryContract aanroept, een INativeHandleContractverwacht. In dit geval is de aanroeper de adapter aan de hostzijde, die in een volgende subsectie wordt behandeld.

Notitie

U moet ook QueryContract in dit model overschrijven om tabbing mogelijk te maken tussen de gebruikersinterface van de hosttoepassing en de gebruikersinterface van de invoegtoepassing. Zie 'WPF Add-In Limitations' in WPF Add-Ins Overviewvoor meer informatie.

Omdat de invoegtoepassingsadapter een interface implementeert die is afgeleid van INativeHandleContract, moet u ook GetHandleimplementeren, hoewel dit wordt genegeerd wanneer QueryContract wordt overschreven.

Het implementeren van het pijplijnsegment voor hostweergave

In dit model verwacht de hosttoepassing doorgaans dat de hostweergave een FrameworkElement subklasse is. De host-side adapter moet de INativeHandleContract converteren naar een FrameworkElement nadat de INativeHandleContract de isolatiegrens overschrijdt. Omdat een methode niet wordt aangeroepen door de hosttoepassing om de FrameworkElementop te halen, moet de hostweergave de FrameworkElement retourneren door deze te bevatten. De hostweergave moet daarom worden afgeleid van een subklasse van FrameworkElement die andere UIs kan bevatten, zoals UserControl. De volgende code toont de hostweergave van het contract, geïmplementeerd als de WPFAddInHostView klasse.

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

Het Host-Side-adapterpijplijnsegment implementeren

Hoewel het contract een INativeHandleContractis, verwacht de hosttoepassing een UserControl (zoals opgegeven door de hostweergave). Daarom moet de INativeHandleContract worden geconverteerd naar een FrameworkElement nadat de isolatiegrens is overschreden, voordat deze wordt ingesteld als inhoud van de hostweergave (die is afgeleid van UserControl).

Dit werk wordt uitgevoerd door de adapter aan de hostzijde, zoals wordt weergegeven in de volgende code.

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

Zoals u ziet, verkrijgt de hostzijde-adapter de INativeHandleContract door de QueryContract-methode van de add-in-adapter aan te roepen (dit is het punt waar de INativeHandleContract de isolatiegrens overschrijdt).

De adapter aan de hostzijde converteert vervolgens de INativeHandleContract naar een FrameworkElement door ContractToViewAdapteraan te roepen. Ten slotte wordt de FrameworkElement ingesteld als de inhoud van de hostweergave.

De Add-In implementeren

Met de add-in-side adapter en invoegtoepassingsweergave op hun plaats, kan de invoegtoepassing worden geïmplementeerd door afleiding van de invoegtoepassingsweergave, zoals wordt weergegeven in de volgende code.

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

In dit voorbeeld ziet u een interessant voordeel van dit model: ontwikkelaars van invoegtoepassingen hoeven alleen de invoegtoepassing te implementeren (omdat dit ook de gebruikersinterface is), in plaats van zowel een invoegtoepassingsklasse als een gebruikersinterface voor invoegtoepassingen.

De implementatie van de hostapplicatie

Wanneer de hostadapter en de hostweergave zijn gemaakt, kan de hosttoepassing het .NET Framework-invoegtoepassingsmodel gebruiken om de pijplijn te openen en een hostweergave van de invoegtoepassing te verkrijgen. Deze stappen worden weergegeven in de volgende code.

// 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)

De hosttoepassing maakt gebruik van typische .NET Framework-invoegtoepassingsmodelcode om de invoegtoepassing te activeren, die impliciet de hostweergave retourneert naar de hosttoepassing. De hosttoepassing toont vervolgens de hostweergave (een UserControl) uit een Grid.

De code voor het verwerken van interacties met de gebruikersinterface van de invoegtoepassing wordt uitgevoerd in het toepassingsdomein van de invoegtoepassing. Deze interacties omvatten het volgende:

Deze activiteit is volledig geïsoleerd van de hosttoepassing.

Zie ook