Enhancing Microsoft Project Web Access with Print My Timesheet
Summary
This article describes how to construct a customized Timesheet view for Microsoft® Project Web Access. The sample view is formatted for printing, and is accessible through a menu item added to the Activities in Tasks view.
- Download The Print My Timesheet for Microsoft Project Web Access (printmytimesheet.exe) file has the final HTML code for Print My Timesheet functionality. The file can be downloaded from the Microsoft Download Center.
Introduction
Microsoft Project Web Access provides a browser-based client with a wealth of features that allow project participants to view and update project data with ease. If you want to tailor Microsoft Project to reflect your operating requirements, Microsoft Project Web Access provides the capability of easily integrating customized features into the Microsoft Project Web Access environment.
The key customizability feature is the ability for you to customize the menu structure to integrate a custom Web page. This custom menu capability provides an avenue to add new features directly into the Microsoft Project Web Access interface, providing one-click access to the desired feature at the point it is most relevant. The ability to add custom Web pages extends the capabilities of Microsoft Project Web Access to address client-specific requirements.
Microsoft Project Web Access provides the Timesheet view for each project team member with assigned tasks. The Timesheet view is produced in a grid format that lists several columns of data for a given date range. This same information can be useful in printed form, but there is no built-in capability in Microsoft Project Server for printing only the timesheet grid. This article demonstrates how you can implement an HTML version of the timesheet, tailor it for printing, and access it through a custom menu item.
You can gain access to timesheet data through the use of component object model (COM) objects in Microsoft Project Server. With the accompanying source code, this article shows how you can leverage an existing Microsoft Project Web Access page and build your custom page to display an HTML version of the tasks listed in the Timesheet view.
This article assumes that you have familiarity with Dynamic HTML (DHTML), Microsoft Active Server Pages (ASP), Cascading Style Sheets (CSS), as well as some simple scripting. COM understanding is also helpful, as well as with basic development support tools, such as the OLE Viewer.
Getting Started
The first thing you need is the Microsoft Project Web Access client. It is assumed that your organization has already installed Microsoft Project Server, and that the administrator, if someone other than yourself, has created an account for you with Administrator privileges. You should have Administrator privileges to be able to see the tasks assigned to you and to use that data for testing this exercise.
As you complete your preparation, you should familiarize yourself with Microsoft Project Web Access, and study, in general, the page layout and the menu navigation structure. In particular, pay close attention to the layout of the Tasks page, since this exercise focuses on that page.
Adding a Menu Item
Microsoft Project Web Access provides a simple interface for accessing custom Web pages. You can find the menu customizing feature by first navigating to the Administration overview page, and then clicking Manage organization, as shown in Figure 1.

Figure 1. Administration overview page in Microsoft Project Web Access
In the Manage organization view, you can find the Menus link on the left pane, located just below the Organization options section.
To add a custom menu item:
- Click Menus to display a table of defined menus in the right pane.
- The Name column shows each Microsoft Project Web Access page and the current set of menus associated with each page. You can add a custom menu to any page by highlighting that page before adding a custom menu. You will be adding a menu to the Tasks page.
- Click the Tasks cell in the Name column to highlight it.
- Click Add Custom Menu.
- In the Add Custom Menu -- Web Page Dialog dialog box, click Add a submenu.
- In the Submenu Name box, type Print My Timesheet.
- In the Submenu ToolTip box, type Print My Timesheet.
- In the Submenu URL box, copy and paste the following URL, where <servername> is the name of your Microsoft Project Server:
http://<servername>/projectserver/tasks/printmytimesheet.htm - Note The new page is named PrintMyTimesheet.htm; save this file in the Tasks folder of the Microsoft Project Server Web site.
- This file must reside on the Microsoft Project Server computer to be able to access the COM objects used to construct the custom page. If you do not have access to Microsoft Project Server, you will not be able to use the COM objects.
- Click OK.
- Figure 2 shows the completed dialog box.

Figure 2. Add Custom Menu -- Web Page Dialog dialog box
You have now saved the new menu item to the Microsoft Project Server database. Microsoft Project Web Access loads the defined menus only during logon, so you will need to log out and log on again for your Print My Timesheet to menu appear on the Tasks page.
To test your new menu item, you can place the downloaded Printmytimesheet.htm file into the Tasks subfolder under your Microsoft Project Server virtual directory. This directory is normally located in C:\Program Files\Microsoft Project Server\IIS Virtual Root\Tasks on the IIS server where you installed Microsoft Project Server.
Manually Building the Printmytimesheet.htm Page
The file Printmytimesheet.htm is included in the download for this article. However, you can also build the Printmytimesheet.htm page manually, by creating a file named Printmytimesheet.htm in the Tasks subdirectory of your Microsoft Project Web Server. This directory is normally located in C:\Program Files\Microsoft Project Server\IIS Virtual Root\Tasks on the IIS server where you installed Microsoft Project Server. Add the following HTML text to your Printmytimesheet.htm file:
<html> <body> Print My Timesheet coming soon... </body> </html> To see the page, log out of Microsoft Project Web Access, log on, click Tasks view, and then click Print My Timesheet. You should see that the task list has been replaced with the content of Printmytimesheet.htm. Figure 3 shows this in more detail.

