Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Visual Studio Tools for Office 2008 Ribbon Designer with XLINQ for Yahoo

0.00/5 (No votes)
5 Dec 2007 1  
See the new Ribbon Designer in Visual Studio 2008 allowing you to graphically create ribbons for your Office add-ins. Add images from the Yahoo Image Search API and analyse the content of your document
Screenshot - 2_-_Copy.jpg

Introduction

In this article, I will show you some basic functionality of Visual Studio Tools for Office, XLINQ, .NET framework v3.5 and the new Ribbon Designer in Visual Studio 2008 Professional. The end product will be a Microsoft Word 2007 add-in that will allow users to add images from Yahoo Images straight into their documents via a custom task panel and to find the keywords in their documents. The Yahoo REST web services will be used, and XLINQ will be used to query the XML results. The ribbon designer will be used to add buttons to the ribbon.

Visual Studio Tools for Office, part of Visual Studio 2008 Professional, allow developers to create document-centric solutions in .NET that integrate with Word, Excel, Outlook, etc with managed code.

Creating the code

To make a new Word add-in, go to New Project, Office, 2007, Word Add-in.

This should present you with:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;

namespace WordAddIn1
{
    public partial class ThisAddIn
    {
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {

        }


        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }

        #region VSTO generated code

        /// <summary />

        /// Required method for Designer support - do not modify

        /// the contents of this method with the code editor.

        /// </summary />

        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }
        
        #endregion
    }
}

To search Yahoo Images for images to add to documents, we need to create a user control to put inside a CustomTaskPane. Using WPF would be nice, and allow a more flexible UI, but we will need a bit of simple extra code to get WPF to display, since the Office requires a Windows Forms user control (later). I created a Windows Forms UserControl and left it blank. I then created a WPF user control, and laid out a simple UI to allow text entry, for searching, with a stackpanel to hold the results later.

The Yahoo Image Search API allows rest queries of the form http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=[APPID]&query=[QUERY]&start=[START]. Once the URI has been formed, we will be using XLINQ to query the results to load into a class with the fields we want.

            //Load the result as XDocument

            var xraw = XDocument.Load(uri);
            //Populate array

            var imageArray = from image in xraw.Elements().First().Elements()
                         select new YahooImage
                         {
                             Title = image.Element(
                             "{urn:yahoo:srchmi}Title").Value,

                             LargeUrl = image.Element(
                             "{urn:yahoo:srchmi}ClickUrl").Value,

                             Thumbnail = image.Element(
                             "{urn:yahoo:srchmi}Thumbnail")
                             .Element("{urn:yahoo:srchmi}Url").Value,

                             ThumbHeight = int.Parse(image.Element(
                             "{urn:yahoo:srchmi}Thumbnail")
                             .Element("{urn:yahoo:srchmi}Height").Value),

                             ThumbWidth = int.Parse(
                             image.Element("{urn:yahoo:srchmi}Thumbnail")
                             .Element("{urn:yahoo:srchmi}Width").Value),

                             Summary = image.Element(
                             "{urn:yahoo:srchmi}Summary").Value
                         };

Look at the XLINQ. It uses the result elements of the document and selects a new Yahoo Image. But no constructor is used, the properties are set between the braces. Quite a nice, readable syntax. Watch out for the namespace - it can refuse to find the elements if the namespace is omitted!

I then added each thumbnail image to a button, and placed it in the scrollable StackPanel as shown.

            //Clear old results

            stackPanel1.Children.Clear();
            foreach (var im in imageArray)
            {
                //Show image on button

                Image image = new Image();
                ImageSourceConverter isc = new ImageSourceConverter();
                image.Source = (ImageSource)isc.ConvertFromString(im.Thumbnail);

                Button b = new Button();
                b.Content = image;
                b.Width = im.ThumbWidth;
                b.Height = im.ThumbHeight;
                b.Tag = im.LargeUrl;
                b.ToolTip = im.Title + "\n\n" + im.Summary;

                b.Click += delegate(object clicked_button, RoutedEventArgs evt_args)
                    {
                        Object missing = System.Reflection.Missing.Value;
                        //On click, add image to word document

                        try
                        {
                            Globals.ThisAddIn.Application.Selection.InlineShapes.AddPicture(
                                (clicked_button as Button).Tag.ToString(),
                                ref missing, ref missing, ref missing);
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message, ex.GetType().Name);
                            Globals.ThisAddIn.CustomTaskPanes.RemoveAt(0);
                        }
                    };
                stackPanel1.Children.Add(b);
            }

