OfficeRibbon - STARIONGROUP/COMET-IME-Community-Edition GitHub Wiki

Introduction

The CDP4-COMET Office Addin is developed using the NetOffice .NET Wrapper Assemblies for accessing MS Office applications. The COMAddin class provides extension points to implement the IDTExtensibility2, Office.IRibbonExtensibility and Office.ICustomTaskPaneConsumer interfaces. The OfficeRibbon is customized using the Office.IRibbonExtensibility interface. The Office.IRibbonExtensibility interface loads the XML markup, either from an XML customization file or from XML markup embedded in the procedure, that customizes the Ribbon user interface.

Customizing the Office Ribbon

Any useful Office addin needs to customize the Office Ribbon to add controls to the Ribbon to allow the user to interact with the Addin. Ribbon customization is performed by overriding the GetCustomUI method of the COMAddin base class and returining valid Ribbon XML. The GetCustomUI method is defined by the IRibbonExtensibility interface that is implemented by the COMAddin class of NetOffice, in order to use it, it must be overridden in the concrete Addin class.

IMPORTANT: invalid Ribbon XML that is returned from the GetCustomUI method will result in the Ribbon customization to be ignored.

The contents of the Office Ribbon shall be delivered by the different CDP4-COMET Plugins (Modules) at Addin startup. The result of using a control on the Ribbon, clicking on a button, selecting an item from a combobox, shall be executed by the Plugin that delivered that specific control. This is enabled by the IFluentRibbonManager interface and the FluentRibbonManager implementation of that interface.

Read more about customizing the Ribbon here, here and here.

IFluentRibbonManager Interface

The IFluentRibbonManager interface is used to orchestrate the content of the Ribbon and the interaction with the controls on the Ribbon. The Office Ribbon makes use of call backs that are declared in the Ribbon XML. The Addin class implements these call backs and redirects the execution of these callbacks to the IFluentRibbonManager. Each CDP4-COMET plugin that needs to expose it's functionality to the Addin has a reference to the IFluentRibbonManager, where the IFluentRibbonManager registers so-called RibbonParts. The different RibbonParts provide the IFluentRibbonManager with specific bits of Ribbon XML and contain the business logic associated with the execution of the interaction with the controls on the bit of Ribbon XML that they deliver to the IFluentRibbonManager. The IFluentRibbonManager interface derives from the IFluentRibbonCallback interface, the abstract RibbonPart base class implements the IFluentRibbonCallback interface. This interface defines all the possible call backs that can be implemented as part of the Ribbon customization.

The IFluentRibbonManager is defined as follows:

public interface IFluentRibbonManager : IFluentRibbonCallback
{
   bool IsActive { get; set; }

   void RegisterRibbonPart(RibbonPart ribbonPart);

   string GetFluentXml();
}

The RegisterRibbonPart method is used to register RibbonParts of a Module with the IFluentRibbonManager. The GetFluentXml method returns the combined Ribbon XML of all RibbonParts that have been registered. This combined Ribbon XML is used to create the custom CDP4-COMET Ribbon of the Addin. The IsActive property is used to enable or disable the IFluentRibbonManager. When the IFluentRibbonManager is inactive, the business logic defined in the RibbonParts is not executed.

IFluentRibbonCallback Interface

The IFluentRibbonCallback interface that defines the different Callback methods that can be invoked by Ribbon customization:

public interface IFluentRibbonCallback
{
   void OnAction(string ribbonControlId, string ribbonControlTag = "");

   string GetDescription(string ribbonControlId, string ribbonControlTag = "");

   string GetKeytip(string ribbonControlId, string ribbonControlTag = "");

   string GetScreentip(string ribbonControlId, string ribbonControlTag = "");

   string GetContent(string ribbonControlId, string ribbonControlTag = "");

   string GetLabel(string ribbonControlId, string ribbonControlTag = "");

   string GetSize(string ribbonControlId, string ribbonControlTag = "");

   string GetSupertip(string ribbonControlId, string ribbonControlTag = "");

   Image GetImage(string ribbonControlId, string ribbonControlTag = "");

   bool GetEnabled(string ribbonControlId, string ribbonControlTag = "");

   bool GetPressed(string ribbonControlId, string ribbonControlTag = "");

   bool GetVisible(string ribbonControlId, string ribbonControlTag = "");

   bool GetShowImage(string ribbonControlId, string ribbonControlTag = "");

   bool GetShowLabel(string ribbonControlId, string ribbonControlTag = "");
}

Each of these call-backs can be defined in the Ribbon XML. The Ribbon XML specifies for the different kinds of controls XML attributes that have as value the name of the call-back that shall be invoked when the callback attribute is defined.

IMPORTANT: CDP4-COMET RibbonParts shall always use as value the exact name of the method as defined by the IFluentRibbonCallback interface. example: <button id="someid" label="this is a button" onAction="OnAction"/>

With the exception of the onAction XML attribute, the call back methods allow to dynamically set the properties of the Ribbon controls through these call-backs. The property and the associated callback cannot be declared at the same time. The onAction XML attribute defines the action that needs to be executed when a control is clicked.

WARNING: The control property and associated call-back cannot be declared at the same time! Example of INVALID Ribbon XML <button id="someid" label="this is a button" getLabel="GetLabel"/>. The label property and getLabel call-back shall not be declared in the same control.

Ribbon Parts

Each Module that needs to be integrated with an Office Addin, most likely putting controls on the Ribbon, need to implement at least one Ribbon Part. A concrete Ribbon Part shall derive from the abstract RibbonPart base class. The abstract RibbonPart base class implements the IFluentRibbonCallback interface as virtual methods. A concrete Ribbon Part can override these methods when required. Concrete RibbonParts shall be instantiated in the IModule implementing class of each CDP4-COMET Plugin. The constructor of a RibbonPart is defined as follows:

protected RibbonPart(int order, IPanelNavigationService panelNavigationService)

The int order parameter specifies in what order the Ribbon XML of the RibbonPart should be displayed on the Office Ribbon. The IPanelNavigationService panelNavigationService parameter is used to inject the IPanelNavigationService in order to support navigation to the different IPanelViewModel using the void Open(IPanelViewModel viewModel, bool useRegionManager) method where the useRegionManager should be set to false. The IRegionManager does not exist in an Office Addin, therefore it cannot be used to select where to place the IPanelView. Displaying a browser (IPanelViewModel and IPanelView combination) is handled by the Addin class, see below. The content (XML) of the Ribbon that is exposed by a particular RibbonPart is declared in an embedded resource and loaded by the RibbonPart (this is performed in the constructor of the abstract base class). The name of the embedded resource is specified in the overridden GetRibbonXmlResourceNane method, a convention based location of both RibbonPart and resource make sure the proper XML is loaded.

Conventions

  • The RibbonParts shall be placed in a folder called OfficeRibbon in the root of the Plugin project.
  • The namespace of the RibbonPart shall not include the name of the folder, it shall be the same as the name of the assembly.
  • The name of a RibbonPart shall be <name>RibbonPart.
  • The XML of the RibbonPart shall be placed in the following folder Resources\RibbonXml in the root of the Plugin project. The build action shall be embedded resource.
  • The file name of the XML ribbon resource shall have as it's extention .xml
  • The GetRibbonXmlResourceNane method shall be overriden and return the name of the XML resource, excluding the .xml extension

IModule Initialization

The IFluentRibbonManager is declared in the CDP4Composition Assembly. The FluentRibbonManager class implements the IFluentRibbonManager interface and is injected by PRISM/MEF into the different CDP4-COMET Plugins (Modules) that need to provide any Ribbon customization. COMET Plugins always contain one Module class that implements IModule, the Module class shall use constructor injection to set it's RibbonManager property:

[ModuleExportName(typeof(SomeCDPModule), "Some CDP Module")]
public class SomeCDPModule : IModule
{
   [ImportingConstructor]
   public SomeCDPModule(IFluentRibbonManager ribbonManager)
   {
        this.RibbonManager = ribbonManager;
   }
   
   internal IFluentRibbonManager RibbonManager { get; private set; }
}

Opening CDP4-COMET views as Custom Task Panes

Office Addins support showing Custom Task Panes as docked panels, this is a feature of Microsoft Office. Custom Task Panes are implemented as Winforms. The CDP4-COMET is implemented as a WPF application, WPF UserControls can be hosted by WinForms The Addin project contains one Winforms UserControl called TaskPaneWpfHostControl that is instantiated each time a CDP4-COMET browser (view) needs to be visualized. The Addin listens for NavigationPanelEvent messages on the CDPMessageBus, whenever one of these events is received, depending on the PanelStatus the browser is either displayed or closed and disposed of. The RegionName property is used to decide where the Custom Task Pane needs to be placed in the Office window. This can either be left, right or bottom. The Region that is declared on the IPanelView is used by the IPanelNavigationService to send the correct NavigationPanelEvent.

When a user closes a panel using panel's close button, the panel will actually only be hidden and not closed. When this happens a HidePanelEvent message is added to the CDPMessageBus. A RibbonPart descendent can therefore decide to listen for this event and have its own implementation on what to do next: Do nothing, or for example dispose the IPanelView and IPanelViewModel 'by hand'.

read more about docking WPF panels in Addins here.

⚠️ **GitHub.com Fallback** ⚠️