The Upcoming Tasks Web Part is defined in the Microsoft Visual C# UpcomingTasksWP.cs class file. The tool part associated with the Upcoming Tasks Web Part is defined in the UTToolPart.cs file. You can modify the class files, compile your own version of the custom assembly, and then follow the procedure described in Installing a Custom Web Part to deploy the Web Part on a computer.
The following procedures and code explain how to create the UpcomingTasksWP class that displays the Web Part content. The Web Part uses the WebPartPages namespace in the Microsoft.SharePoint.dll assembly.
To create a Web Part class:
-
Create a class library project and solution in Visual Studio, for example, PWARefWebParts.
-
Rename the Class1.cs file to match the name of the Web Part class you will create, for example, UpcomingTasksWP.cs.
-
Add references to the following assemblies: System.Drawing.dll, System.Web.dll, and System.Web.Services.dll. Add a reference to the Microsoft.SharePoint.dll assembly in the following directory, or copy the assembly to your development computer.
[Program Files]\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI
-
If you plan to integrate with other Project Server Web Parts on the same page, add a reference to the Microsoft.Office.Project.Server.PWA.dll assembly.
-
To find the assembly in Windows Explorer and copy it to your development computer, run the following in a Command Prompt window.
regsvr32 /u [Windows]\Microsoft.NET\Framework\v2.0.50727\shfusion.dll
-
Copy the assembly from the following directory.
[Windows]\assembly\GAC_MSIL\Microsoft.Office.Project.Server.PWA\12.0.0.0__71e9bce111e9429c
-
Restore the default assembly view by re-registering shfusion.dll.
regsvr32 [Windows]\Microsoft.NET\Framework\v2.0.50727/shfusion.dll
-
Add a Web references for each PSI Web service that the Web Part needs.
Note: |
|---|
|
If you are compiling the sample applications in the SDK download, delete the Web references and then add them again with the same namespace names. This ensures that wsdl.exe generates proxy code from the installed version of Project Server.
|
-
In Solution Explorer, right-click the References node, and then click Add Web Reference.
-
Type the URL in the Add Web Reference dialog box, for example:
http://ServerName/ProjectServerName/_vti_bin/psi/project.asmx
-
Rename the Web reference, for example, ProjectWS, and then click Add.
-
Edit UpcomingTasksWP.cs to create the UpcomingTasksWP class. In the following code, gridID, numTasks, and projWS are private fields for class properties. The gridID field is not needed in the NoPWARefWebParts solution.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Serialization;
using System.Net;
using System.Data;
using Microsoft.SharePoint.WebPartPages;
namespace PWARefWebParts
{
//[DefaultProperty("Text")]
//[ToolboxData("<{0}:UpcomingTasksWP runat=server></{0}:UpcomingTasksWP>")]
[XmlRoot(Namespace = "PWARefWebParts")]
public class UpcomingTasksWP : WebPart
{
private const string PROJECT_SERVER_URI = "http://localhost/pwa"; // Change to your value
private const string PROJECT_WEBSERVICE = "/_vti_bin/psi/project.asmx";
private string gridID; // ID of the ActiveX grid in the selected
// Project Center Web Part
private int numTasks; // Number of upcoming tasks to display
private DataGrid displayGrid; // The data grid to display in the Web Part
private ProjectWS.Project projWS = null; // Project object in the Web service
public UpcomingTasksWP()
{
}
}
If you use the Visual Studio Web Part template to create the project, it adds three class attributes for Windows SharePoint Services 2.0. In the Visual Studio design environment, when you drag an .aspx or .ascx file onto a page or a user control, the DefaultProperty and ToolboxData attributes govern behavior. DefaultProperty specifies the property that is selected by default in the Visual Studio Property pane. The tag prefix that is registered for the control in the Page directive (or the Control directive for a user control) replaces the {0} token in the ToolboxData attribute.
To import a Web Part, you must define an XML namespace for all of the Web Part properties. You can use the XmlRoot attribute in the class definition or an XmlElement attribute for each property in the class. The XML namespace can be any value you want; it is generally given the namespace of the custom Web Part.
-
Add public class properties for access by other classes in the Web Part, such as a class for the tool part. The NoPWARefWebParts solution does not need the GridID property, and adds a ProjectName property.
#region Properties
// ProjectWS handles the connection to the Project Web Service.
// It is a public property so that other classes can use the same connection.
public ProjectWS.Project ProjectWS
{
get {
// If we are not connected to a project server, make the connection.
if (projWS == null)
{
projWS = new ProjectWS.Project();
projWS.Url = "http://localhost/projectserver/" + PROJECTWEBSERVICE;
projWS.CookieContainer = new CookieContainer();
projWS.Credentials = CredentialCache.DefaultCredentials;
}
return projWS;
}
}
// These properties are exposed so the tool part can update
// the selected Project Center grid and the number of tasks to show.
[ Browsable(false),
Category("Miscellaneous"),
WebPartStorage(Storage.Personal)
]
public string GridID
{
get { return gridID; }
set { gridID = value; }
}
[ Browsable(false),
Category("Miscellaneous"),
WebPartStorage(Storage.Personal)
]
public int NumDisplayTasks
{
get { return numTasks; }
set { numTasks = value; }
}
/* Add the following only in the NoPWARefWebParts solution.
[ Browsable(false),
Category("Miscellaneous"),
WebPartStorage(Storage.Personal)
]
public string ProjectName
{
get { return projectName; }
set { projectName = value; }
}
*/
#endregion
-
Override the WebPart.GetToolParts method to specify the order of tool parts in the tool part pane. For more information, see the GetToolParts method in the Windows SharePoint Services 3.0 SDK.
// To use our own tool part, we must override the GetToolParts method.
// The CustomPropertyToolPart is a container for the dynamic drop-down list of
// Project Center Web Parts.
public override ToolPart[] GetToolParts()
{
ToolPart[] toolParts = new ToolPart[3];
toolParts[0] = new CustomPropertyToolPart();
toolParts[1] = new WebPartToolPart();
toolParts[2] = new UTToolPart();
return toolParts;
}
The remainder of the UpcomingTasksWP class adds controls to the Web Part. You can add or change controls in the custom Web Part by modifying the OnPreRender method.
To change or add controls to the Upcoming Tasks Web Part:
-
Modify the OnPreRender method in the WebPart class. For example, the following override method adds script to the Web page that gets information from the Project Center Web Part. The script is constructed by the GetClientScript method, which is described later in this article. The method then adds custom controls for a title and a grid to the Web Part content. The GetWebPartTitle method defines the custom title control and also returns the selected project name.
The LiteralControl additions insert raw HTML around the title control. Instead of using LiteralControl objects, you could include the HTML in the Label text of the GetWebPartTitle method.
protected override void OnPreRender(EventArgs e)
{
string projName;
this.Page.ClientScript.RegisterStartupScript(this.GetType(),
this.ID + "KEY_UTWPSCRIPT", GetClientScript(), false);
Controls.Add(new LiteralControl("<b>"));
Controls.Add(GetWebPartTitle(out projName));
Controls.Add(new LiteralControl("</b><br/><br/>"));
Controls.Add(GetWebPartContent(projName));
base.OnPreRender(e);
}
-
To add the standard Web Part header and menus, call the OnPreRender method of the base class after you add custom controls.
The GetWebPartTitle method creates a custom control that uses a System.Web.UI.WebControls.Label control. When the project name is defined, the title includes the project name; otherwise, the title is for the Web Part configuration directions, which are defined in the GetWebPartContent method.
In the PWARefWebParts solution, the GetWebPartTitle method gets the project name from an HTTP request for the page when the user clicks a project row in the Project Center Web Part.
In the NoPWARefWebParts solution, the GetWebPartTitle method gets the project name from the ProjectName property of the UpcomingTasksWP class. The UIToolPart class for the tool pane sets the ProjectName property when the user configures the Upcoming Tasks Web Part. GetWebPartTitle returns a Control object as well as the projName string for use by the GetWebPartContent method.
[C#]
private Control GetWebPartTitle(out string projName)
{
Control titleControl;
Label titleControlLabel = new Label();
titleControlLabel.ForeColor = Color.Blue;
projName = Page.Request["UTWPProjName"];
// The following line is for the NoPWARefWebParts solution.
// projName = ProjectName;
if (projName != null && projName != string.Empty)
{
titleControlLabel.Text = "Upcoming Tasks for Project: " + projName;
}
else
{
projName = string.Empty;
titleControlLabel.Text = "Upcoming Tasks: Web Part Configuration Directions";
}
titleControl = titleControlLabel;
return titleControl;
}
If there is no project name, Project Web Access has just shown the Project Center page, so the GetWebPartContent method creates a Label with the configuration instructions.
If the project name is defined, the user has clicked a project row (in the PWARefWebWebParts solution) or has selected a project in the tool part pane of the NoPWARefWebParts solution. GetWebPartContent creates a DataGrid object with rows for the uncompleted tasks.
The following code shows the basic outline of the GetWebPartContent method and the section that creates the webControlLabel object with configuration instructions.
When there is no project name, the method assigns webControlLabel to the webControl.
[C#]
private Control GetWebPartContent(string projName)
{
int iTask = 0;
Guid projGuid;
bool isTasksUncompleted = false;
ProjectWS.ProjectDataSet projectDS;
DataRow taskDataRow;
DataRow displayTaskDataRow;
DataRow[] taskRows;
DataTable displayTaskDT = new DataTable();
Control webControl;
Label webControlLabel = new Label();
if (projName != string.Empty)
{
/* Add code to create the grid and add rows for uncompleted tasks.
*/
}
else // No project name is selected, so show instructions to configure the Web Part.
{
string labelText = "Click a project row in the associated ";
labelText += "Project Center Web Part<br/>to see the upcoming tasks for a project.<br/>";
labelText += "<br/>Configure this Web Part to set the associated Project Center Web Part<br/>";
labelText += "and the number of tasks to display. The current number is ";
labelText += NumDisplayTasks.ToString() + ".<br/>";
webControlLabel.Text = labelText;
webControl = webControlLabel;
}
return webControl;
}
For the code that creates the grid in the GetWebPartContent method, both solutions call the ReadProjectList method of the Project Web service to get a ProjectDataSet object with the names of all projects to which the user has access. Except for the text the Upcoming Tasks Web Part displays when it is initialized, the GetWebPartContent code is the same for both solutions.
The code performs the following steps:
-
Gets the GUID of the selected project.
-
Reads all of the project information into a ProjectDataSet variable named projectDS.
-
Creates a DataTable object named displayTaskDT and adds the columns to display in the grid.
-
Filters the uncompleted tasks into the taskRows variable.
-
Copies the specified number of uncompleted tasks to displayTaskDT.
-
Binds the displayTaskDT to the displayGrid DataGrid object.
-
Assigns displayGrid to the webControl variable to show the tasks in the Web Part.
The following code creates the grid within the if (projName != string.Empty){ . . . } section of the GetWebPartContent method, in the PWARefWebParts solution.
[C#]
displayGrid = new DataGrid();
displayGrid.CellPadding = 3;
// Get a DataSet of all the projects that the user has access to.
ProjectWS.ProjectDataSet projectListDS = ProjectWS.ReadProjectList();
DataRow[] projectRows = projectListDS.Project.Select("PROJ_NAME = '" +
projName + "'");
projGuid = (Guid)projectRows[0]["PROJ_UID"];
// Get a DataSet of all the tasks in a project.
projectDS = ProjectWS.ReadProject(projGuid,
PWARefWebParts.ProjectWS.DataStoreEnum.PublishedStore);
// Ignore the project summary task.
if (projectDS.Task.Count > 1)
{
// Create a table with just the three columns we want to display in the Web Part.
displayTaskDT.Columns.Add(new DataColumn("Task Name",
projectDS.Task.Columns[projectDS.Task.TASK_NAMEColumn.ColumnName].DataType));
displayTaskDT.Columns.Add(new DataColumn("Start",
projectDS.Task.Columns[projectDS.Task.TASK_START_DATEColumn.ColumnName].DataType));
displayTaskDT.Columns.Add(new DataColumn("Finish",
projectDS.Task.Columns[projectDS.Task.TASK_FINISH_DATEColumn.ColumnName].DataType));
// Filter out tasks that are already completed.
taskRows = projectDS.Task.Select(projectDS.Task.TASK_PCT_COMPColumn.ColumnName +
" <> 100 AND " +
projectDS.Task.TASK_IS_SUMMARYColumn.ColumnName + " = 0",
projectDS.Task.TASK_FINISH_DATEColumn.ColumnName + " ASC");
if (taskRows.Length > 0)
{
// Copy the number of tasks defined by the user into the DataTable object.
while (iTask < taskRows.Length && iTask < NumDisplayTasks)
{
taskDataRow = taskRows[iTask];
displayTaskDataRow = displayTaskDT.NewRow();
displayTaskDataRow["Task Name"] =
taskDataRow[projectDS.Task.TASK_NAMEColumn.ColumnName];
displayTaskDataRow["Start"] =
taskDataRow[projectDS.Task.TASK_START_DATEColumn.ColumnName];
displayTaskDataRow["Finish"] =
taskDataRow[projectDS.Task.TASK_FINISH_DATEColumn.ColumnName];
displayTaskDT.Rows.Add(displayTaskDataRow);
iTask++;
}
isTasksUncompleted = true;
}
}
if (isTasksUncompleted)
{
// Bind the DataTable object to the DataGrid object we display in the Web Part.
displayGrid.DataSource = displayTaskDT;
displayGrid.DataBind();
displayGrid.HeaderStyle.BackColor = System.Drawing.Color.Beige;
displayGrid.HeaderStyle.HorizontalAlign = HorizontalAlign.Center;
displayGrid.BorderStyle = BorderStyle.None;
webControl = displayGrid;
}
else
{
webControlLabel.Text = "<br/>This project has no upcoming tasks to display.<br/>";
webControl = webControlLabel;
}
In the PWARefWebParts solution, the OnPreRender override method inserts the string from the GetClientScript method into the page. The string contains a script that gets the project name from the ActiveX grid in the Project Center Web Part. The NoPWARefWebParts solution does not use the GetClientScript method, because it does not communicate with the Project Center Web Part.
[C#]
private string GetClientScript()
{
string sRetVal = String.Empty;
sRetVal = String.Format(@"
<SCRIPT for={0} event='OnNewRow(y,PaneNum)' language='jscript'>
location='{1}?UTWPProjName=' + {0}.GetCellValue(y, 'Project Name') + '&UTWPRow=' + y;
</SCRIPT>
",
// Replaceable parameters
GridID, // {0}
GetUrl()); // {1}
return sRetVal;
}
In the GetClientScript code, GridID is a property of the UpcomingTasksWP class. The tool part (UTToolPart) finds the value of GridID and sets the property when the user first configures the Upcoming Tasks Web Part. For example, if the GridID value is ctl00_m_g_cfef6446_3753_4c1d_a861_668b85258ab5_MSPJGrid and the value from GetUrl is /pwa/projects.aspx, then the OnPreRender method inserts the following script into the page before the browser renders it.
<SCRIPT for=ctl00_m_g_cfef6446_3753_4c1d_a861_668b85258ab5_MSPJGrid event='OnNewRow(y,PaneNum)' language='jscript'>
location='/pwa/projects.aspx?UTWPProjName=' + ctl00_m_g_cfef6446_3753_4c1d_a861_668b85258ab5_MSPJGrid.GetCellValue(y, 'Project Name') + '&UTWPRow=' + y;
</SCRIPT>
The GetUrl method finds the '?' URL option in the PathAndQuery string, and then returns just the part of the URL that corresponds to the Project Web Access page. For example, if the complete URL of the page request is http://servername/pwa/projects.aspx?UTWPProjName=TestProj2&UTWPRow=3, the PathAndQuery string is /pwa/projects.aspx?UTWPProjName=TestProj2&UTWPRow=3, and the returned value is /pwa/projects.aspx.
private string GetUrl()
{
string url;
int questionIndex = Page.Request.Url.PathAndQuery.IndexOf('?');
if (questionIndex > 0)
{
url = Page.Request.Url.PathAndQuery.Remove(questionIndex);
}
else
{
url = Page.Request.Url.PathAndQuery;
}
return url;
}
Web Parts are designed to be distributed over the Internet or an intranet. For security reasons when creating a custom Web Part, you must strong name it to ensure that users can trust the Web Part.
A strong name consists of the assembly's identity, a public key, and a digital signature. You can use the Strong Name tool (sn.exe) that is installed with the .NET Framework SDK to manage keys, generate signatures, and verify signatures.
To strong name the assembly:
-
Open a Visual Studio 2005 Command Prompt window and type the following.
cd <path to PWARefWebParts project>
sn -k StrongName.snk
-
Open your PWARefWebParts project in Visual Studio, and on the Project menu, click PWARefWebParts Properties.
-
Click the Signing tab on the side of the properties page, and then select Sign the assembly.
-
In the drop-down list, browse to the StrongName.snk file you created in Step 1.
By default, the AssemblyVersion property of your project is set to increment each time you recompile your Web Part. A Web Part Page identifies a Web Part with the version number specified in the web.config file. With the AssemblyVersion property set to increment, if you recompile your Web Part after importing it into a Web Part Page, the Web Part infrastructure looks for the old version number you specified in the web.config file.
If the assembly version numbers in the Web Part and the web.config file do not match, an error occurs. To prevent the version number of your Web Part from incrementing each time you recompile, you need to set the full version number in AssemblyInfo.cs.
To set the assembly version number:
-
In Solution Explorer, expand the Properties folder and double-click AssemblyInfo.cs.
-
Change the line [assembly: AssemblyVersion("1.0.*")]
to the following:
[assembly: AssemblyVersion("1.0.0.0")]