Unfortunately, the thumbnail images hosted by Yahoo have faulty headers, which cause errors which stop the image displaying. To allow the images to be shown even with protocol violations, I created an App.config file and put the following inside:

  <system.net>
    <settings>
      <httpWebRequest useUnsafeHeaderParsing="true" />
    </settings>
  </system.net>

When a result is clicked, the large image is added to the document using Globals.ThisAddIn.Application.Selection.InlineShapes.AddPicture((clicked_button as Button).Tag.ToString(), ref missing, ref missing, ref missing). This method allows default parameters, but C# does not support default parameters. To send the default value, we use the value of the Missing object. This is needed in many Office functions.

Right-click the project in Solution Explorer and click Add, New Item, Ribbon (Visual Designer). This will display the ribbon designer. You can add ribbon items to either new ribbons or existing ribbons (e.g. Home, Insert, Page Layout, References). To add ribbon items to a new ribbon, click on the ribbon tab, and in properties, set the label property, and change the ControlIdType property to custom.

However, we want to add our first button to the Insert tab since we will be inserting images into the document. To do this we need to click on the tab and in properties, make sure that ControlIdType shows Office, and type the OfficeId of the tab we want to add. This can be one of the following:

  • TabHome
  • TabInsert
  • TabPageLayoutWord
  • TabReferences
  • TabMailings
  • TabReviewWord
  • TabView
  • TabDeveloper
  • TabAddIns
  • TabOutlining
  • TabPrintPreview
  • TabBlogInsert
  • TabBlogPost
  • TabSmartArtToolsDesign
  • TabSmartArtToolsFormat
  • TabChartToolsDesign
  • TabChartToolsLayout
  • TabChartToolsFormat
  • TabPictureToolsFormat
  • TabDrawingToolsFormatClassic
  • TabWordArtToolsFormat
  • TabDiagramToolsFormatClassic
  • TabOrganizationChartToolsFormat
  • TabTextBoxToolsFormat
  • TabTableToolsDesign
  • TabTableToolsLayout
  • TabHeaderAndFooterToolsDesign
  • TabEquationToolsDesign
  • TabPictureToolsFormatClassic
  • TabInkToolsPens

Screenshot - 1_-_Copy.jpg

So, to add to the insert tab, we use TabInsert. A group has already been added (more can be added from the toolbox). Controls must be placed inside groups. From the control toolbox, add a button. Since there is only one button, I will set its ControlSize to RibbonControlSizeLarge so that it looks better. The other property of note is OfficeImageId. A button can hold either no image, an image from a resource, or a built in Office image. To use an Office image, I needed to find the image's Id which can be done by downloading the Office 2007 Icons Gallery from http://www.microsoft.com/downloads/details.aspx?familyid=12b99325-93e8-4ed4-8385-74d0f7661318&displaylang=en . I chose OmsSlideInsert. Double-clicking the button brings up the click event handler, and inside it we need code to create the custom task pane and display it. An ElementHost is added to the Windows Forms user control, to host the WPF control. For this, we need WindowsFormsIntegration.dll to be referenced. An event handler is also added for when the task pane is closed, so that it can be removed from memory.

        private void button1_Click(object sender, RibbonControlEventArgs e)
        {
            // use a WPF control host

            ElementHost _ElementHost;
            _ElementHost
            = new ElementHost();
            _ElementHost.Child
            = new ImageSearchWpfUserControl();
            _ElementHost.Dock = System.Windows.Forms.DockStyle.Fill;

            // stick the WPF host into a user control

            WinFormsUserControl _UserControl;
            _UserControl = new WinFormsUserControl();
            _UserControl.Controls.Add(_ElementHost);

            // make the user control the custom pane

            Microsoft.Office.Tools.CustomTaskPane m_CustomTaskPane
            = Globals.ThisAddIn.CustomTaskPanes.Add(_UserControl, "Yahoo Images Searcher");

            m_CustomTaskPane.DockPosition =
Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionLeft;

            m_CustomTaskPane.Visible = true;
            m_CustomTaskPane.VisibleChanged +=
                new EventHandler(m_CustomTaskPane_VisibleChanged);
        }

        void m_CustomTaskPane_VisibleChanged(object sender, EventArgs e)
        {
            Microsoft.Office.Tools.CustomTaskPane ctp =
                (sender as Microsoft.Office.Tools.CustomTaskPane);
            if (ctp.Visible == false)
            {
                Globals.ThisAddIn.CustomTaskPanes.Remove(ctp);
            }
        }

Here we, again, used the wonderful Globals class, which gives easy access to your add-in and Office integration from any code file.

Screenshot - 2_-_Copy.jpg

See the Yahoo Images button on the far right, and the WPF search results.

