Pieter van der Westhuizen

Programming for Microsoft Project 2013

Microsoft Project is a product that not many people realize is part of the Microsoft Office family. It is also a product that most users do not have on their PC. Where you do see it, however, is on the computer of many project managers and herein lies the opportunity for us as Office developers.

Microsoft Project, as with all the applications in the MS Office suite, provides developers with a rich object model. This, combined with the power of Add-in Express, makes Microsoft Project a prime candidate for customization.

Microsoft Project base objects

Let’s begin with having a closer look at the main object of Microsoft Project. Let’s run through them one at a time.

Application object

As with all the Microsoft Office application object models, the one object almost all other objects originate from is the Application object. When starting a new Add-in Express project that targets Microsoft Project, you will automatically get a reference to the Application object in the form of the MSProjectApp property in the AddinModule class:

public MSProject._MSProject MSProjectApp
{
    get
    {
        return (HostApplication as MSProject._MSProject);
    }
}

Projects / Project object

The MS Project Application object contains a collection object called Projects. As the name implies, it contains a reference to all the open MS Project files. The Application object provides a property with which you can get a reference to the active project, called ActiveProject.

MSProject.Project project = MSProjectApp.ActiveProject;

Use the project object as the starting point for most MS Project add-ins, because with this object you can gain access to almost all properties of the project, including resources and tasks.

Selection object

The Selection object is, to a certain extent, similar to the Selection objects of the rest of the Office family. In Microsoft Project, the user is able to select either tasks or resources via the user interface and the MS Project object model caters for this. In the following code example, we will check whether the user has selected either tasks or resources and then display a message box showing all the selected task or resource names:

private void adxRibbonButton1_OnClick(object sender, 
    IRibbonControl control, bool pressed)
{
    MSProject.Selection selection = null;
    MSProject.Tasks selectedTasks = null;
    MSProject.Resources selectedResources = null;
    MSProject.Task task = null;
    MSProject.Resource resource = null;
    string taskList = string.Empty;
    string resourceList = string.Empty;
 
    try
    {
        selection = MSProjectApp.ActiveSelection;
        selectedTasks = selection.Tasks;
        selectedResources = selection.Resources;
 
        if (selectedTasks != null)
        {
            for (int t = 1; t <= selectedTasks.Count; t++)
            {
                task = selectedTasks[t];
                int duration = Convert.ToInt32(task.Duration);
                taskList += string.Format(
                    "{0}({1} hours){2}", 
                    task.Name, duration / 60, Environment.NewLine);
                if (task != null) Marshal.ReleaseComObject(task);
            }
            MessageBox.Show(string.Format(
                "{0} tasks selected: {1}{2}", 
                selectedTasks.Count, Environment.NewLine, taskList));
        }
 
        if (selectedResources != null)
        {
            for (int r = 1; r <= selectedResources.Count; r++)
            {
                resource = selectedResources[r];
                resourceList += resource.Name + 
                    "(" + resource.StandardRate + ")" + Environment.NewLine;
                if (resource != null) Marshal.ReleaseComObject(resource);
            }
            MessageBox.Show(string.Format(
                "{0} resources selected: {1}{2}", 
                selectedResources.Count, Environment.NewLine, resourceList));
        }
    }
    finally
    {
        if (selectedResources != null) Marshal.ReleaseComObject(selectedResources);
        if (selectedTasks != null) Marshal.ReleaseComObject(selectedTasks);
        if (selection != null) Marshal.ReleaseComObject(selection);
    }
}

You will notice that in the above code, we also read properties such as task duration and resource cost and added it to the message box message.

Microsoft Project events

The Microsoft Project object model exposes a wide range of events that programmers can subscribe to. Some notable events are:

  • NewProject
  • ProjectBeforeClose
  • ProjectBeforeResourceChange
  • ProjectBeforeResourceNew
  • ProjectBeforeTaskChange
  • ProjectBeforeTaskDelete
  • ProjectBeforeTaskNew

In order to respond to Project events, you first need to add a new Microsoft Project events component to the AddinModule designer surface:

Adding a new Microsoft Project events component to the AddinModule designer surface

To generate an event handler stub, double-click next to the name of the event in the Visual Studio properties window:

Generating an event handler stub

Next, you can add code to respond to the event, as illustrated below:

private void adxProjectEvents_ProjectBeforeResourceChange(object sender,
    ADXProjectBeforeChangeEventArgs e)
{
    MSProject.Resource resource = e.Item as MSProject.Resource;
 
    if (resource != null)
        MessageBox.Show("You're changing " + resource.Name);
}

Microsoft Project calendar

Microsoft Project helps project managers plan the timeline of their project by allowing them to enter the days and hours resources that are available to work on tasks. This information is stored on the Calendar property of the Project object.

In the following code, we changed the calendar so that the only work days will be Wednesday through to Friday and the working hours for Friday will start at 10:30 and end at 16:00.

private void setCalendarRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Project currProject = null;
    MSProject.Calendar calendar = null;
    MSProject.WeekDays weekDays = null;
    MSProject.WeekDay monday = null;
    MSProject.WeekDay tuesday = null;
    MSProject.WeekDay wednesday = null;
    MSProject.WeekDay thursday = null;
    MSProject.WeekDay friday = null;
    MSProject.Shift fridayShift1 = null;
 
    try
    {
        currProject = MSProjectApp.ActiveProject;
        calendar = currProject.Calendar;
        weekDays = calendar.WeekDays;
 
        monday = weekDays[MSProject.PjWeekday.pjMonday];
        monday.set_Working(false);
 
        tuesday = weekDays[MSProject.PjWeekday.pjTuesday];
        tuesday.set_Working(false);
 
        wednesday = weekDays[MSProject.PjWeekday.pjWednesday];
        wednesday.set_Working(true);
 
        thursday = weekDays[MSProject.PjWeekday.pjThursday];
        thursday.set_Working(true);
 
        friday = weekDays[MSProject.PjWeekday.pjFriday];
        friday.set_Working(true);
 
        fridayShift1 = friday.Shift1;
        fridayShift1.Start = new DateTime(2014, 02, 20, 10, 30, 0);
        fridayShift1.Finish = new DateTime(2014, 02, 20, 16, 00, 0);
    }
    finally
    {
        if (fridayShift1 != null) Marshal.ReleaseComObject(fridayShift1);
        if (friday != null) Marshal.ReleaseComObject(friday);
        if (thursday != null) Marshal.ReleaseComObject(thursday);
        if (wednesday != null) Marshal.ReleaseComObject(wednesday);
        if (tuesday != null) Marshal.ReleaseComObject(tuesday);
        if (monday != null) Marshal.ReleaseComObject(monday);
        if (weekDays != null) Marshal.ReleaseComObject(weekDays);
        if (calendar != null) Marshal.ReleaseComObject(calendar);
        if (currProject != null) Marshal.ReleaseComObject(currProject);
    }
}

You will notice that Visual Studio might complain that it cannot resolve the set_Working method of the WeekDay object. This is because this accessor method is hidden for an unexplained reason. You can still use it, because your code will compile.

Microsoft Project views

Microsoft Project views come in two flavors:

  • Single views, and
  • Combination views

In order to create a new single view, you need to specify the following:

  • Name
  • Screen name
  • Table name
  • Group
  • Filter

Creating a new single Project view

In order to create the same view in MS Project as illustrated in the screenshot above, you’ll use the following code:

private void addViewRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Project currProject = null;
    try
    {
        currProject = MSProjectApp.ActiveProject;
        MSProjectApp.ViewEditSingle(
            "My New View", true, Type.Missing, 1, 
            true, false, "Cost", "All Tasks", "Critical");
        MSProjectApp.ViewApplyEx(
            "Gantt with Timeline", Type.Missing, Type.Missing, 0);
    }
    finally
    {
        if (currProject != null) Marshal.ReleaseComObject(currProject);
    }
}

You’ll further notice a lot of places in the MS Project object model when adding things like views and tasks that it is not done by adding to a collection, but rather by using a method on the Project object. In this case we call the ViewEditSingle method to create a new view and the ViewApplyEx method to apply and activate the newly created view.

Filtering MS Project data

Filtering data in Microsoft Project is done by creating a new filter. This is accomplished by invoking the FilterEdit method of the Project object. To apply the filter, call the Project objects’ FilterApply method. In the following code, we created a new filter called “My New Filter” and set it to only show tasks that contain the word “Develop” in their name:

private void filterDataRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProjectApp.FilterEdit(
        "My New Filter", true, true, true, Type.Missing, Type.Missing, 
        "Name", Type.Missing,"contains", "Develop", Type.Missing, true, true);
    MSProjectApp.FilterApply("My New Filter");
}

Working with MS Project reports

Microsoft Project comes with a host of built-in reports. However, you can also create your own custom reports. MS Project has an interesting way to create reports via the object model. Essentially you will create a report and add shapes to the report. Each shape in turn has a ReportTable object which contains the data for the shape.

In the following code, we created a new report that will display the name and duration for all the critical tasks in the project.

private void createReportRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Project currProject = null;
    MSProject.Reports reports = null;
    MSProject.Report report = null;
    MSProject.Shapes shapes = null;
    MSProject.Shape shape = null;
    MSProject.ReportTable shapeTable = null;
    MSProject.PjField[] fields = null;
 
    try
    {
        currProject = MSProjectApp.ActiveProject;
        reports = currProject.Reports;
 
        report = reports.Add("My New Report");
        shapes = report.Shapes;
        shape = shapes.AddTable(3, 4, 20, 20, 200, 100);
        shapeTable = shape.Table;
 
        fields = new MSProject.PjField[] { 
            MSProject.PjField.pjTaskName, MSProject.PjField.pjTaskDuration };
        shapeTable.UpdateTableData(true, "Critical", "Critical", -1, fields);
 
    }
    finally
    {
        if (shapeTable != null) Marshal.ReleaseComObject(shapeTable);
        if (shape != null) Marshal.ReleaseComObject(shape);
        if (shapes != null) Marshal.ReleaseComObject(shapes);
        if (report != null) Marshal.ReleaseComObject(report);
        if (reports != null) Marshal.ReleaseComObject(reports);
        if (currProject != null) Marshal.ReleaseComObject(currProject);
    }
}

Working with MS Project resources

As any good project manager would tell you, a project is simply not possible without the resources to do the work. Below follows the code to add and edit resources:

Adding a resource to the current project

private void addResourceRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Project currProject = null;
    MSProject.Resources resources = null;
    MSProject.Resource resource = null;
 
    try
    {
        currProject = MSProjectApp.ActiveProject;
        resources = currProject.Resources;
        resource = resources.Add("A New Resource");
        _newResourceId = resource.UniqueID;
    }
    finally
    {
        if (resource != null) Marshal.ReleaseComObject(resource);
        if (resources != null) Marshal.ReleaseComObject(resources);
        if (currProject != null) Marshal.ReleaseComObject(currProject);
    }
}

Editing an existing resource

private void editResourceRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Project currProject = null;
    MSProject.Resources resources = null;
    MSProject.Resource resource = null;
 
    try
    {
        currProject = MSProjectApp.ActiveProject;
        resources = currProject.Resources;
        resource = resources.UniqueID[_newResourceId];
        if (resource != null)
            resource.Name = "Updated New Resource";
    }
    finally
    {
        if (resource != null) Marshal.ReleaseComObject(resource);
        if (resources != null) Marshal.ReleaseComObject(resources);
        if (currProject != null) Marshal.ReleaseComObject(currProject);
    }
}

Working with MS Project tasks

As a project cannot exist without resources, it certainly cannot exist without tasks. Tasks can consist of a single task or a summary task made up of a group of tasks. MS Project’s way to add a summary task, for a lack of a better word, is somewhat weird. One would think, you can add a new task to the Tasks collection of the Project object and set its Summary property to true, but the Summary property is read-only.

Adding a summary task to the current project

Instead of adding a new summary task, you need to call the InsertSummaryTask method of the Project Application object. You’ll then need to get a reference to the ActiveCell in order to get a reference to the task associated with the cell.

private void addSummaryTaskRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Cell activeCell = null;
    MSProject.Task task = null;
    MSProject.Tasks subTasks = null;
    MSProject.Task subTask = null;
 
    try
    {
        MSProjectApp.InsertSummaryTask();
        activeCell = MSProjectApp.ActiveCell;
        task = activeCell.Task;
        task.Name = "Task Summary";
        subTasks = task.SuccessorTasks;
        subTask = subTasks.Add("New Sub Task");
        subTask.Resources.Add("Developer");
    }
    finally
    {
        if (activeCell != null) Marshal.ReleaseComObject(activeCell);
        if (subTask != null) Marshal.ReleaseComObject(subTask);
        if (subTasks != null) Marshal.ReleaseComObject(subTasks);
        if (task != null) Marshal.ReleaseComObject(task);
    }
}

One caveat with this approach is that it assumes an empty row is selected in the current Task view. I could not find another way to create summary tasks using the Project object model.

Adding a normal task to the current project

Adding a normal task is done in a more familiar and logical way by using the Tasks collection of the Project object.

private void addTaskRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Project project = null;
    MSProject.Tasks tasks = null;
    MSProject.Task task = null;
 
    try
    {
        project = MSProjectApp.ActiveProject;
        tasks = project.Tasks;
        task = tasks.Add("My New Task");
        _newTaskId = task.UniqueID;
    }
    finally
    {
        if (task != null) Marshal.ReleaseComObject(task);
        if (tasks != null) Marshal.ReleaseComObject(tasks);
        if (project != null) Marshal.ReleaseComObject(project);
    }
}

Editing an existing task

To edit an existing task in MS Project, use the code similar to this:

private void editTaskRibbonButton_OnClick(object sender,
    IRibbonControl control, bool pressed)
{
    MSProject.Project project = null;
    MSProject.Tasks tasks = null;
    MSProject.Task task = null;
 
    try
    {
        project = MSProjectApp.ActiveProject;
        tasks = project.Tasks;
        task = tasks.UniqueID[_newTaskId];
        if (task!=null)
        {
            task.Name = "Updated New Task";
        }
    }
    finally
    {
        if (task != null) Marshal.ReleaseComObject(task);
        if (tasks != null) Marshal.ReleaseComObject(tasks);
        if (project != null) Marshal.ReleaseComObject(project);
    }
}

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

Available downloads:

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

