2007 Microsoft Office System - RibbonExtensibility (Teil 2)

(Ein Update auf Basis VSTO 2005 SE - Fortsetzung von Teil 1)  

Ändern bestehender Ribbon Elemente

 

Bisher hatten wir lediglich neue Tabs und darin neue Gruppen erzeugt. Können wir uns auch in bestehende Tabs bzw. Gruppen von Office 2007 „einmischen“? Ja, wir können bestehende Tabs verwenden, um eigene Gruppen hinzuzufügen. Nein, wir können bestehende Gruppen nicht verändern. Das würde das Konzept der Ribbons torpedieren. Schließlich gibt es ja Tabs, die kontextabhängig Ihren Inhalt preisgeben und das sollte auch nur solcher Inhalt sein, der gerade Sinn macht.

Um uns in bestehende Tabs zu integrieren, benötigen wir die ganz am Anfang einmal erwähnte idMso und den Namen der zu verändernden Tabs. Des Weiteren kommt uns bei der Platzierung der neuen Gruppe das insertAfterMso Attribut zugute. Genau nach der angegebenen eingebauten Gruppe wird unsere neue angeordnet. Eine Liste der Bezeichnungen eingebauter Elemente finden Sie hier. Im folgenden Beispiel wird im Tab View eine Gruppe „My View / Hide“ erzeugt, und zwar nach einer vorhandenen Gruppe mit der id GroupViewShowHide. Wichtig ist auch hier: die Begriffe sind case-sensitiv, also besser nicht verschreiben.

<tab idMso="TabView">

  <group id="MyViewHide" label="My View / Hide" insertAfterMso="GroupViewShowHide" >

    <toggleButton id="tgViewHideProps"

                  size="large"

                  imageMso="FileProperties"

                  label="View / Hide Property Panel"

                  onAction="OnViewHideProps"/>

  </group>

</tab>

Über das Globals-Objekt kann auf das Objektmodell der Hostanwendung zugegriffen werden:

public void OnViewHideProps(Office.IRibbonControl control, bool pressed)

{

  Globals.ThisAddIn.Application.DisplayDocumentInformationPanel = pressed;

}

Verwenden von eingebauter Funktionalität

Brauchen Sie Funktionalität, die in die Hostanwendung schon eingebaut ist, allerdings an anderer Stelle? Auch kein Problem. Mit Hilfe der Ids der eingebauten Controls können wir diese dort plazieren, wo wir wollen, z.B. in einer eigenen Gruppe:

<group id="BuiltInControlGroup" label="Group of built-in Controls">

  <comboBox idMso="Font"/>

  <comboBox idMso="FontSize"/>

  <separator id="sep3"/>

  <buttonGroup id="BuiltInControls">

    <splitButton idMso="BordersGallery"/>

    <gallery idMso="CellFillColorPicker"/>

    <gallery idMso="FontColorPicker"/>

  </buttonGroup>

  <buttonGroup id="BuiltInSimpleControls">

    <toggleButton idMso="Bold" />

    <toggleButton idMso="Italic" />

    <toggleButton idMso="Underline" />

  </buttonGroup>

</group>

Sogar ganze Gruppen können einfach in eigene Tabs übernommen werden :

<group idMso="GroupViewShowHide">

</group>

<group idMso="GroupFont">

</group>

Sie brauchen auch keine Callback Handler zu deklarieren, da die Funktionalität schon an den Controls hängt. Wenn Sie also der (Toggle) Button für Fettschrift in einen Word Ribbon einbauen, dann können Sie damit markierte Bereiche mit dem Stil Fettschrift versehen.

Repurposing von eingebauten Befehlen

 

Für das Überschreiben von eingebauten Befehlen mit eigener Logik gab es in Microsoft Office verschiedene Wege. So wurde der Speichern-Befehl überschrieben, wenn wir ein VBA Makro mit dem Namen FileSave() angelegt haben. Wir konnten auch die OnAction Property des Buttons auf eine eigene Routine setzen (und dann feststellen, dass [Ctrl]+[s] immer noch den originalen Befehl ausführt) oder auf den Click Event des Save Buttons lauschen und im Event Handler CancelDefault auf true setzen, damit die eingebaute Aktion nicht ausgeführt wird.

Mit Ribbons in Office 2007 können wir unser Ribbon XML File hernehmen und die Änderung hier durchführen. Es existiert eine <commands> Sektion (außerhalb der Tabs), die genau dafür gedacht ist. Hier können wir einen neuen onAction Callback Handler verdrahten und ebenfalls – sozusagen in einem Atemzug – auch noch steuern, ob der Befehl überhaupt zur Verfügung stehen soll.

<commands>

  <command idMso="Save" onAction="mySave" getEnabled="getEnabled"/>

</commands>

Ganz wichtig: dies muss im Ribbon XML File ganz zu Beginn, also noch vor der Ribbon-Deklaration stehen.

Natürlich haben wir auch hier im Callback Handler das von anderen Methoden bekannte CancelDefault Flag, mit dem die originale Aktion deaktiviert werden kann.

 

public void MySave(Office.IRibbonControl control, out bool CancelDefault)

{

  MessageBox.Show("My new Save Function");

  CancelDefault = true;

}

Normalerweise würden Sie jetzt in Anhängigkeit vom Programmfluß oder den Aktionen, die der Benutzer schon durchgeführt hat, die Verfügbarkeit bestimmter Befehle steuern. Hier im Beispiel soll es anhand eines ToggleButtons geschehen:

 

<group id="DisableFunktions" label="Disable Functionality">

  <toggleButton id="btnEnableButton"

                size="large"

                getImage="getEDFImage"

                getLabel="getLabel"

                onAction="OnToggleAvailability"/>

</group>

 

Hier wird gleich noch gezeigt, wie Sie zur Laufzeit das Aussehen des Buttons selbst verändern, sprich ihm wird ein neuer Titel und ein neues Icon zugewiesen (getLabel bzw. getImage Callback). Auf dem Flag ButtonsEnabled wird der Status gespeichert, getEnabled liefert diesen einfach zurück. Im Callback Handler des Buttons OnToggleAvailability schalten wir die Verfügbarkeit ein oder aus. Ribbon.Invalidate() zeichnet das UserInterface der Hostanwendung neu. Wenn Sie nur ein oder wenige Controls neu zeichnen müssen, so können Sie auch InvalidateControl(ControlID) aufrufen.

 

private bool ButtonsEnabled = true;

public bool getEnabled(Office.IRibbonControl control)

{

  return ButtonsEnabled;

}

public void OnToggleAvailability(Office.IRibbonControl control, bool pressed)

{

  ButtonsEnabled = !pressed;

  this.ribbon.Invalidate();

}

public string getLabel(Office.IRibbonControl control)

{

  if (ButtonsEnabled)

    return "Disable Save";

  else

    return "Enable Save";

}

public stdole.IPictureDisp getEDFImage(Office.IRibbonControl control)

{

  if (ButtonsEnabled)

    return ImageConverter.IconToPictureDisp(Properties.Resources.Disable);

  else

    return ImageConverter.IconToPictureDisp(Properties.Resources.Enable);

}

 

Starten wir das Projekt direkt aus Visual Studio heraus – hatte ich schon erwähnt, dass wir auch hier wie gewohnt BreakPoints setzen und debuggen können? – wird der Office Client gestartet und unser Add-In geladen. Es sollte der Tab MyTab sichtbar sein und durch einen Klick darauf bekommen wir unser Ribbon Tab zu sehen. Deaktivieren wir den Speichern-Befehl über unseren Button „Disable Save“, so sehen wir, dass wirklich sowohl im Menü als auch in der Quick Access Toolbar Speichern nicht mehr möglich ist. In Word und Powerpoint wird ebenfalls [Ctrl]+[s] abgefangen, in Excel leider zum derzeitigen Stand nicht.

 

 