The next task is to add a button to the Review tab (TabReviewWord) labelled Keyword Analysis, which will send the document to the Yahoo Content Analysis web service (after trying it it's not that good but oh well) to get keywords for the document. I added a new tab to the ribbon, and added a group with a button. The button's OfficeImageId was set to FunctionsRecentlyUsedtInsertGallery. On clicking the button, we need to post the data to the web service, and then iterate through the results before displaying them in a standard MessageBox. To get the document text I used Globals.ThisAddIn.Application.ActiveDocument.Content.Text . Many other properties and functions can be found by browsing the intellisense. Below, note the Keyword Analysis button and see that the web service has identified relevant keywords to the active document.

Screenshot - 3_-_Copy.jpg

        private void button2_Click(object sender, RibbonControlEventArgs e)
        {
            //Send query by post

            System.Net.WebClient wc = new System.Net.WebClient();
            System.Collections.Specialized.NameValueCollection nvc =
                new System.Collections.Specialized.NameValueCollection();
            nvc.Add("appid", Properties.Settings.Default.AppId);
            nvc.Add("context",
                Globals.ThisAddIn.Application.ActiveDocument.Content.Text);
            XDocument xdoc = XDocument.Parse(Encoding.UTF8.GetString(
                wc.UploadValues("http://search.yahooapis.com/ContentAnalysisService/V1/termExtraction", 
                "POST", nvc)));
            //Iterate through keywords

            StringBuilder keywords = new StringBuilder();
            foreach (var xresult in xdoc.Elements().First().Elements())
            {
                keywords.AppendLine(xresult.Value);
            }
            //Show a message box!

            System.Windows.Forms.MessageBox.Show(
                keywords.ToString(), "Keywords");
        }

This was a quick intro to, among other things, the new Ribbon Designer in Visual Studio. The Ribbon Designer also allows you to add new menu items to the Office Button as well as allowing you to (irreversibly) export the ribbon to XML for some more advanced functions such as repurposing commands. For example, if you repurpose FileSave, whenever a user presses Ctrl+S, or clicks Save instead of the normal save dialog opening, your custom code can run. To try this, export the XML from the ribbon designer, follow instructions in the comments in the code and add the following XML and C# code, which will ask a user if he/she really wants to save before saving:

<?xml version="1.0" encoding="UTF-8"?>
<customUI onLoad="Ribbon_Load" xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<!--HERE-->
   <commands> 
     <command idMso="FileSave" onAction="doYouWantToSave" /> 
   </commands> 
<!--/HERE-->
    <ribbon>
        <tabs>
            <tab idMso="TabInsert">
                <group id="group1" label="Web">
                    <button id="button1" imageMso="OmsSlideInsert" 
onAction="button1_Click" screentip="Yahoo! Images" 
supertip="Search Yahoo! Images for images to add to your document." label="Yahoo Images" 
size="large" />
                </group>
            </tab>
            <tab idMso="TabReviewWord">
                <group id="group2" label="Analysis">
                    <button id="button2" imageMso="FunctionsRecentlyUsedtInsertGallery" 
onAction="button2_Click" 
screentip="Keyword Analysis" supertip="Find keywords in this document using Yahoo! web service" 
label="Keyword Analysis" size="large" />
                </group>
            </tab>
    </ribbon>
</customUI>

        private void doYouWantToSave(Microsoft.Office.Core.IRibbonControl control, bool cancel)
        {
            cancel = (System.Windows.Forms.MessageBox.Show("Do you really want to do FileSave?",
            "???", System.Windows.Forms.MessageBoxButtons.YesNo)
                        == System.Windows.Forms.DialogResult.OK) ? false : true;
        }

The attached code also contains a ribbon tab called mess. This has visible = false set by default, but after changing that, you can see how other controls can be added to the ribbon (besides buttons) such as galleries, button groups, toggle buttons. Notice how it appears before the Mailings tab � this is due to the Position property being set.

Screenshot - 4_-_Copy.jpg

The code Download WordAddIn1.zip - 132.9 KB is functional, and it fulfils its purpose of showing basic integration with Office using VS 2008, and some XLINQ. Enjoy. A brief description of how to use the article or code. The class names, the methods and properties, any tricks or tips. Blocks of code should be set as style "Formatted" like this:

Points of Interest

Annoying: The thumbnail images being hosted on a bad server which gives protocol violations

Nice: There is a function for practically anything you can do in Office

Nice: To install, go to the debug folder and just doubleclick the WordAddIn1.vsto! Easy.

Important: There are loads of codes/OfficeIds for things but they can be found on downloadable spreadsheets/documents.

Nice: It actually works quite well.

History

28 November 2007 - Added original article

30 November 2007, 5 December 2007 - Minor update

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here