Sample MS Project add-in (C#)

You may also be interested in:

12 Comments

  • https://secure.gravatar.com/avatar/61dd8a9a268c3f44176f7c4544d7e303?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G shrikant says:

    Hi Pieter,

    I went through your blog and tried to modify the MS Project 2007 calendar using C#. According to your code I am looking the changes in standard calendar.
    But it is not applying on my result task record.
    I mean to say changes are not reflected in Gantt chart section i.e. it is still showing sat/sun as non working.
    Please could you help me.
    Thanks in Advance.

    Regards
    Shrikant

  • https://secure.gravatar.com/avatar/e1a4c2b21a5186e0b27c1c601f418b76?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Pieter van der Westhuizen says:

    Hi Shrikant,

    In the example we do not set Saturday & Sunday as working days. In order to make Saturday and Sunday working days, create a new MSProject.WeekDay object for each and use the set_Working method e.g.:

    MSProject.WeekDay saturday = weekDays[MSProject.PjWeekday.pjSaturday];
    saturday.set_Working(true);
    MSProject.WeekDay sunday = weekDays[MSProject.PjWeekday.pjSunday];
    sunday .set_Working(true);

    Hope this helps!

  • https://secure.gravatar.com/avatar/1401216e4fe80a173967c307e3bd448c?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Neeraj Ukinkar says:

    i have VBA Macro written i want to convert it into VB.net in Visual Studio Community Can any one help is there any wizard for conversion ……..i want apply one view in vb.net code in Project 13

    what is function to apply view in visual studio like we have ViewApplyEx in VBA.
    Plz HElp

  • https://secure.gravatar.com/avatar/29957f26ad2d8ba527fd9cc8cfa7b2e0?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Andrei Smolin (Add-in Express Team) says:

    Hello Neeraj,

    No such wizard exists. Note that although VBA and VB.NET are different languages, you use the same object model. That means, you may call ViewApplyEx from VBA or from VB.NET.

  • https://secure.gravatar.com/avatar/e8644080722dae8166a9dbf853fc0ece?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Vincent says:

    Hello, Pieter!

    Have you any idea how to get selected CELLS in a Project? Say, user selected ‘start’ of one task and ‘finish’ of another task – I need just these two cells. Is it possible? I almost broke Google finding the answer!!

    10x!

  • https://secure.gravatar.com/avatar/29957f26ad2d8ba527fd9cc8cfa7b2e0?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Andrei Smolin (Add-in Express Team) says:

    Hello Vincent,

    In the VBA IDE run this VBA macro and then open the Immediate window.

    Sub ListSelectedRowsAndColumns()
    Dim sel As Selection
    Set sel = Application.ActiveSelection

    Dim fldList As List
    Set fldList = sel.FieldNameList

    Dim i As Integer
    For i = 1 To fldList.Count
    Debug.Print “Field=” + fldList.Item(i)
    Next i

    For i = 1 To sel.Tasks.Count
    Debug.Print “Task=” + sel.Tasks.Item(i).Name
    Next i

    End Sub

  • https://secure.gravatar.com/avatar/6e26ea138a4518221b09ca2b4462c2e5?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G SomasundaraM says:

    How to insert new task in between the existing tasks? using c# or VB.NET

  • https://secure.gravatar.com/avatar/29957f26ad2d8ba527fd9cc8cfa7b2e0?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Andrei Smolin (Add-in Express Team) says:

    Hello SomasundaraM,

    You need to use the Macro Recorder to find this out. Start it and insert a new task. The Macro Recorder will create a VBA macro showing the classes/methods involved in the process.

  • https://secure.gravatar.com/avatar/c324a0c393b9326cd79a56a959777e80?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G jose alberto marti martin says:

    Hello Pieter,

    My problem is that I’m exporting information from my own app to MSProject and I want to show some hide columns by default. I’m writing information on the Text1, etc fields and I want to display it by default. Could you help me with that??

    Thanks for your help and congrats for your blog, it’s hard to find good information about programming for project.

  • https://secure.gravatar.com/avatar/29957f26ad2d8ba527fd9cc8cfa7b2e0?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Andrei Smolin (Add-in Express Team) says:

    Hello Jose,

    You need to use the Macro Recorder to find this out. Start it and hide this and that column. The Macro Recorder will create a VBA macro showing the classes/methods involved in the process.

  • https://secure.gravatar.com/avatar/afa8fd6ad104ddc153435b81e447c4fc?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Sagar Sen says:

    Hi,

    How to implement Calendar.Period().Please help me ,I have stuck on this sort of code from last one week.

  • https://secure.gravatar.com/avatar/29957f26ad2d8ba527fd9cc8cfa7b2e0?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Andrei Smolin (Add-in Express Team) says:

    Hello Sagar,

    It seems you are looking for https://msdn.microsoft.com/en-us/vba/project-vba/articles/calendar-period-method-project.

Post a comment

Have any questions? Ask us right now!