Pieter van der Westhuizen

Create a custom Outlook view programmatically: C# sample

When creating Outlook add-ins I used to always use the Add-in Express Web view when I wanted to display my own data in the folder view. Until recently, when I realized that Outlook does expose a lot of properties that make it easy to display your custom data using the built-in Outlook types as well as Outlook views.

In today’s article I’ll walk you through importing product data from the Northwind database and only display the fields of the Northwind Products table in an Outlook view.

Creating the Visual Studio project

Start by creating a new ADX COM Add-in project in Visual Studio with Add-in Express for Office and .net.

Creating a new COM Add-in project in Visual Studio

Select Visual C# Project and Microsoft Office 2007 as the minimum supported version, which means that our add-in will work for Outlook 2007, 2010 and 2013.

Selecting Visual C# Project and Microsoft Office 2007

And lastly, select Microsoft Outlook for our supported application.

Selecting Microsoft Outlook for our supported application

Add a button to the Outlook Explorer ribbon

Next, we need to add a button to the Outlook Explorer ribbon, to do this click on the ADXRibbonTab toolbar button. With the Ribbon Tab visual designer open, add a Ribbon Group and Ribbon button.

Adding a button to the Outlook Explorer ribbon

Select the Ribbon button and add an event handler for its OnClick event by double-clicking next to the OnClick event in the Visual Studio properties window.

Adding an event handler for the OnClick event

Add the following code to the button’s OnClick event:

private void importProductssRibbonButton_OnClick(object sender,
	IRibbonControl control, bool pressed)
{
    Outlook._NameSpace session = null;
    Outlook.Folder destinationFolder = null;
 
    try
    {
        session = OutlookApp.GetNamespace("MAPI");
        destinationFolder = session.PickFolder() as Outlook.Folder;
 
        if (destinationFolder != null &&
            destinationFolder.DefaultItemType == Outlook.OlItemType.olContactItem)
        {
            LoadData(destinationFolder);
            SetupView(destinationFolder);
        }
    }
    finally
    {
        if (session != null)
            Marshal.ReleaseComObject(session);
        if (destinationFolder != null)
            Marshal.ReleaseComObject(destinationFolder);
    }
}

Loading the data

By using the PickFolder method of the Outlook NameSpace object, the user can select a folder in which they would like to import the employees from the Northwind database. The PickFolder method returns a MAPIFolder object and will return a null if the user cancels the pick folder dialog.

We then verify that we received a valid folder by checking that it is not null and its default item type is Outlook contact items. Once we’re happy that the user did select a valid folder, we load the data from the database into the LoadData method.

The code listing for the LoadData method looks like this:

private void LoadData(Outlook.MAPIFolder destinationFolder)
{
    Outlook.Items destinationFolderItems = null;
 
    SqlConnection conn = null;
    SqlCommand cmd = null;
    SqlDataReader reader = null;
 
    try
    {
        conn = new SqlConnection("Data Source=(local);" +
			"Initial Catalog=Northwind;Integrated Security=SSPI;");
        cmd = new SqlCommand("Select * from [Products by Category]", conn);
        conn.Open();
        reader = cmd.ExecuteReader();
        destinationFolderItems = destinationFolder.Items;
 
        while (reader.Read())
        {
            Outlook._ContactItem productItem =
				destinationFolderItems.Add(Outlook.OlItemType.olContactItem) as
				Outlook._ContactItem;
            Outlook.UserProperties productUserProperties = productItem.UserProperties;
 
            Outlook.UserProperty categoryNameUserProperty =
				productUserProperties.Find("Category Name");
            if (categoryNameUserProperty != null)
            {
                categoryNameUserProperty.Value = reader["CategoryName"].ToString();
            }
            else
            {
                categoryNameUserProperty = productUserProperties.Add(
					"Category Name", Outlook.OlUserPropertyType.olText);
                categoryNameUserProperty.Value = reader["CategoryName"].ToString();
            }
 
            Outlook.UserProperty productNameUserProperty =
				productUserProperties.Find("Product Name");
            if (productNameUserProperty != null)
            {
                productNameUserProperty.Value = reader["ProductName"].ToString();
            }
            else
            {
                productNameUserProperty = productUserProperties.Add(
					"Product Name", Outlook.OlUserPropertyType.olText);
                productNameUserProperty.Value = reader["ProductName"].ToString();
            }
 
            Outlook.UserProperty quantityPerUnitUserProperty =
				productUserProperties.Find("Quantity Per Unit");
            if (quantityPerUnitUserProperty != null)
            {
                quantityPerUnitUserProperty.Value = reader["QuantityPerUnit"].ToString();
            }
            else
            {
                quantityPerUnitUserProperty = productUserProperties.Add(
					"Quantity Per Unit", Outlook.OlUserPropertyType.olText);
                quantityPerUnitUserProperty.Value = reader["QuantityPerUnit"].ToString();
            }
 
            Outlook.UserProperty unitsInStockProperty =
				productUserProperties.Find("Units In Stock");
            if (unitsInStockProperty != null)
            {
                unitsInStockProperty.Value = Convert.ToInt32(reader["UnitsInStock"]);
            }
            else
            {
                unitsInStockProperty = productUserProperties.Add(
					"Units In Stock", Outlook.OlUserPropertyType.olInteger);
                unitsInStockProperty.Value = Convert.ToInt32(reader["UnitsInStock"]);
            }
 
            Outlook.UserProperty discontinuedUserProperty =
				productUserProperties.Find("Discontinued");
            if (discontinuedUserProperty != null)
            {
                discontinuedUserProperty.Value = Convert.ToInt32(reader["Discontinued"]);
            }
            else
            {
                discontinuedUserProperty = productUserProperties.Add(
					"Discontinued", Outlook.OlUserPropertyType.olYesNo);
                discontinuedUserProperty.Value =
					Convert.ToBoolean(reader["Discontinued"]);
            }
 
            productItem.Save();
 
            if (productItem != null)
                Marshal.ReleaseComObject(productItem);
            if (productUserProperties != null)
                Marshal.ReleaseComObject(productUserProperties);
            if (categoryNameUserProperty != null)
                Marshal.ReleaseComObject(categoryNameUserProperty);
            if (productNameUserProperty != null)
                Marshal.ReleaseComObject(categoryNameUserProperty);
            if (quantityPerUnitUserProperty != null)
                Marshal.ReleaseComObject(quantityPerUnitUserProperty);
            if (unitsInStockProperty != null)
                Marshal.ReleaseComObject(unitsInStockProperty);
            if (discontinuedUserProperty != null)
                Marshal.ReleaseComObject(discontinuedUserProperty);
        }
    }
    finally
    {
        if (destinationFolderItems != null)
            Marshal.ReleaseComObject(destinationFolderItems);
    }
}

In the LoadData method, we were connected to the Northwind database and retrieved a list of products. Each product is then added as an Outlook ContactItem to the folder the user selected. We’re not using any of the standard Outlook ContactItem properties in this case. We’ll add a user property to the new ContactItem for each column in the Northwind employees table.

Once the products are loaded into Outlook, we need to setup the folder view to only show the fields related to the product and hide the Outlook ContactItem fields. We’ll be using the SetupView method to accomplish this.

Customizing the Outlook Folder View

The code listing for the SetupView looks like this:

private void SetupView(Outlook.MAPIFolder destinationFolder)
{
    Outlook.Views productFolderViews = null;
    Outlook.TableView productTableView = null;
    Outlook.ViewFields productViewFields = null;
 
    Outlook.ViewField categoryNameViewField = null;
    Outlook.ColumnFormat categoryNameColumnFormat = null;
 
    Outlook.ViewField productNameViewField = null;
    Outlook.ColumnFormat productNameColumnFormat = null;
 
    Outlook.ViewField quantityPerUnitViewField = null;
    Outlook.ColumnFormat quantityPerUnitColumnFormat = null;
 
    Outlook.ViewField unitsInStockViewField = null;
    Outlook.ColumnFormat unitsInStockColumnFormat = null;
 
    Outlook.ViewField discontinuedViewField = null;
    Outlook.ColumnFormat discontinuedColumnFormat = null;
 
    try
    {
        productFolderViews = destinationFolder.Views;
        productTableView = productFolderViews.Add("Northwind products View",
			Outlook.OlViewType.olTableView,
			Outlook.OlViewSaveOption.olViewSaveOptionThisFolderEveryone
			) as Outlook.TableView;
        productViewFields = productTableView.ViewFields;
 
        productTableView.XML = Properties.Settings.Default.ViewXML;
 
        categoryNameViewField = productViewFields.Add("Category Name");
        categoryNameColumnFormat = categoryNameViewField.ColumnFormat;
        categoryNameColumnFormat.Align = Outlook.OlAlign.olAlignLeft;
        categoryNameColumnFormat.Width = 25;
 
        productNameViewField = productViewFields.Add("Product Name");
        productNameColumnFormat = productNameViewField.ColumnFormat;
        productNameColumnFormat.Align = Outlook.OlAlign.olAlignLeft;
        productNameColumnFormat.Width = 50;
 
        quantityPerUnitViewField = productViewFields.Add("Quantity Per Unit");
        quantityPerUnitColumnFormat = quantityPerUnitViewField.ColumnFormat;
        quantityPerUnitColumnFormat.Align = Outlook.OlAlign.olAlignLeft;
        quantityPerUnitColumnFormat.Width = 25;
 
        unitsInStockViewField = productViewFields.Add("Units In Stock");
        unitsInStockColumnFormat = unitsInStockViewField.ColumnFormat;
        unitsInStockColumnFormat.Align = Outlook.OlAlign.olAlignCenter;
        unitsInStockColumnFormat.Width = 25;
 
        discontinuedViewField = productViewFields.Add("Discontinued");
        discontinuedColumnFormat = discontinuedViewField.ColumnFormat;
        discontinuedColumnFormat.Align = Outlook.OlAlign.olAlignCenter;
        discontinuedColumnFormat.Width = 15;
 
        productTableView.Save();
        productTableView.Apply();
    }
    finally
    {
        if (productFolderViews != null)
            Marshal.ReleaseComObject(productFolderViews);
        if (productTableView != null)
            Marshal.ReleaseComObject(productTableView);
        if (productViewFields != null)
            Marshal.ReleaseComObject(productViewFields);
        if (categoryNameViewField != null)
            Marshal.ReleaseComObject(categoryNameViewField);
        if (categoryNameColumnFormat != null)
            Marshal.ReleaseComObject(categoryNameColumnFormat);
        if (productNameViewField != null)
            Marshal.ReleaseComObject(productNameViewField);
        if (productNameColumnFormat != null)
            Marshal.ReleaseComObject(productNameColumnFormat);
        if (quantityPerUnitViewField != null)
            Marshal.ReleaseComObject(quantityPerUnitViewField);
        if (quantityPerUnitColumnFormat != null)
            Marshal.ReleaseComObject(quantityPerUnitColumnFormat);
        if (unitsInStockViewField != null)
            Marshal.ReleaseComObject(unitsInStockViewField);
        if (unitsInStockColumnFormat != null)
            Marshal.ReleaseComObject(unitsInStockColumnFormat);
        if (discontinuedViewField != null)
            Marshal.ReleaseComObject(discontinuedViewField);
        if (discontinuedColumnFormat != null)
            Marshal.ReleaseComObject(discontinuedColumnFormat);
    }
}

In the SetupView method, we first created a new view called “Northwind products View”. Next – to essentially clear the default view – we set the XML property to a XML string stored in our application settings file.

The XML markup looks like this:

<?xml version="1.0"?>
<view type="table">
  <viewname>Northwind Employees View</viewname>
  <viewstyle>
	table-layout:fixed;width:100%;font-family:Segoe UI;font-style:normal;
	font-weight:normal;font-size:8pt;color:Black;font-charset:0
  </viewstyle>
  <viewtime>216300224</viewtime>
  <linecolor>8421504</linecolor>
  <linestyle>3</linestyle>
  <gridlines>1</gridlines>
  <autosizing>0</autosizing>
  <newitemrow>1</newitemrow>
  <incelledit>1</incelledit>
  <collapsestate/>
  <rowstyle>background-color:White;color:Black</rowstyle>
  <headerstyle>background-color:#D3D3D3</headerstyle>
  <previewstyle/>
  <arrangement>
    <autogroup>0</autogroup>
    <enablexfc>0</enablexfc>
    <collapseclient/>
    <collapseconv/>
  </arrangement>
  <multiline>
    <gridlines>0</gridlines>
  </multiline>
  <column>
    <name>HREF</name>
    <prop>DAV:href</prop>
    <checkbox>1</checkbox>
  </column>
  <column>
    <format>boolicon</format>
    <heading>Attachment</heading>
    <prop>urn:schemas:httpmail:hasattachment</prop>
    <type>boolean</type>
    <bitmap>1</bitmap>
    <width>18</width>
    <style>padding-left:3px;;text-align:center</style>
    <editable>0</editable>
    <displayformat>3</displayformat>
  </column>
  <orderby>
    <order>
      <heading>Attachment</heading>
      <prop>urn:schemas:httpmail:hasattachment</prop>
      <type>string</type>
      <sort>asc</sort>
    </order>
  </orderby>
  <groupbydefault>0</groupbydefault>
  <previewpane>
    <markasread>0</markasread>
  </previewpane>