Weg mit allem, ich mach’s selbst

 

Besonders Excel wurde von Entwicklern nicht selten derart umgestaltet, dass auf den ersten Blick nicht mehr zu erkennen war, das es sich um Microsoft Excel handelt. Es wurden Menüs und Toolbars entfernt und durch eigene ersetzt. Selbst Tastaturkürzel wurden entfernt, so dass nichts mehr auf Excel hinwies bis auf die eigentliche Tabelle. Die Idee, Excel’s eingebaute Funktionalität zu verwenden und ein neues Outfit darum herum zu bauen., ist also nicht so neu. Doch wie viel Aufwand bedeutete es, das alles zu verstecken?

Office 2007 und speziell IRibbonExtensibility kennt Ribbon Attribut StartFromScratch. Setzen wir dieses auf true, so verschwindet magischerweise die inzwischen vertraute Benutzeroberfläche. Was bleibt ist das, was wir selbst erzeugt haben. Excel’s Ribbon und Quick Access Bar sind leer und das Office Menü wurde auf die Basiseinträge reduziert.

 

Deployment

 

Echtes ClickOnce Deployment gibt es ab dieser Version von Visual Studio Tools für Office. Wir nutzen also den ClickOnce Download Cache und nicht mehr den des Internet Explorers, um die Updates lokal zu speichern. Apropos Updates: die kann der Administrator zentral bereit stellen - die Clients holen sie sich, wenn sie gebraucht werden.

Natürlich kann auch der Windows Installer (MSI) für die Installation genutzt werden. Bei Add-In-Projekten wird automatisch ein MSI-Projekt hinzugefügt und alle erforderlichen Registry-Einträge für die COM-Add-In-Registrierung gesetzt. Verteilt werden müssen aber noch zwei Dinge:

 

· Die VSTO Runtime

· Die Office Primary Interop Assemblies (PIA)

 

Da wir mit .NET arbeiten, müssen wir uns auch um Security kümmern, genauer gesagt um Code Access Security (CAS). Diese sorgt dafür, dass nicht jede Software tun kann, was ihr so gefällt. Mittels CAS kann der Administrator ein Vertrauensverhältnis zu einer bestimmten Software festlegen. Das basiert entweder auf der Herkunft der Software (von welchem Pfad wurde sie geladen) oder auf inhaltlichen Beweisen, wie z.B. eine digitale Signatur. Der Administrator richtet also einen sog. Trusted Publisher (Vertrauter Herausgeber) ein und jeder Software, die mit dem Zertifikat signiert wurde, wird automatisch vertraut. Ich muss nicht erwähnen, das alle andere Software auf .NET Basis keinerlei Rechte besitzt. Für Office-Interoperability müssen wir das Vertrauensverhältnis auf FullTrust (wegen COM Interop) setzen.

Fazit

 

Wir könnten uns hier sicher noch eine Weile mit dem Thema beschäftigen. Office Integration ist inzwischen ein Thema so spannend wie .NET selbst. Während noch vor 3 Jahren kaum einer die Notwendigkeit einer Integration seiner Anwendung in Office gesehen hat, wird es meiner Meinung nach in 3 Jahren kaum noch Chancen geben, eine Anwendung ohne Office Integration zu verkaufen. Microsoft setzt mit Office 2007 und Visual Studio Tools für Office ganz klare Zeichen in Richtung LOB-Integration. In diese Richtung gehen auch Ansätze bzw. Lösungen wie IBF, Duet für SAP und Microsoft Office bzw. LOBi. Office 2007 ist mehr als die Summe von Einzelprodukten. Es ist eine Plattform für Smarte Clients. Eine Plattform, die es leichter denn je macht, seinen Anwendern Funktionalität an Ort und Stelle zur Verfügung zu stellen.