Figure 3. Print My Timesheet page with placeholder content
Microsoft Project Web Access invokes the ASP CustPage.asp, indicated in the Address field of Microsoft Internet Explorer, and passes it the ID of the page to display. When you view the source for CustPage.asp, you can see that an IFRAME is used to refer to the custom HTML file.
<DIV ID="idIframe"> <IFRAME ID="idCustPageFrame" FRAMEBORDER="0" NAME="idCustPageFrame" SCROLLING="YES" STYLE="display: block; width: 100%; height:100%;"> </IFRAME> </DIV> <SCRIPT LANGUAGE="JScript"> function Workspace_OnLoad() { var link = unescape("http%3A//<servername>/projectserver/tasks/ printmytimesheet.htm%3FInnerPage%3D1"); if (link == "") return; idLinkHelper.href = link; var sProt = sProtocol + "://"; if (idLinkHelper.protocol != "") sProt = ""; document.frames['idCustPageFrame'].window.location.href = sProt+link; } </SCRIPT> Accessing Task List Data
Now that you've completed your page activation procedure, you're ready to start composing the content of the Print My Timesheet page.
Microsoft Project Web Access does not pass parameters dynamically to your custom page when it is launched. To launch a page with the task list data provided, you can use the existing Tasks page (TasksPage.asp in the Tasks subdirectory of Microsoft Project Server). If you look closely at the Tasks page, you will see that the task list and timesheet are rendered within a Microsoft Project Server ActiveX® grid control. This ActiveX grid control provides a scrolling view of all the columns, and also allows project participants to update their task lists. However, you can not use this grid control to easily print the entire task. The purpose of building this custom page is to be able to extract all the data from the grid control and build an HTML table to render the relevant information to print.
One approach would be to duplicate the code inside of TasksPage.asp that gains access to the grid control and data. However, this would require a very careful inspection of the code to ensure that the essential security and application context is maintained. The effort required and the necessary code duplication would produce a non-scalable result.
This exercise will use an IFRAME to make it easier to embed the original task page, grid control, and other components within the page. The grid control will be accessed through the embedded document by using DHTML to extract the required task list data.
It is important to note that the Taskspage.asp page is being embedded for the sole purpose of accessing the grid. The goal of this exercise is not the rendering of taskspage.asp, and the IFRAME attributes should be specified accordingly. Besides setting visibility='hidden', the height and width attributes should be set to zero so that the IFRAME does not occupy any space. Edit the Printmytimesheet.htm page that you created, and replace its contents with the following:
<html> <body> <IFRAME name="tasksframe" height="0" width="0" visibility="hidden"; src="taskspage.asp"> </IFRAME> </body> </html> To access the data, it is necessary to know the HTML tag used to reference the grid control. To do this, return to the Microsoft Project Web Access View my tasks page, right-click in the left pane, and then click View Source. Use your text editor to search for the OBJECT tag that specifies the ActiveX grid control. There are several OBJECT tags within this page, the correct object reference (with the ID attribute "MSPJGrid" and the alt text "Microsoft Project Task Grid") contains the following HTML:
<OBJECT ID="MSPJGrid" ALT="Microsoft Project Task Grid" CLASSID="CLSID:67EF6CD7-CA45-4c9c-882E-48613F134F7C" ACCESSKEY="." WIDTH="1" HEIGHT="1"> </OBJECT> The object with the ID attribute MSPJGrid will be the identifier when accessing the grid through DHTML.
Accessing data within the grid will be done using methods and properties available for the ActiveX object. To discover the available methods and properties, we need to locate the ActiveX file (OCX) on the Microsoft Project Server computer. The CLASSID attribute on the OBJECT tag specifies a COM library class identifier that can be used to locate the COM library on the Microsoft Project Server computer. The string of characters following CLSID: is a globally unique identifier that is used to search for the COM library.
The Microsoft Windows® registry contains information for all of the ActiveX objects installed on the computer, including the CLSID and the physical location of the OCX file. To search the Windows registry, click Start, click Run, and type regedit in the Open box.
Use the Find command to search for "67EF6CD7-CA45-4c9c-882E-48613F134F7C." Figure 4 shows results similar to what you will find.

