How to: Create an Add-In That Returns a UI

This example shows how to create an add-in that returns a Windows Presentation Foundation (WPF) user interface (UI) to a host WPF standalone application.

The add-in returns a UI that is a WPF user control. The content of the user control is a single button that, when clicked, displays a message box. The WPF standalone application hosts the add-in and displays the user control (returned by the add-in) as the content of the main application window.

Prerequisites

This example highlights the WPF extensions to the .NET Framework add-in model that enable this scenario, and assumes the following:

To create an add-in that returns a WPF UI requires specific code for each pipeline segment, the add-in, and the host application.

Implementing the Contract Pipeline Segment

A method must be defined by the contract for returning a UI, and its return value must be of type INativeHandleContract. This is demonstrated by the GetAddInUI method of the IWPFAddInContract contract in the following code.

using System.AddIn.Contract; // IContract, INativeHandleContract 
using System.AddIn.Pipeline; // AddInContractAttribute 

namespace Contracts
{
    /// <summary> 
    /// Defines the services that an add-in will provide to a host application 
    /// </summary>
    [AddInContract]
    public interface IWPFAddInContract : IContract
    {
        // Return a UI to the host application
        INativeHandleContract GetAddInUI();
    }
}

Implementing the Add-In View Pipeline Segment

Because the add-in implements the UIs it provides as subclasses of FrameworkElement, the method on the add-in view that correlates to IWPFAddInView.GetAddInUI must return a value of type FrameworkElement. The following code shows the add-in view of the contract, implemented as an interface.

using System.AddIn.Pipeline; // AddInBaseAttribute 
using System.Windows; // FrameworkElement 

namespace AddInViews
{
    /// <summary> 
    /// Defines the add-in's view of the contract 
    /// </summary>
    [AddInBase]
    public interface IWPFAddInView
    {
        // The add-in's implementation of this method will return 
        // a UI type that directly or indirectly derives from  
        // FrameworkElement.
        FrameworkElement GetAddInUI();
    }
}

Implementing the Add-In-Side Adapter Pipeline Segment

The contract method returns an INativeHandleContract, but the add-in returns a FrameworkElement (as specified by the add-in view). Consequently, the FrameworkElement must be converted to an INativeHandleContract before crossing the isolation boundary. This work is performed by the add-in-side adapter by calling ViewToContractAdapter, as shown in the following code.

using System.AddIn.Contract; // INativeHandleContract 
using System.AddIn.Pipeline; // AddInAdapterAttribute, FrameworkElementAdapters, ContractBase 
using System.Windows; // FrameworkElement 

using AddInViews; // IWPFAddInView 
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
    {
        IWPFAddInView wpfAddInView;

        public WPFAddIn_ViewToContractAddInSideAdapter(IWPFAddInView wpfAddInView)
        {
            // Adapt the add-in view of the contract (IWPFAddInView)  
            // to the contract (IWPFAddInContract) 
            this.wpfAddInView = wpfAddInView;
        }

        public INativeHandleContract GetAddInUI()
        {
            // Convert the FrameworkElement from the add-in to an INativeHandleContract  
            // that will be passed across the isolation boundary to the host application.
            FrameworkElement fe = this.wpfAddInView.GetAddInUI();
            INativeHandleContract inhc = FrameworkElementAdapters.ViewToContractAdapter(fe);
            return inhc;
        }
    }
}

Implementing the Host View Pipeline Segment

Because the host application will display a FrameworkElement, the method on the host view that correlates to IWPFAddInHostView.GetAddInUI must return a value of type FrameworkElement. The following code shows the host view of the contract, implemented as an interface.

using System.Windows; // FrameworkElement 

namespace HostViews
{
    /// <summary> 
    /// Defines the host's view of the add-in 
    /// </summary> 
    public interface IWPFAddInHostView
    {
        // The view returns as a class that directly or indirectly derives from  
        // FrameworkElement and can subsequently be displayed by the host  
        // application by embedding it as content or sub-content of a UI that is  
        // implemented by the host application.
        FrameworkElement GetAddInUI();
    }
}