</view>

You can set up the entire Outlook view using XML markup, but for this example we’ll add the required fields using the Outlook object model.

You need to make sure that the user properties we want to add to the view already exist in the folder. We’ve created the user properties in the folder automatically when we’ve added the product item to the folder earlier.

From there, all you need to do is create a new Outlook.ViewField object for each field and add it to the Outlook folder view after setting its properties such as the column alignment and width:

categoryNameViewField = productViewFields.Add("Category Name");
categoryNameColumnFormat = categoryNameViewField.ColumnFormat;
categoryNameColumnFormat.Align = Outlook.OlAlign.olAlignLeft;
categoryNameColumnFormat.Width = 25;

Once all your code is in place, you can build, register and run you project. In Outlook, click the Import Products Data button; you will be prompted to choose a folder where you would like to import the Products to.

Choosing a folder to import the Products to

The products should then import into the selected folder and the Outlook view will look something like this:

Custom view of the Outlook Contacts folder

In my next article I’ll walk you through creating a custom form and ribbon for creating a Northwind product in Outlook and how to show the end-user the form when they create a new item in the Products folder.

Thank you for reading. Until next time, keep coding!

Available downloads:

This sample COM Add-in was developed using Add-in Express for Office and .net:

C# sample COM add-in

You may also be interested in:

13 Comments

  • Ankit Kumar says:

    Hi pieter

    I am not able to add an event handler for OnClick event of a button as Events and property pages is disabled for adxribbonbutton that I have added

    Regards
    Ankit

  • Pieter van der Westhuizen says:

    Hi Ankit,

    Mmmm…that is weird. So when you select the button that is inside a ribbon group on a ribbon tab, do you see all its properties in the properties window but the event list is disabled?
    Have you tried double-clicking next to the event name in the properties window?

  • Ankit Kumar says:

    Thanks for the expeditious reply Pieter!!!

    Yes I can see 3 items viz ‘categorized’ ‘alphabetical’ and ‘property pages’ for a button
    but the ‘property pages’ is disabled.
    Also I am not able to see the ‘properties’ and ‘events’ item for the button
    But all of these items are visible and enabled for adxribbontab

    Is there any particular setting for this?

  • Ankit Kumar says:

    When I right click on adxribbontab1 in the designer.cs file and select edit controls

    I can see the following hierarchy

    Ribbon tab
    Ribbon group
    Button

    For all the 3 elements the behaviour is same which I mentioned in the previous mail

  • Pieter van der Westhuizen says:

    Ok. Did you double-click on AddinModule.cs and it opens the designer surface from there you add the ribbon tab by clicking on its button in the toolbar?

    Have you tried adding a new ribbon tab to see if you see the same behavior? Which version of visual studio and Add-in Express are you using?

  • Ankit Kumar says:

    Yes is the answer for both of your questions.

    Microsoft Visual studio 2008 professional edition enu
    Add in express 7

  • Dmitry Kostochko (Add-in Express Team) says:

    Hi Ankit,

    A very interesting issue! Could you please send us a screenshot of your Visual Studio with the visual designer opened, in which the ribbon button is selected and the Properties window visible?

    You can find our support email address in the readme.txt file located in the Add-in Express installation folder. Thank you!

  • Anderson says:

    Hi there, i am also failing to see the OnClick event handler on the Buttons Properties,
    I also downloaded the sample, and am still failing to find it.
    Please let me know if this issue has been resolved.
    (I’m using Add-in Express for Microsoft Office and .net, Standard)

    Best Wishes
    Anderson

  • Anderson says:

    Nevermind, found it

  • Dmitry Kostochko (Add-in Express Team) says:

    Hi Anderson,

    Thank you for letting us know.

    I suppose you used the modal editor with the old visual designer. Am I right? We will need to disable those old-style editors, so that they do not cause any confusion.

  • Emre says:

    Hi, where is the Import Products Data button In Outlook?

  • Andrei Smolin (Add-in Express Team) says:

    Hello Emre,

    That button is a custom button created by the add-in described. Alas, I’ve found out that the project supplied and the code provided in the blog don’t match. And a correct project is unavailable. I’ll re-create the project using the code provided in the article and publish the add-in project here. Meanwhile, you can re-create the project yourself: the blog seemingly provides all the code required to do so.

  • Andrei Smolin (Add-in Express Team) says:

    Hello Emre,

    I’ve uploaded the modified project. Please use the link above to re-download it. The Products Data button is shown on the Nothwind tab in the Explorer window.

Post a comment

Have any questions? Ask us right now!