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
- Microsoft Project events
- Microsoft Project calendar
- Microsoft Project views
- Filtering MS Project data
- Working with MS Project reports
- Working with MS Project resources
- Working with MS Project tasks
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:
To generate an event handler stub, double-click next to the name of the event in the Visual Studio properties window:
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
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:
22 Comments
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
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!
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
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.
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!
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
How to insert new task in between the existing tasks? using c# or VB.NET
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.
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.
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.
Hi,
How to implement Calendar.Period().Please help me ,I have stuck on this sort of code from last one week.
Hello Sagar,
It seems you are looking for https://msdn.microsoft.com/en-us/vba/project-vba/articles/calendar-period-method-project.
I am new to VB.
I download from SAP into Project and the Resource names column is populated with a resource work centre (eg.XNAB-P01). Most of the RWC’s have specific names which I have create a macro to convert but the XNAB-P01 RWC is a vendor specialist and could be any of 30 vendors.
I need to find a way of changing the XNAB-P01 to the first word of the Task Name cell of the same row. It will need to be repeated for each cell with the XNAB-P01 RWC.
Can anyone assist?
Hello Paul,
I suggest that you start Macro Recorder (see the Developer tab), change one of the names, stop Macro Recorder and study the VBA macro it creates. The macro reveals the classes/members involved in the process. You need to use these classes/members in your code. Record more macros to find more details.
Hi Paul.
I am facing one issue in Project VSTO addin.
I would like to move one existing task from one parent to another parent grammatically. I heard that copy and paste is not good approach. I tried with copying ID to temp column and sort it.
Issue: When I actually moved task, it shows in screen that it is under another parent but it keeps the parent child relationship with old parent.
Is that any way, I can remove existing parent child relation with old parent and add as a child in new parent
I hope I am clear with my issue
Hi,
I am exporting my data to MS Project using C# code. I want to add some columns like group, parent, etc. Is there any way to add columns so that the value for them can be assigned for each task?
Hi Shruti,
Microsoft provides very comprehensive documentation for MS Project developers, please have a look, hope it will be useful:
https://docs.microsoft.com/en-us/office/vba/project/concepts/project-object-model
Hi all,
I’ve added the custom fields in MS Project, Now i need to add values in the respective column. How to call created custom field to add values init.
Hello Sruthi,
You can record a macro while performing these steps in the Project UI. The macro should reveal classes/methods involved.
Thanks.. its very help me.
Hi,
is it possible to read from Visual studio if a weekday in work weeks detail is setted on “use times from default work week for this day”, I am able to set it in default but i am not able to read if it is default. I am writing a very complicated code that allow me to know each day witch is the work / remain avalaibility and overallocated work has a resource.
Thank you
Hello Manu,
You should check the Project Object Model; start with https://learn.microsoft.com/en-us/office/vba/api/overview/project. Some things can be discovered by recording a VBA macro:
– show the Developer tab
– on the Deeloper ta, click Record Macro
– in the UI perform the steps you need to perform in your code
– stop recording the macro
– study the VBA macro that uses objects and their properties/methods
– adapt the code to use in your add-in.