Implementing the Host-Side Adapter Pipeline Segment

The contract method returns an INativeHandleContract, but the host application expects a FrameworkElement (as specified by the host view). Consequently, the INativeHandleContract must be converted to a FrameworkElement after crossing the isolation boundary. This work is performed by the host-side adapter by calling ContractToViewAdapter, as shown in the following code.

using System.AddIn.Contract; // INativeHandleContract 
using System.AddIn.Pipeline; // HostAdapterAttribute, FrameworkElementAdapters, ContractHandle 
using System.Windows; // FrameworkElement 

using Contracts; // IWPFAddInContract 
using HostViews; // IWPFAddInHostView 

namespace HostSideAdapters
{
    /// <summary> 
    /// Adapts the add-in contract to the host's view of the add-in 
    /// </summary>
    [HostAdapter]
    public class WPFAddIn_ContractToViewHostSideAdapter : IWPFAddInHostView
    {
        IWPFAddInContract wpfAddInContract;
        ContractHandle wpfAddInContractHandle;

        public WPFAddIn_ContractToViewHostSideAdapter(IWPFAddInContract wpfAddInContract)
        {
            // Adapt the contract (IWPFAddInContract) to the host application's 
            // view of the contract (IWPFAddInHostView) 
            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);
        }

        public FrameworkElement GetAddInUI()
        {
            // Convert the INativeHandleContract that was passed from the add-in side 
            // of the isolation boundary to a FrameworkElement
            INativeHandleContract inhc = this.wpfAddInContract.GetAddInUI();
            FrameworkElement fe = FrameworkElementAdapters.ContractToViewAdapter(inhc);
            return fe;
        }
    }
}

Implementing the Add-In

With the add-in-side adapter and add-in view created, the add-in (WPFAddIn1.AddIn) must implement the IWPFAddInView.GetAddInUI method to return a FrameworkElement object (a UserControl in this example). The implementation of the UserControl, AddInUI, is shown by the following code.

    <UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="WPFAddIn1.AddInUI">

    <StackPanel>
		<Button Click="clickMeButton_Click" Content="Click Me!" />
    </StackPanel>

</UserControl>
using System.Windows; // MessageBox, RoutedEventArgs 
using System.Windows.Controls; // UserControl 

namespace WPFAddIn1
{
    public partial class AddInUI : UserControl
    {
        public AddInUI()
        {
            InitializeComponent();
        }

        void clickMeButton_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Hello from WPFAddIn1");
        }
    }
}

The implementation of the IWPFAddInView.GetAddInUI by the add-in simply needs to return a new instance of AddInUI, as shown by the following code.

using System.AddIn; // AddInAttribute 
using System.Windows; // FrameworkElement 

using AddInViews; // IWPFAddInView 

namespace WPFAddIn1
{
    /// <summary> 
    /// Add-In implementation 
    /// </summary>
    [AddIn("WPF Add-In 1")]
    public class WPFAddIn : IWPFAddInView
    {
        public FrameworkElement GetAddInUI()
        {
            // Return add-in UI 
            return new AddInUI();
        }
    }
}

Implementing the Host Application

With the host-side adapter and host view created, the host application can use the .NET Framework add-in model to open the pipeline, acquire a host view of the add-in, and call the IWPFAddInHostView.GetAddInUI method. These steps are shown in the following 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(IWPFAddInHostView), appPath);
AddInToken wpfAddInToken = addInTokens[0];
this.wpfAddInHostView = wpfAddInToken.Activate<IWPFAddInHostView>(AddInSecurityLevel.Internet);

// Get and display add-in UI
FrameworkElement addInUI = this.wpfAddInHostView.GetAddInUI();
this.addInUIHostGrid.Children.Add(addInUI);
Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft