2007 Microsoft Office System - RibbonExtensibility (Teil 1) - Office Development is more than VBA - Site Home - MSDN Blogs

2007 Microsoft Office System - RibbonExtensibility (Teil 1)

2007 Microsoft Office System - RibbonExtensibility (Teil 1)

  • Comments 8

(Ein Update auf Basis VSTO 2005 SE) 

Die Version 2007 von Microsoft Office macht Schluss mit einem über Jahre hinweg gehegten und gepflegten Stiefkind der Softwareentwicklung: Menüs. Was anfangs noch neue Möglichkeiten suggerierte, geriet schnell zum Albtraum von Ottonormalbenutzer. Überfrachtete Menüstrukturen mit hunderten von Befehlen laden zum Suchen ein. Oftmals wünschen sich Anwender bei den Softwarefirmen Funktionen, die es schon lange gibt. Nur weil diese nicht zu finden sind in mehrfach geschachtelten Untermenüs oder Dialogen. Menüs waren gestern, heute gibt es Ribbons.

Diese Ribbons, auch Multifunktionsleisten genannt, sind nicht von irgendwoher gekommen, sondern das Produkt  langer und ausführlicher Tests in den Microsoft Usibility Labs. Da sitzen nicht etwa Mitarbeiter von Microsoft, sondern eben jene Ottonormalbenutzer vom Büro nebenan und testen die geistigen Auswüchse findiger Köpfe. Intuitive Benutzerführung, Funktionen schneller finden und natürliches Suchen sind grundlegende Ideen, die dahinter stecken. Und zugegeben, nach etwas Eingewöhnungszeit findet man sich ganz gut zurecht. Natürlich sind wir seit Jahren auf Menüs geeicht worden und wer bestimmte, immer wieder benutzte Programme, wie seine sprichwörtliche Westentasche kennt, wird sicherlich eine gewisse Umgewöhnungsphase einplanen müssen. Auch bekommen nicht alle zehn Microsoft Office Client Programme (ja, es sind zehn, von Access bis Visio) sofort die neue Oberfläche. Vorerst sind diese Ribbons den Programmen Word, Excel, Powerpoint und Outlook vorbehalten, wobei letzteres eine hybride Zwischenstellung einnimmt, da die Shell weiterhin Menüs hat, während ein Inspector Window (Nachrichten, Aufgaben, Kontakte, etc. werden in einem Inspektor-Fenster geöffnet)  diese neue Multifunktionsleiste bekommt.

 

 

Was haben Entwickler davon?

 

Während noch vor 3 Jahren fast ausschließlich VBA-Programmierer Lösungen für Office angeboten haben – allenfalls wurde OLE Automation von „richtigen Programmierern“ (pfui, welche Anmaßung) verwendet – wird heute der Ruf nach einer Office Integration immer lauter. Ist ja auch verständlich, will doch der produktive Anwender , welcher fast sein gesamtes (Büro-) Leben in Outlook, Word bzw. Excel zubringt, diese nicht verlassen, um businessrelevante Daten aus verborgenen Datensilos zu holen. Ganz zu schweigen von den Schulungskosten, die anfallen, um Ottonormalbenutzer für die verschiedenen Clients der ERP-, CRM- oder sonstwelcher Systeme fit zu machen. Von einigen Power Usern einmal abgesehen wird i.d.R. weniger als 10% der von solchen Spezialanwendungen zur Verfügung gestellten Funktionalität genutzt. Warum also nicht die am meisten genutzen Bestandteile dorthin bringen, wo sie am meisten gebraucht werden – in Büroanwendungen wie Microsoft Office.

Nun wird der gemeine VB.NET oder C# - Programmierer nicht unbedingt sein gewohntes Visual Studio verlassen wollen, um dann auch noch eine andere Sprache (VBA, igitt) programmieren zu müssen. Visual Studio geht ja schon seit einiger Zeit den Weg „One Tool for Everything“ und erlaubt es, ASP.NET-Projekte, Projekte für Desktop- und Server-Anwendungen, aber auch für mobile Geräte oder Gerätetreiber unter einem Dach zu entwickeln. Auch Office-Anwendungen bilden da keine Ausnahme. Mit Visual Studio Tools für Office (VSTO) hat Microsoft eine Möglichkeit geschaffen, Projekte in Visual Studio für Microsoft Office zu entwickeln, debuggen und testen. Die für diesen Artikel zugrunde liegende Version von VSTO 2005 SE arbeitet mit Visual Studio 2005 und Office 2007 (sowie Office 2003) und integriert sich nahtlos in die IDE  der Microsoft Entwicklungsumgebung. 

 

 

 

Um Ribbons erweitern zu können, muss ein von den Office Clients bereitgestelltes COM Interface (ja, Office 2007 ist immer noch COM Technologie) – nämlich IRibbonExtensibility – bedient werden. Es wird nur eine Funktion zur Verfügung gestellt: GetCustomUI(string), welche die Beschreibung der Ribbon-Erweiterung – natürlich in Form von XML Markup - entgegen nimmt. Es gibt nun mehrere Wege, der Office-Anwendung die eigene Ribbon-Beschreibung zu übergeben:

1.     Die XML-Datei in einem Ordner CustomUI innerhalb der Office-Datei (Office Open XML File Format vorausgesetzt). Dies erfordert das Eintragen von Content Types und Relationships ebenfalls innerhalb der Office-Datei. Der Code, der ausgeführt werden soll, liegt dann in VBA vor.

2.     Per COM Add-In, geschrieben in C++, VB6, o.ä.

3.     Per Shared Add-In, geschrieben in einer .NET Sprache

4.     Über ein VSTO Add-In, geschrieben in C# oder VB.NET

 

VBA und native COM Add-Ins wollen wir hier nicht behandeln und Shared Add-Ins haben ein paar Nachteile, die VSTO Add-Ins nicht haben. Sie laufen nicht, wenn die Office Macro Security auf Stufe „hoch“ gesetzt wurde, da es keine digital signierte Shim (Proxy, welcher die .NET Runtime hostet und das Add-In ausführt) gibt. Außerdem werden alle Shared Add-Ins in dieselbe Application Domain geladen, was dazu führt, dass alle Add-Ins in Mitleidenschaft gezogen werden, wenn eines fehlerhaft ist. VSTO bringt einen AddInLoader, der die Aufgabe der o.g. Shim übernimmt, selbst signiert ist und ebenfalls die Code Access Security (CAS) Anforderungen des Add-Ins überprüft und mit CAS Policies der lokalen Maschine vergleicht. Des Weiteren werden klare Schnittstellen, die Events Startup und Shutdown angeboten. Shared Add-Ins bieten OnConnection, OnDisconnection, OnAddInsUpdate, OnStartupComplete und OnBeginShutdown an und es ist nicht sofort klar, was wann verwendet werden muss.

Ribbons mit Visual Studio Tools für Office entwickeln

 

Zuerst einmal muss sich der Leser über ein paar Begriffe klar werden, die im Zusammenhang mit Ribbons immer wieder auftauchen. Ein Ribbon ist im Prinzip eine Kombination aus Tabbed Dialog Control und Toolbar. Ein Ribbon enthält ein bis mehrere Tabs und diese wiederum Groups. In den Gruppen werden Controls platziert, die wiederum zu sog. Control Groups zusammengefasst werden können.

 

 

 

In Visual Studio legen wir dazu einfach ein neues Projekt an und nutzen eine der von VSTO gelieferten Projektvorlagen für Add-Ins für die Programme Word, Excel, Powerpoint oder Outlook. Welche Programmiersprache Sie verwenden, ist Ihnen überlassen. Zur Verfügung stehen Visual Basic .NET und C#. Danach fügen wir über Rechtsklick auf den Projektnamen im Projektmappen-Explorer (Hinzufügen | Neues Element) den Ribbon Support ein. Hinzugefügt wird sowohl ein Ribbon XML File (Ribbon1.xml) als auch das dazugehörige Code Modul (Ribbon1.cs bzw. Ribbon1.vb). Öffnen wir also Ribbon1.xml und wir sehen den Prototyp eines Ribbons an. Er enthält einen Tab und eine Gruppe, die ein Control, nämlich einen ToggleButton enthält. Das Projekt ist sofort lauffähig, Wir können es also gleich starten. Freilich ist das Beispiel sehr spartanisch.

Wenn wir die Möglichkeiten, die uns Ribbons zur Verfügung stellen, mit denen von älteren Office-Versionen vergleichen, fällt auf, dass da einiges Hinzugekommen ist.

Folgende Controls stehen zur Verfügung:

·         button

·         checkBox

·         comboBox

·         dropDown

·         dynamicMenu

·         editBox

·         gallery

·         labelControl

·         menu

·         splitButton (Kombination aus Button und Menu)

·         toggleButton

 

Desweiteren können folgende Elemente verwendet werden:

·         box (Horizontaler Gruppierungscontainer)

·         buttonGroup (Element zum Gruppieren von Buttons innerhalb einer Gruppe)

·         control (Element zum Steuern eingebauter Controls)

·         command (Element zum Steuern eingebauter Befehle)

·         group (Eine Gruppe innerhalb eines Tabs)

·         separator (Vertikaler Separator)

·         tab (Ein Reiter eines Ribbons)

 

Da Visual Studio 2005 bzw. VSTO 2005 SE noch keine Syntaxunterstützung bieten, fällt die Erweiterung ein wenig schwer. Aber das zugrundeliegende XML Schema bringt Abhilfe. Kopieren Sie die Datei CustomUI.xsd aus dem Ordner (VSTO muß vorher installiert sein)

   \program files\microsoft visual studio 8\Xml\Schemas\1033\CustomUI.xsd

nach

   \program files\microsoft visual studio 8\Xml\Schemas\

Wenn wir jetzt Änderungen in Ribbon1.xml machen, haben wir IntelliSense!

 

Doch schauen wir uns erst einmal das folgende Listing an.

 

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnLoad">

 

  <ribbon startFromScratch="false">

    <tabs>

      <tab id="MyTab" label="My Tab">

 

        <group id="MiscGroup" label="Misc Group">

          <splitButton id="mySplit" size="large">

            <button id="btnSplitMain" label="Surf the Net" description="Surfin' USA"

                    imageMso="OutlookGlobe" onAction="OnSplitButton"/>

            <menu id="splitMenu" itemSize="large">

              <button id="btnSplitMenu1" label="Smile" description="Be Happy"

                      screentip="Happy People live longer"

                      imageMso="HappyFace" 

                      onAction="OnActionDefault" />

              <button id="btnSplitMenu2" label="Paint" description="Paint it black"

                      screentip="The famous song by the Rolling Stones"

                      imageMso="FormatPainter" 

                      onAction="OnActionDefault" />

              <button id="btnSplitMenu3" label="Filter" description="Filter Inbox"

                      screentip="Apply the Filter and find what you are looking for"

                      imageMso="Filter"

                      onAction="OnActionDefault" />

            </menu>

          </splitButton>

        </group>

      </tab>

    </tabs>

  </ribbon>

</customUI>

 

Gleich in der ersten Zeile finden wir unsere CustomUI Schema Definition wieder. Danach die Verknüpfung mit dem OnLoad Callback. Überfliegen wir das Markup, sehen wir sofort, dass jedes Element einen Identifikator (id) besitzt, der im gesamten Ribbon XML eindeutig sein muss. Die weitere Spezifizierung von Elementen wird also über XML Attribute erledigt. Wir deklarieren unsere Oberfläche. Das Attribut label beispielsweise setzt den zum Control gehörenden Text, onAction den Callback für den Click Event und imageMso das zu verwendende Icon. Übrigens, wenn ein Attribut auf „Mso“ endet, so heißt das, dass eingebaute Funktionalität verwendet wird. So verweist imageMso auf ein in der Office Bibliothek vorhandenes Icon und idMso auf ein vorhandenes Element. Aber dazu später. Die Icons von Office werden jetzt per Namen referenziert und nicht mehr über eine numerische ID, die sich keiner merken konnte.

 

Über das onAction Attribut haben wir in den callback Handler für die Click Events festgelegt. Doch wie muss dieser aussehen? Welche Signatur muss er haben, damit es funktioniert? Eine Auflistung der verfügbaren Callback Handler finden Sie hier (unter: Table 3. List of all C#, VBA, C++, and Visual Basic callbacks and signatures). Und so könnte der Callback unseres SplitButtons aussehen:

 

public void OnSplitButton(Office.IRibbonControl control)

{

  switch (control.Id)

  {

    case "btnSplitMain":

      MessageBox.Show("Surf");

      break;

    case "btnSplitMenu1":

      MessageBox.Show("Smile");

      break;

    case "btnSplitMenu2":

      MessageBox.Show("Paint");

      break;

    case "btnSplitMenu3":

      MessageBox.Show("Filter");

      break;

    default:

      MessageBox.Show("Not implemented yet");

      break;

  }

}

 

Es handelt sich um einen Callback, da wir ja mit einer COM Applikation kommunizieren müssen. Die Verdrahtung erfolgt im Hintergrund und wird dank VSTO von uns fern gehalten. Wenn wir genauer hinschauen, werden wir sehen, dass unsere Ribbon Klasse das Microsoft.Office.Core.IRibbonExtensibility Interface implementiert und ServiceRequest und andere Helper Methoden im Hintergrund werkeln.

Was passiert nun genau, wenn ein Add-In mit einer Ribbon-Erweiterung geladen wird?

 

1.     Office lädt das Add-In

2.     Office überprüft das Add-In daraufhin, ob IRibbonExtensibility implementiert wurde

3.     Das Add-In gibt Office eine Referenz auf das Objekt, das IRibbonExtensibility implementiert

4.     Später ruft Office auf diesem Interface zurück (GetCustomUI) und holt das Ribbon XML

5.     Office parst das XML und erzeugt Tabs, Groups und Controls
(Diese Controls sind unmanaged und in Office. Wir haben keinen direkten Einfluss darauf)

6.     IRibbonExtensibility ist ein Dispatch Interface, es können beliebig viele Callbacks implementiert werden

7.     Bei Benutzerinteraktion mit diesen Controls ruft Office auf dem IRibbonExtensibility Objekt zurück und ruft den Callback Handler auf (IDispatch::Invoke)

Dynamische Inhalte für Ribbon Elemente

 

Während das erste Beispiel nur ein statisches Ribbon Element enthielt, wollen wir jetzt etwas dynamischer werden. Der folgende XML Code  erzeugt eine neue Gruppe und darin eine Gallery, welche erst zur Laufzeit mit Informationen bzw. Daten gefüttert wird.

 

<group id="GalleryGroup" label="Group with Galleries" visible="true">

  <gallery id="IconGallery"

           label="IconGallery"

           columns="3"

           rows="4"

           size="large"

           getImage="getImage"

           getItemImage="getItemImage"

           getItemCount="getItemCount"

           getItemLabel="getItemLabel"

           getItemSupertip="getItemSupertip"

           itemHeight="100" itemWidth="100"

           onAction="OnIconGalleryAction">

  </gallery>

</group>

 

Galleries sind neue Oberflächenelemente in Office 2007, die eine visuelle Repräsentation des zu erwartenden Ergebnisses – eine Art statische Vorschau – bieten können. Abbildung 4 zeigt ein Beispiel aus Powerpoint 2007.

 

 

 

Galleries können auch einfach nur eine mehrspaltige Liste von Fotos anzeigen. Genau das soll im folgenden Beispiel gemacht werden. Zum dynamischen Befüllen von Galleries benötigen wir mehrere Callback Handler.

 

·         getImage liefert das Icon des Gallery Buttons

·         getItemCount liefert die Anzahl der Gallery Items

·         getItemImage liefert die einzelnen Bilder der Gallery Items

·         getItemLabel liefert den Anzeigetext zu den Items

·         getItemSupertip liefert den Text für den erweiterten Tooltip

·         itemHeight bzw. itemWidth liefern Höhe und Breite der Bilder

·         onAction verknüpft den Click Event

 

Wir wollen Bilder, die entweder als Ressource im Add-In vorliegen oder aus einer Datenbank zur Laufzeit geholt werden, für unsere Gallery verwenden. Dafür müssen wir eine Datenbankverbindung aufmachen, ein DataSet bzw. DataTable erzeugen und diese füllen. Das sind Standard-Aufgaben, auf die hier nicht eingegangen werden soll.

 

Jetzt können wir daran gehen, die Callback Handler zu verdrahten. Wenn wir uns aber die Signatur von getImage ansehen, haben wir ein kleines Problem.

 

public stdole.IPictureDisp getImage(Office.IRibbonControl control)

 

Office möchte gerne ein COM Objekt zurück haben, welches das IPictureDisp Interface implementiert, um darüber Eigenschaften des Bildes ermitteln zu können. Unter .NET kennen wir den Typ Image. Wie können wir hier dem Wunsch von Office nachkommen? Glücklicherweise gibt es eine Klasse System.Windows.Forms.AxHost und darin eine Funktion, die wir gebrauchen können: GetIPictureDispFromPicture. Da dies eine Virtual Function ist, benötigen wir eine eigene Klasse, die von AxHost ableitet.

 

internal class ImageConverter : System.Windows.Forms.AxHost

{

  public ImageConverter() : base(string.Empty) {}

 

  static public stdole.IPictureDisp ImageToPictureDisp(Image image)

  {

    return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);

  }

 

  static public stdole.IPictureDisp IconToPictureDisp(Icon icon)

  {

    return ImageToPictureDisp(icon.ToBitmap());

  }

}

 