Figure 4. Registry Editor search results
In the Registry editor, click the InprocServer32 subkey. The Default value shows Pjgrid2.ocx as the COM library associated with this identifier.
You are now ready to examine the methods provided by Pjgrid2.ocx by using the Microsoft Visual Studio® 6.0 tool Oleview.exe. You can use this library viewer to examine the published interfaces and all public properties and methods. The PJGRID2.OCX method names are self-explanatory. These methods and properties will be used to build the HTML version of the timesheet.
Building HTML Tables
The timesheet will be displayed using an HTML table. A table will be defined in the HTML file, and DHTML will be used to dynamically generate the rows of the table. Add a TABLE tag with an id of TasksTable to the Printmytimesheet.htm page.
<TABLE id="TasksTable> </TABLE> The data used to build the table resides in the grid within TasksPage.asp, which has been embedded in the document by using an IFRAME. As the HTML file is loaded, Microsoft Internet Explorer constructs a series of objects that correspond to the elements on the page, according to the document object model (DOM). As Internet Explorer processes the document, each object's state is initially set to uninitialized, and then to loading. When the data loading process is complete, the state of the link object passes through the loaded and interactive states to reach the complete state. Because document objects are normally populated asynchronously, certain programmatic operations can only be performed reliably on objects when they are ready for use. Therefore, the appropriate code should be written to confirm the readyState of objects prior to performing certain operations on them. The correct place to test the readyState property is in the event handler for onreadystatechange. This is done by checking the value of the document.readyState='completed' status to ensure that the entire document has been loaded; only after this condition has been met will the grid be accessed.
<SCRIPT> document.onreadystatechange=BuildTables; Function BuildTables() { if (document.readyState=='completed') { //build tables dynamically here } } </SCRIPT> Oleview.exe reveals that there are methods in Pjgrid2.ocx for iterating and returning data from the grid control. Table 1 shows the methods you can use to extract timesheet data:
Table 1. PJGRID2.OCX methods used for extracting timesheet data
| Method | Description |
| GetNumRows | Returns the total number of rows in a particular pane |
| GetNumColumns | Returns the total number of columns in a particular pane |
| GetFieldName | Returns the name of the column header for a particular pane |
| GetTextForCell | Returns the cell text for a particular pane, row, and column |
The first parameter of these methods is an identifier to a particular "pane" within the grid control. The task list data resides in pane index 0, while the timesheet data is in pane index 1. The HTML tables are now ready to be constructed. This code example shows how to build the task list table.
<SCRIPT> Function BuildTables() { // Ensure that the grid can be safely accessed if (document.readyState == completed') { // Access the grid var oGrid = document.frames("tasksframe").document.MSPJGrid; var nRows = oGrid.GetNumRows(0); var nCols = oGrid.GetNumColumns(0); var oNewRow = TasksTable.insertRow(); var oNewCol; var oNewColHead; // Iterate over each column to get the column header names for (c = 1; c < nCols; c++) { oNewColHead = document.createElement('th'); oNewColHead.innerText = oGrid.GetFieldName(0, c); oNewRow.appendChild(oNewColHead); } // Walk down the rows and extract the data for each column for (r = 0; r < nRows; r++) { oNewRow = TasksTable.insertRow(); for (c = 1; c < nCols; c++) { oNewCol = oNewRow.insertCell(); oNewCol.innerText = oGrid.GetTextForCell(0, r, c); } } } } </SCRIPT> After assigning the oGrid object to the Microsoft Project Server grid, it can be used to query for the number of rows and columns. First, insert a header row into the table to display the field names at the top of each column. The GetFieldName method returns the field name for a given column from the grid object. Next, iterate through the grid rows and build up each row of data one column at a time by using the GetTextForCell method.
The process of building the timesheet table is identical to the code example. The timesheet data resides in pane index 1, so all that is needed is to define another TABLE element to represent the timesheet, and to follow the table building code example to populate this table.
The tables have now been built with data extracted from the grid, but to provide a useful display of information for printing, they will need to have appropriate styles applied.
Formatting Tables
You can use HTML styles creatively to render the tables to closely resemble the grid control, including a visible hierarchy of tasks and subtasks. Using Oleview.exe to closely examine the grid control shows that there are no methods to inquire whether a row is a subtask, a main task, or even a project. Likewise, there are no methods that can distinguish one column from another. The data in the table cells will need to be interpreted to render the table to display these details. Define the following styles in the Printmytimesheet.htm page, which will be associated with the appropriate target elements as the table is built.
<STYLE> body { font-family:Verdana, Arial, Helvetica, Sans-serif; } .tableStyle { border-collapse:collapse; font-size:10pt; text-align:right; } .tableHeader { background-color:cornflowerblue; text-align:center; } .projectHeader { background-color:slategray; color:white; font-weight:bold; } .subtaskHeader { background-color:lightsteelblue; font-weight:bold; } .lfAlign { text-align:left; } </STYLE> For a project, the cell in the Project column will be empty. The following code tests to determine whether there is a value for the contents of that cell; the value of the index for the Project column is 9. If there is no value, the task must be a project.
if (c == 9 && oGrid.GetTextForCell(0, r, c) == '') { oNewRow.className = 'projectHeader'; } For a task that has subordinate tasks, the cell in the % Work Complete column will be empty. The following code tests to determine whether there is a value for the contents of that cell; the value of the index for the % Work Complete column is 3. If there is no value, the task must be a summary task.
if (c == 3 && oGrid.GetTextForCell(0, r, c) == '') { oNewRow.className = 'subtaskHeader'; } By testing for these two conditions, the tables can be rendered to convey task information more effectively by identifying summary tasks and subtasks, based on the absence of timesheet data. The accompanying HTML source code includes further enhancements that allow resizing of columns to offer a printer-friendly version of the task list. You can easily make enhancements of your own, including the ability to resize columns, display a separate window for printing, or place one table above the other to fit your standard printer widthanything you might conveniently do with DHTML.