views:

507

answers:

3

I would like to add a new section to the To-Do Bar in Outlook 2010 (or 2007). I found some code to create a new collapsible task pane and someone claiming you can't modify the To-Do bar, but I also found a product called Add-In Express that claims it can do it (although at $349 it's not worth it for a one-off project).

Is is possible to do that?

A: 

Michael,

Take a look at Outlook form regions. http://msdn.microsoft.com/en-us/library/bb386301.aspx You can add them using a VSTO addin. Though Add-in express has a few more options to where you can add them. There are quite a few tutorials on the net as well.

Marcus

76mel
The problem is attaching/adjoining the new form region to the ToDo bar. I know how to attach it to the mail window and many of the "standard" forms, but there seems no official way to attach it to the ToDo Bar?
Michael Stum
+2  A: 

What you're looking for is called a TaskPane, not exactly a Form Region. TaskPanes work a little differently than Form Regions and they are only available in Office 2007 and higher (which doesn't look like it will be a problem for you since you need this for 2007-2010). If nothing else at least knowing the right term might make googling for this a bit easier.

Here's a Custom Task Panes Overview on MSDN.

Now, as far as adding a section to an existing TaskPane, I do not know for sure. But hopefully that gets you a bit closer.

BTW, as a side note, the Add-in Express libraries are awesome. Make this sort of thing a 2-minute task. Highly recommended - and it is likely something you'll use again since they make the job so easy.

Ryan Farley
+2  A: 

After some research (and after having seen the product documentation of Add-in Express), I figured that it is possible to customize the To-Do Bar in Outlook 2007.

There is a proof-poof-concept on CodeProject that embeds a "custom" (read self-written) pane into Outlooks main window. The article has been written by Lukas Neumann and is available here:

Additional custom panel in Microsoft Outlook

The principle is the following:

  1. Search the Outlook window for the child window where you want to place your own window (i.e. the To-Do Bar child window)
  2. Resize the contents of that window to make some space for your controls
  3. Add your own window as a child
  4. Subclass the To-Do Bar window to hook into the message loop of that window

There is basically only two modifications that need to be done to adjust the sample code:

  1. Get the correct child window handles: The window class of the To-Do Bar is called "WUNDERBAR". This class is used for several child windows so make sure to also check for the correct window title ("ToDoBar") or search by window title only.
  2. Get the resizing of the panel right (simple but not always easy ;-).

(And add some proper error handling if the To-Do Bar is not found etc).

It's a strong plus if you are familiar with Spy++ as it is needed to find out the class names and window titles of Outlook's child windows.

I suggest you to download the sample code and apply the following modifications:

In Connect.cs:

private const string SIBLING_WINDOW_CLASS = "NetUINativeHWNDHost";
public delegate bool EnumChildCallback(IntPtr hwnd, ref IntPtr lParam);

[DllImport("User32.dll")]
public static extern bool EnumChildWindows(IntPtr hWndParent, EnumChildCallback lpEnumFunc, ref IntPtr lParam);

[DllImport("User32.dll")]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);

public static bool EnumChildProc(IntPtr hwndChild, ref IntPtr lParam)
{
    StringBuilder className = new StringBuilder(128);
    GetClassName(hwndChild, className, 128);

    int length = GetWindowTextLength(hwndChild);
    StringBuilder windowText = new StringBuilder(length + 1);
    GetWindowText(hwndChild, windowText, windowText.Capacity);

    if (className.ToString() == "WUNDERBAR" && windowText.ToString() == "ToDoBar")
    {
        lParam = hwndChild;
        return false;
    }
    return true;
}

public void OnStartupComplete(ref System.Array custom)
{
    if (_outlookApplication == null)
        return; //We were not loaded into Outlook, so do nothing

    //Get the instance of Outlook active explorer (= the main window) and start capturing selection changes
    _outlookExplorer = _outlookApplication.ActiveExplorer();
    _outlookExplorer.SelectionChange += new ExplorerEvents_10_SelectionChangeEventHandler(outlookExplorer_SelectionChange);

    //Find Outlook window handle (HWND)
    IntPtr outlookWindow = FindOutlookWindow();

    if (outlookWindow == IntPtr.Zero)
        return;

    // Find ToDoBar window handle
    IntPtr todoBarWindow = IntPtr.Zero;
    EnumChildCallback cb = new EnumChildCallback(EnumChildProc);
    EnumChildWindows(outlookWindow, cb, ref todoBarWindow);

    if (todoBarWindow == IntPtr.Zero)
        return;

    //Find sibling window handle (HWND)
    //Sibling window is the window which we are going to "cut" to make space for our own window
    IntPtr siblingWindow = SafeNativeMethods.FindWindowEx(todoBarWindow, IntPtr.Zero, SIBLING_WINDOW_CLASS, null);
    if (siblingWindow == IntPtr.Zero)
        return;

    //Initialise PanelManager and assign own panel to it
    _panelManager = new PanelManager(outlookWindow, siblingWindow);
    _customPanel = new MyPanel();
    _panelManager.ShowBarControl(_customPanel);
}

In PanelManager.cs:

private void ResizePanels()
{
    if (_changingSize)
        return; //Prevent infinite loops

    _changingSize = true;

    try
    {
        //Get size of the sibling window and main parent window
        Rectangle siblingRect = SafeNativeMethods.GetWindowRectangle(this.SiblingWindow);
        Rectangle parentRect = SafeNativeMethods.GetWindowRectangle(this.ParentWindow);

        //Calculate position of sibling window in screen coordinates
        SafeNativeMethods.POINT topLeft = new SafeNativeMethods.POINT(siblingRect.Left, siblingRect.Top);
        SafeNativeMethods.ScreenToClient(this.ParentWindow, ref topLeft);

        //Decrease size of the sibling window
        int newHeight = parentRect.Height - topLeft.Y - _panelContainer.Height;
        SafeNativeMethods.SetWindowPos(this.SiblingWindow, IntPtr.Zero, 0, 0, siblingRect.Width, newHeight, SafeNativeMethods.SWP_NOMOVE | SafeNativeMethods.SWP_NOZORDER);

        //Move the bar to correct position
        _panelContainer.Left = topLeft.X;
        _panelContainer.Top = topLeft.Y + newHeight;

        //Set correct height of the panel container
        _panelContainer.Width = siblingRect.Width;
    }
    finally
    {
        _changingSize = false;
    }
}

The proof-of-concept is a managed COM Add-in and not using VSTO, but a very similar approach should also work for VSTO. Let me know in case you need any further help, as the proof-of-concept already requires some knowledge about subclassing and the Office add-in architecture (IDTExtensibility2).

Please also consider that this is just a proof-of-concept showing the basic technique how to customize the Outlook UI. And my edits are far from beautiful code ;-)

0xA3
That works after some tweaking, although now I realize it's rather ugly and hacky, so I'll have to think if I really want that in the end. Thanks for the effort.
Michael Stum
Thanks too. Indeed it is rather ugly and hacky, and it leads straight into problems if several add-ins compete about space in the ToDo bar. It might be safe to do in controlled (corporate) environments, but out in the wild...
0xA3