Ehrlich gesagt, ich hoffe stark, dass diese Konvertierungsklasse in VSTO 3 (ORCAS) nicht mehr erforderlich sein wird. Zum heutigen Zeitpunkt allerdings müssen wir uns um die Konvertierung selbst kümmern. Des Weiteren wird uns auch noch viel Arbeit beim Design von Ribbons abgenommen werden, wenn dort ebenfalls Ribbon Designer zu VSTO hinzukommen.

 

Die implementierten Callback Handler für die dynamische Gallery sehen dann folgendermaßen aus:

 

public stdole.IPictureDisp getImage(Office.IRibbonControl control)

{

  switch (control.Id)

  {

    case "IconGallery":

      return ImageConverter.ImageToPictureDisp(Properties.Resources.Office);

    default:

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

  }

}

 

public int getItemCount(Office.IRibbonControl control)

{

  return vistaIconsTable.Rows.Count;

}

 

public string getItemLabel(Office.IRibbonControl control, int index)

{

  return vistaIconsTable.FindByID(index + 1).ProgramName;

}

 

public string getItemSupertip(Office.IRibbonControl control, int index)

{

  return vistaIconsTable.FindByID(index + 1).Description;

}

 

public stdole.IPictureDisp getItemImage(Office.IRibbonControl control, int index)

{

  MemoryStream iconStream = new MemoryStream(vistaIconsTable.FindByID(index + 1).IconPic);

  return ImageConverter.ImageToPictureDisp(Image.FromStream(iconStream));

}

 

public void OnIconGalleryAction(Office.IRibbonControl control, string selectedId, int selectedIndex)

