Gewusst wie: Erstellen eines Add-Ins, das eine Benutzeroberfläche ist
Aktualisiert: November 2007
Dieses Beispiel zeigt, wie Sie ein Add-In erstellen, bei dem es sich um eine Windows Presentation Foundation (WPF)-Benutzeroberfläche (user interface, UI) handelt, die von einer eigenständigen WPF-Anwendung gehostet wird.
Das Add-In ist eine Benutzeroberfläche, bei der es sich um ein WPF-Benutzersteuerelement handelt. Der Inhalt des Benutzersteuerelements ist eine einzelne Schaltfläche, bei der ein Meldungsfeld angezeigt wird, wenn Benutzer darauf klicken. Die eigenständige WPF-Anwendung hostet die Add-In-Benutzeroberfläche als Inhalt des Hauptfensters der Anwendung.
Vorbereitungsmaßnahmen
Dieses Beispiel zeigt die WPF-Erweiterungen des .NET Framework-Add-In-Modells, die dieses Szenario ermöglichen. Dabei wird Folgendes vorausgesetzt:
Kenntnis des .NET Framework-Add-In-Modells, einschließlich Pipeline-, Add-In- und Hostentwicklung. Wenn Sie mit diesen Begriffen nicht vertraut sind, finden Sie weitere Informationen unter Übersicht über Add-Ins. Ein Lernprogramm, das die Implementierung einer Pipeline, eines Add-Ins und einer Hostanwendung veranschaulicht, finden Sie unter Exemplarische Vorgehensweise: Erstellen von erweiterbaren Anwendungen.
Kenntnis der WPF-Erweiterungen des .NET Framework-Add-In-Modells. Informationen finden Sie unter: Windows Presentation Foundation – Übersicht über Add-Ins.
Beispiel
Das vollständige Beispiel zu diesem Thema finden Sie unter Add-In ist eine Benutzeroberfläche – Beispiel.
Beispiel
Zum Erstellen eines Add-Ins, bei dem es sich um eine WPF-Benutzeroberfläche handelt, ist spezieller Code für die einzelnen Pipelinesegmente, das Add-In und die Hostanwendung erforderlich.
Implementieren des Vertragspipelinesegments
Handelt es sich bei einem Add-In um eine Benutzeroberfläche, muss der Vertrag für das Add-In INativeHandleContract implementieren. In diesem Beispiel wird durch IWPFAddInContractINativeHandleContract implementiert, wie in folgendem Code gezeigt.
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 {}
}
Implementieren des Add-In-Ansichtspipelinesegments
Da das Add-In als Unterklasse des FrameworkElement-Typs implementiert wird, muss auch die Add-In-Ansicht FrameworkElement als Unterklasse verwenden. Im folgenden Code wird die als WPFAddInView-Klasse implementierte Add-In-Ansicht des Vertrags gezeigt.
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 { }
}
Hier wird die Add-In-Ansicht von UserControl abgeleitet. Daher sollte die Benutzeroberfläche des Add-Ins auch von UserControl abgeleitet werden.
Implementieren des Add-In-seitigen Adapterpipelinesegments
Bei dem Vertrag handelt es sich um INativeHandleContract, das Add-In entspricht jedoch FrameworkElement (wie durch das Add-In-Ansichtspipelinesegment festgelegt). Daher muss FrameworkElement vor dem Überschreiten der Isolationsgrenze in INativeHandleContract umgewandelt werden. Dieser Vorgang wird vom Add-In-seitigen Adapter durch Aufrufen von ViewToContractAdapter ausgeführt, wie im folgenden Code gezeigt.
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();
}
}
}
Im Add-In-Modell, in dem ein Add-In eine Benutzeroberfläche zurückgibt (siehe Gewusst wie: Erstellen eines Add-Ins, das eine Benutzeroberfläche zurückgibt), hat der Add-In-Adapter das FrameworkElement in einen INativeHandleContract umgewandelt, indem ViewToContractAdapter aufgerufen wurde. ViewToContractAdapter muss in diesem Modell ebenfalls aufgerufen werden, obwohl Sie eine Methode implementieren müssen, in der Sie den Code für den Aufruf schreiben müssen. Dazu überschreiben Sie QueryContract und implementieren den Code, der ViewToContractAdapter aufruft, wenn der Code, der QueryContract aufruft, INativeHandleContract erwartet. In diesem Fall ist der Aufrufer der hostseitige Adapter, der in einem nachfolgenden Unterabschnitt behandelt wird.
Tipp
Außerdem müssen Sie in diesem Modell QueryContract überschreiben, damit das Wechseln zwischen der Benutzeroberfläche der Hostanwendung und der Benutzeroberfläche des Add-Ins mit der TAB-TASTE unterstützt wird. Weitere Informationen finden Sie im Abschnitt "Einschränkungen des WPF-Add-Ins" unter Windows Presentation Foundation – Übersicht über Add-Ins.
Da der Add-In-seitige Adapter eine Schnittstelle implementiert, die sich von INativeHandleContract ableitet, müssen Sie auch GetHandle implementieren, obwohl dies beim Überschreiben von QueryContract ignoriert wird.
Implementieren des Host-Ansichtspipelinesegments
In diesem Modell wird normalerweise von der Hostanwendung erwartet, dass die Hostansicht eine FrameworkElement-Unterklasse ist. Der hostseitige Adapter muss INativeHandleContract in FrameworkElement umwandeln, nachdem die Isolationsgrenze von INativeHandleContract überschritten wurde. Da von der Hostanwendung keine Methode aufgerufen wird, um FrameworkElement abzurufen, muss FrameworkElement von der Hostansicht "zurückgegeben" werden, indem es darin enthalten ist. Die Hostansicht muss also von einer Unterklasse von FrameworkElement abgeleitet sein, das andere UIs enthalten kann, z. B. UserControl. Im folgenden Code wird die als WPFAddInHostView-Klasse implementierte Hostansicht des Vertrags gezeigt.
using System.Windows.Controls; // UserControl
namespace HostViews
{
/// <summary>
/// Defines the host's view of the add-in
/// </summary>
public class WPFAddInHostView : UserControl { }
}
Implementieren des hostseitigen Adapterpipelinesegments
Der Vertrag entspricht zwar INativeHandleContract, die Hostanwendung erwartet jedoch UserControl (wie durch die Hostansicht festgelegt). Daher muss INativeHandleContract nach dem Überschreiten der Isolationsgrenze in FrameworkElement umgewandelt werden, bevor er als Inhalt der Hostansicht festgelegt wird (die abgeleitet wird von UserControl).
Dieser Vorgang wird vom hostseitigen Adapter ausgeführt, wie im folgenden Code gezeigt.
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;
}
}
}
Wie Sie sehen, ruft der hostseitige Adapter INativeHandleContract ab, indem er die QueryContract-Methode des Add-In-seitigen Adapters aufruft (an diesem Punkt wird die Isolationsgrenze von INativeHandleContract überschritten).
Anschließend wird INativeHandleContract vom hostseitigen Adapter in FrameworkElement umgewandelt. Dies geschieht durch Aufrufen von ContractToViewAdapter. Schließlich wird FrameworkElement als Inhalt der Hostansicht festgelegt.
Implementieren des Add-Ins
Sind der Add-In-seitige Adapter und die Add-In-Ansicht eingerichtet, kann das Add-In durch Ableiten von der Add-In-Ansicht implementiert werden, wie in folgendem Code gezeigt.
<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");
}
}
}
Dieses Beispiel veranschaulicht einen interessanten Vorteil dieses Modells: Add-In-Entwickler müssen nur das Add-In implementieren (da es auch die Benutzeroberfläche ist) und nicht sowohl eine Add-In-Klasse als auch eine Add-In-Benutzeroberfläche.
Implementieren der Hostanwendung
Nachdem der hostseitige Adapter und die Hostansicht erstellt wurden, kann die Hostanwendung das Add-In-Modell von .NET Framework verwenden, um die Pipeline zu öffnen und eine Hostansicht des Add-Ins abzurufen. Diese Schritte werden im folgenden Code gezeigt.
// 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);
Die Hostanwendung verwendet typischen Code für das Add-In-Modell von .NET Framework, um das Add-In zu aktivieren, das die Hostansicht implizit an die Hostanwendung zurückgibt. Daraufhin zeigt die Hostanwendung die Hostansicht (bei der es sich um UserControl handelt) aus einem Grid an.
Der Code zum Verarbeiten von Interaktionen mit der Benutzeroberfläche des Add-Ins wird in der Anwendungsdomäne des Add-Ins ausgeführt. Diese Interaktionen umfassen Folgendes:
Anzeigen von MessageBox
Diese Aktivität ist von der Hostanwendung vollständig isoliert.
Siehe auch
Aufgaben
Add-In ist eine Benutzeroberfläche – Beispiel