Creating a TFS Work Item from an Outlook mail message using VSTO


Many times new work-items are the by-product of an email thread. Sometimes it a question that turns into a feature request or a bug report from an external customer. When this happens I need to move the email message to one screen and open VS in another. Browse out to the Work Item menu and create a new work item. Go back to Outlook and CTRL+A, CTRL+C to get the message, go back to VS and paste it … not really a big deal but why not streamline it a bit more?


So I spent a few minutes looking at the VSTO wizard that comes with Visual Studio 2005 (see more here: http://msdn.microsoft.com/office/understanding/vsto/training/labs05/default.aspx) and whipped up a quick Outlook add-in that lets me right-click on an Email and create a new workitem from it.  You can see an example of the context menu created in Outlook when your right-click on an email message.  Notice the “Create Workitem” menu item.


The email subject becomes the bug title and the email body the description. It would be just a tad more work to save the email as a temp file and make it an attachment instead.



To get started you will want to create an Outlook Add-In project (this means you need to have VSTO installed!).  This will create a lot of the plumbing code you need to make this work.  I’ve only included the code I modified.  The link I gave earlier includes walk-throughs that will teach you how to do this.


The code is fairly well documented so I won’t spend a lot of time on it.  This is a quick hack – about 30 minutes of work (which was mostly figuring out how to write an Outlook add-in) and it is pretty rigid.  A better solution would be to let the user configure the project and server name.  Also support for mandatory fields or custom fields would need to be added – but I think this shows how easy it is to extend Outlook in a time-saving way.


using System;


using System.Windows.Forms;


using Microsoft.VisualStudio.Tools.Applications.Runtime;


using Outlook = Microsoft.Office.Interop.Outlook;


using Office = Microsoft.Office.Core;


using System.Collections.Generic;


using Microsoft.Office.Interop.Outlook;


using Microsoft.TeamFoundation.Client;


using Microsoft.TeamFoundation.WorkItemTracking.Client;


 


namespace EmailToWorkitem


{


    public partial class ThisApplication


    {


        // the prompt and action name


        const string createNewPrompt = “Create Workitem”;


 


        Outlook.Explorer _explorer = null;


 


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


        {


            // cache the explorer object


            _explorer = this.Explorers.Application.ActiveExplorer();


 


            // when an email selection changes this event will fire


            _explorer.SelectionChange += new ExplorerEvents_10_SelectionChangeEventHandler(_explorer_SelectionChange);


        }


 


        // event fired when any selection changes.


        void _explorer_SelectionChange()


        {


            foreach (object selectedItem in _explorer.Selection)


            {


                // we only want to deal with selected mail items


                MailItem item = selectedItem as MailItem;


                if (item != null)


                {


                    // see if the action already exists on mail item


                    Action newAction = item.Actions[createNewPrompt];


 


                    // and create it if it does not


                    if(newAction == null)


                    {


                        newAction = item.Actions.Add();


                        newAction.Name = createNewPrompt;


                        newAction.ShowOn = OlActionShowOn.olMenu;


                        newAction.Enabled = true;


                        item.Save();


                    }


 


                    // add the event handler for our action


                    item.CustomAction += new ItemEvents_10_CustomActionEventHandler(item_CustomAction);


                }


            }


        }


 


        void item_CustomAction(object Action, object Response, ref bool Cancel)


        {


            try


            {


                Action mailAction = (Action)Action;


                switch (mailAction.Name)


                {


                    // only process the action we know about


                    case createNewPrompt:


                        try


                        {


                            MailItem mailItem = _explorer.Selection[1] as MailItem;


                            if (mailItem != null)


                            {


                                // TODO: modify the server details to point you your server


                                TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(“http://yourserver:8080”);


                                WorkItemStore store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));


 


                                // TODO: modify the project name to your project


                                WorkItemTypeCollection workItemTypes = store.Projects[“YOURPROJECT”].WorkItemTypes;


 


                                // Enter the work item as a bug


                                WorkItemType wit = workItemTypes[“bug”];


                                WorkItem workItem = new WorkItem(wit);


 


                                workItem.Title = mailItem.Subject;


                                workItem.Description = mailItem.Body;


                                workItem.Save();


 


                                MessageBox.Show(string.Format(“Created bug {0}”, workItem.Id));


                            }


                            else


                            {


                                MessageBox.Show(“Unable to convert selected item to a mail item”);


                            }


                        }


                        finally


                        {


                            Cancel = true;


                        }


                        break;


                       


                }


            }


            catch (System.Exception e)


            {


                MessageBox.Show(e.ToString());


            }


        }


 


        private void ThisApplication_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(ThisApplication_Startup);


            this.Shutdown += new System.EventHandler(ThisApplication_Shutdown);


        }


 


        #endregion


    }


}


 



 



Comments (5)

  1. Rob Caron tells us the localized Team Foundation Server versions have shipped. He also posts about the…

  2. A while back, I took at a look at Personify Design’s Teamlook tool that provided functionality within…

  3. Buck Hodges says:

    I recently had to put together a list of links to code samples.&amp;nbsp; This isn’t even close to comprehensive,…

  4. Buck Hodges says:

    I recently had to put together a list of links to code samples.&amp;nbsp; This isn’t even close to comprehensive,…

  5. .NETicated says:

    General OzTFS.com – Australian Team Foundation Server online community Radio TFS – Team System Talk Radio