{

}

 

 

 (Fortsetzung in Teil 2)

 

  • PingBack from http://blogs.msdn.com/jensha/archive/2006/12/20/2007-microsoft-office-system-ribbonextensibility-teil-2.aspx

  • Nachdem ich zwei vorangegangenen Posts ( Teil 1 und Teil 2 ) darüber geschrieben habe, wie man Ribbons

  • hmmm, also

    > ...Microsoft Usibility Labs. Da sitzen nicht etwa Mitarbeiter von Microsoft, sondern eben jene Ottonormalbenutzer...

    Also welchem "Otto" da eingefallen ist, dass man "Einfügen" nicht im "Einfügen" Menü findet (sondern unter "Start")... Und wenn man die Anwendungs-Optionen erst mal gefunden hat, dann ist es spätestens dort mit der Übersichtlichkeit vorbei.

    Also da habt Ihr schon noch einen langen Weg vor Euch... aber ein paar ganz gute Ansätze sind ehrlicherweise auch dabei bei den Ribbons.

  • Hallo,

    der Blog war mir bisher wirklich sehr lehrreich, doch ein Problem habe ich noch. Ich will über einen Eventhandler (WindowActivate) den Wert von Checkboxen verändern, finde das entsprechende Objekt zur Referenzierung aber nicht. Jetzt habe ich hier etwas gefunden, das mich etwas erstaunt.

    Unter dem Punkt 5 bei "Was passiert nun genau, wenn ein Add-In mit einer Ribbon-Erweiterung geladen wird?" steht "(Diese Controls sind unmanaged und in Office. Wir haben keinen direkten Einfluss darauf)"

    Heißt es das was ich schon fast befürchte, nämlich dass bisher keine Möglichkeit besteht ein Referenzobjekt zu erstellen? Falls doch, wie bekomme ich dieses Referenzobjekt in VB?

    Gruß

    Jan

  • Jan,

    ich bin mir nicht sicher, ob ich die Frage richtig verstanden habe. Geht es darum, den Wert einer CheckBox auf einer TaskPane z.B. in Excel abhängig von WindowActivate zu setzen?

    Die Controls sind schon managed Controls, bekommen aber einen COM Wrapper verpasst, damit Soie in Office verwendet werden können.

    Gehen würde es z.B. so:

       public partial class ThisAddIn

       {

         public Microsoft.Office.Tools.CustomTaskPane ctp;

         public UserControl1 uc;

         private void ThisAddIn_Startup(object sender, System.EventArgs e)

         {

           uc = new UserControl1();

           ctp = this.CustomTaskPanes.Add(uc, "xxx");

           ctp.Visible = true;

           Globals.ThisAddIn.Application.WindowActivate += new Microsoft.Office.Interop.Excel.AppEvents_WindowActivateEventHandler(Application_WindowActivate);

         }

           void Application_WindowActivate(Microsoft.Office.Interop.Excel.Workbook Wb, Microsoft.Office.Interop.Excel.Window Wn)

           {

             uc.checkBox1.Checked = true;

             uc.Refresh();

             System.Windows.Forms.MessageBox.Show("Go 4 It");

           }

         }

    Voraussetzung ist, dass die CheckBox im MyTaskPane.Designer.cs (oder .vb) als Public deklariert wird.

    Bei Applikationen wie Outlook oder Word, wo das Windows Handling anders ist (jedes Fenster besitzt eine eigene Instanz der TaskPane) muss man dieses Instanzhandling erledigen (siehe VSTO Power Tools).

  • Chris,

    die Idee der Registerkarte Start (klingt komisch, Home Tab find' ich besser) ist es, die am häufigsten benutzten Funktionen zusammen zu fassen. Natürlich gebe ich Dir Recht, dass rein kontextuell Einfügen woanders hingehört.

    - Jens -

  • Hallo,

    ich suche einen Weg, einen Button in ein bestehendes Addin einzufügen - leider weiß ich die control id's der tabs nicht - da es ja tabs aus einem anderen addin sind.

    Gibt es eine Möglichkeit die in Excel bestehenden Gruppen abzufragen?

    bye, laxroth

  • laxroth,

    selbst wenn Du die Id wüßtest, würde Dir das nicht weiter helfen, denn durch die App Domain Isolation werden die AddIns voneinander getrennt. Es würde also nur ein weiterer tab mit demselben Namen aufgemacht werden. Desweiteren ist die Ladereihenfolge nicht festgelegt, so dass Dein Add-In, welches sich in das andere integrieren soll, vielleicht vor diesem geladen wird und es deshalb gar nicht findet.

Page 1 of 1 (8 items)