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:
Kennis van het .NET Framework-invoegtoepassingsmodel, waaronder pijplijn, invoegtoepassing en hostontwikkeling. Als u niet bekend bent met deze concepten, raadpleegt u invoegtoepassingen en uitbreidbaarheid. Zie Walkthrough: Een uitbreidbare toepassing makenvoor een zelfstudie waarin de implementatie van een pijplijn, een invoegtoepassing en een hosttoepassing wordt gedemonstreerd.
Kennis van de WPF-extensies voor het .NET Framework-invoegtoepassingsmodel. Zie WPF Add-Ins Overview.
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 IWPFAddInContract
INativeHandleContract, 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:
Het MessageBoxweergeven.
Deze activiteit is volledig geïsoleerd van de hosttoepassing.
Zie ook
.NET Desktop feedback