Extending the Time Tracker Starter Kit

 

By Bill Evjen
Reuters

July 2005

Applies to:
   Microsoft ASP.NET 2.0
   Microsoft Visual Web Developer 2005 Express Edition
   Microsoft Visual Studio 2005

Summary: Learn how to extend the new Time Tracker Starter Kit, which is a downloadable project type for Microsoft Visual Studio 2005 and Microsoft Visual Web Developer 2005 Express Edition. (17 printed pages)

Contents

Introduction
Changing How People Log In
Adding Your Own Components
Conclusion

Introduction

Built into Microsoft Visual Studio 2005 and Microsoft Visual Web Developer 2005 Express Edition, you will find a built-in project labeled as the Personal Web Site Starter Kit. Although this is the only Microsoft ASP.NET starter kit that is provided with the default install of these IDEs, you can still use either IDE to search for additional starter kits to use and extend upon (you can do this by clicking Community, Community Search, and then Starter Kits from the IDE menu).

One such starter kit that you will be able to find from MSDN is the Time Tracker Starter Kit (shown in Figure 1). This starter kit provides a series of pages that allow you to easily present and manage the time dedicated to multiple projects. Starting pages for this starter kit include a home page that provides logs of the time individual users allocate to specific projects. Also included in the starter kit are pages that allow you to view projects, resources dedicated to projects, as well as reports based either on projects or resources.

For a detailed look at the basics of this starter kit, please be sure to read about it in my previous article, Introducing the Time Tracker Starter Kit. If you need a review of this starter kit, it is recommended that you review this previous article first.

Click here for larger image.

Figure 1. Time Tracker Starter Kit (click the image for a larger picture)

The Time Tracker Starter Kit uses some of the best features provided by the latest release of ASP.NET 2.0, such as master pages, the membership and role management systems, navigation systems, and more.

This article will take a look at how to extend the application, because this is the purpose of this and the other starter kits. Remember, it is called a starter kit for a reason! You should consider this starter kit a starting point to use to build a truly unique application for yourself. In the end, you might not even end up with what is usually referred to as a Time Tracker Web site; instead, it might be something else entirely.

Changing How People Log In

When you first pull up the Time Tracker Starter Kit in the browser, the application will create three distinct roles (Project Administrator, Project Manager, and Consultant) in the system. These roles are created through some code in the Global.asax page's Application_Start event. Once the roles are created, you can then create users that are assigned one of them.

By default, the password required for entering users needs to be in a specific format. To avoid receiving an error when entering a new user, the password needs to be at least eight characters, consisting of uppercase and lowercase letters, as well as at least one digit (for example, 1, 2, 3) and one special character (for example !, #, $).

Although this is less secure, there are developers who will wish to lessen this security setting. To change the regular expression format for passwords, you simply need to make some minor changes in the application's web.config file.

This change needs to occur in the <membership> section of the web.config file that is found in the application's root folder. This change is shown in Listing 1.

Listing 1. Lessening the strict password requirements in the Time Tracker Starter Kit

<membership>
   <providers>
      <clear/>
         <add name="IssueTrackerStarterKit" 
          type="System.Web.Security.SqlMembershipProvider, System.Web, 
             Version=2.0.3600.0, Culture=neutral, 
             PublicKeyToken=b03f5f7f11d50a3a" 
          connectionStringName="aspnet_staterKits_TimeTracker" 
          enablePasswordRetrieval="false" enablePasswordReset="true" 
          requiresQuestionAndAnswer="false" applicationName="/" 
          requiresUniqueEmail="false" passwordFormat="Hashed"
          minRequiredNonalphanumericCharacters="0"
          minRequiredPasswordLength="3"
          description="Stores and retrieves membership data from the local 
             SQL Server database"/>
   </providers>
</membership>

As you can see from Listing 1, the necessary changes are made by changing the behavior of the membership provider. The membership provider is the data access layer that works with all of the membership features found in the Time Tracker application. By using a membership provider as is done here, you can use declarative programming to specify things such as the connection string to use, and more.

Nevertheless, before declaring how we want our new instance of the membership provider to behave, it is important to first clear out the previously declared membership providers that are specified in the machine.config file. To accomplish this task, the <clear/> declaration is used as the first line in the <providers> section.

Once the previous declaration has been cleared out, the next step is to declare the IssueTrackerStarterKit provider with the behaviors we are interested in using. The changes that I have made to the default IssueTrackerStarterKit provider are shown in bold in Listing 1. First, I changed the passwordFormat attribute (which was already present) to have a value of Hashed. This means that the end users' passwords are not stored as clear text (as was done when the value of the passwordFormat attribute was set to Clear), but instead they are stored in the database hashed. This means that even developers perusing the values stored in the database won't see their passwords. Whenever a user with a hashed password then logs in to the application, the password he or she provides will be hashed before being compared to the value that is stored in the database. If the two hashed values are the same, the password is a match; otherwise, the user has entered in an incorrect value.

In addition to the change to the passwordFormat attribute, the other changes include the addition of the minRequiredNonalphanumericCharacters and minRequiredPasswordLength attributes. The minRequiredNonalphanumericCharacters attribute specifies the number of non-alphanumeric characters that are required in the password. Non-alphanumeric characters include such values as !, #, or $. In Listing 1, this value has been changed to 0—meaning that there isn't a requirement for any non-alphanumeric characters. The other attribute, minRequiredPasswordLength, specifies the minimum number of characters the password needs to be made up of. The declaration in Listing 1 specifies that the passwords provided need to be at least three characters long.

With these password requirement changes in place, the application will now behave in a manner most people expect when it accepts passwords, though it is important to stress that using simpler passwords is a less-secure method and leaves the application more open to attacks such as dictionary attacks.

Adding Your Own Components

Of course, one of the major points of all of the starter kits that come from Microsoft is that they allow you to extend or modify them so that they work more how you want them to work. There are literally thousands of different ways in which you can modify these starter kits.

For an example of extending the Time Tracker Starter Kit, let's take a look at adding additional information to the TimeTracker.aspx page. Presently, the TimeTracker.aspx page (shown earlier in Figure 1) shows all of the actual time entries that a selected user makes towards a project. A nice addition to this page would be a section that displays all of the projects to which the person selected in the drop-down list is assigned, as well as the details for each of these projects.

Adding this information will be relatively straightforward, because we will be able to utilize the business and data logic layers that have already been established in the default starter kit install.

The first step in creating this new project section will be to work with the TimeTracker.aspx page to create a specific area on the page to place some text, an ObjectDataSource server control, and a GridView server control.

Getting the Server Controls on the Design Surface

Presently the main part of the TimeTracker.aspx page shows just a list of log entries for a particular user. These items are displayed through the use of an ObjectDataSource control (ProjectListDataSource) and a GridView control (ProjectListGridView).

For this example, we will want the list of projects that are assigned to the selected user to appear directly above this list of time entries. To accomplish this, add the following code to the TimeTracker.aspx page, as shown in Listing 2.

Listing 2. Adding controls to the TimeTracker.aspx page

<%@ Page Language="C#" MasterPageFile="~/TimeTracker/MasterPage.master" 
    CodeFile="TimeEntry.aspx.cs"
    Inherits="TimeEntry_aspx" 
    Title="My Company - Time Tracker - Log a Time Entry" 
    Culture="auto" UICulture="auto"  %>
<asp:Content ID="Content1" ContentPlaceHolderID="maincontent" 
 runat="Server">
    <div id="body">
        <div id="addhours">
           <!-- Code removed for clarity -->
        </div>
        <div id="timesheet">
            <fieldset>
                <legend>
                   Projects Status                
                </legend>
                <asp:ObjectDataSource ID="ObjectDataSource1" 
                 runat="server"></asp:ObjectDataSource>
                <asp:GridView ID="GridView1" 
                 runat="server"></asp:GridView>                   
            </fieldset><br />
            <fieldset>
               <!-- Log entry section removed for clarity -->
            </fieldset>
        </div>
    </div>
</asp:Content>

First, notice that this is a content page that utilizes a master page for much of its display. You can tell which master page is being used by looking at the MasterPageFile attribute in the @Page directive at the top of the page (MasterPage.master).

The content area of this page has two main sections: addhours and timesheet. Each of these sections is defined through its own <div> section on the page.

<div id="timesheet">
</div>

For this example, we are adding our controls to the timesheet section of the page. This is the right-hand portion of the page. Added to this section is a <fieldset> area that contains a basic ObjectDataSource control and a GridView control. The rest of this article will be concerned with showing how we can use the ObjectDataSource control to pull out specific data points about all the projects for which the selected user is assigned. Once selected, these retrieved items will then be displayed in the GridView control, with a bit of logic added to the final solution. At this point, your design page should appear as shown in Figure 2.

Aa479337.ttextend_fig02(en-us,MSDN.10).gif

Figure 2. The Design view of the TimeTracker.aspx page

Building the ObjectDataSource Server Control

If you look through the pages of the default install of the Time Tracker Starter Kit application, you will notice that it is heavily dependent on using the ObjectDataSource control to work with any underlying data. The ObjectDataSource controls that are used work with various methods defined in the data access layer (found in the DAL folder of the application). These methods are tied to specific stored procedures found in the Stored Procedures folder when looking at the ASPNETDB.MDF file in the Database Explorer of Visual Studio. In extending the application, it is best to stick to the same model that has already been designed.

The ObjectDataSource control enables you to use data source controls that work in a three-tiered environment. This data source control interacts with an object that you have established, and that object then interacts with a data store elsewhere. The object needs to be structured properly in order to work with the ObjectDataSource control, meaning that the object can perform basic operations such as selects, updates, inserts, and deletes.

Looking at the new ObjectDataSource control that we placed on the design surface of the TimeTracker.aspx page, highlight the control and open up its smart tag, as shown in Figure 3.

Aa479337.ttextend_fig03(en-us,MSDN.10).gif

Figure 3. Opening the ObjectDataSource1 control's smart tag

After opening the ObjectDataSource1 control's smart tag, you will need to select the only option available—Configure Data Source. This will launch a wizard that will allow you to construct the behavior properties of this control. On the first screen of the wizard, you are going to have to select the business object that you are interested in working with. This is illustrated in Figure 4.

Aa479337.ttextend_fig04(en-us,MSDN.10).gif

Figure 4. Selecting the business object for the ObjectDataSource control

In this case, we are binding this ObjectDataSource control to the Project object, which is defined in the Project.cs class file found in the BLL (Business Logic Layer) folder of the Time Tracker application.

Once you have selected the appropriate object, click the Next button. The second screen of the wizard allows you to select the methods that you will be using for the Select, Update, Insert, and Delete operations.

Since we are only interested in reading information from the database, and we are not going to be allowing the user to delete or update the project information, we are only going to need to work with the Select operation construction. The Select tab is shown in Figure 5.

Aa479337.ttextend_fig05(en-us,MSDN.10).gif

Figure 5. Choosing a method to use for the Select operation

From the drop-down list on the Select tab, you can choose the method that will be invoked when the Select operation is invoked. In this case, since we are only interested in showing the projects that are associated with the user selected in the user drop-down list, select the GetProjectByUserName method. This method takes a single parameter—the username of the logged-in user. In return, this method provides a generic List object of type Project. The GetProjectByUserName method and the Project type are all defined in the Project.cs file. Now that the Select operation is established in the wizard, click the Next button to proceed to the next step.

Since the GetProjectsByUserName method takes a single parameter (a String value that represents the username), the next step is to define where the value of the parameter is generated. The completed screen for this step is shown in Figure 6.

Aa479337.ttextend_fig06(en-us,MSDN.10).gif

Figure 6. Defining the parameter used for the GetProjectsByUserName method

The first step to take on this screen is to select a parameter source. Using the drop-down list, you will actually see that there are a lot of options for the source from which to grab the value. Since we are interested in only showing the list of projects of the user selected in the page's drop-down list, we will use the Control option as the parameter source. Besides the Control option, other options include None, Cookie, Form, Profile, QueryString, and Session.

Now that we have specified that we are interested in using a control to drive the value that is used for the userName parameter, the next drop-down list on the screen allows us to select the control for which we will retrieve this value. In this case, since we are going to be driving this off of the drop-down list of users that is already present on the TimeTracker.aspx page, we just need to select this control from the list of available controls. So, select UserList from the drop-down list of options. After making this selection, you will see that the userName parameter now lists its value as UserList.SelectedValue in the left-hand list of parameters. With all of this in place, click the Finish button. The ObjectDataSource control is now ready to be used.

Switching to the Source view of the page in Visual Studio, you can now see the code construction of the ObjectDataSource1 control. If you are not interested in using the wizard to generate the code for this control, you will want your code to appear as shown in Listing 3.

Listing 3. The generated code from the ObjectDataSource1 control

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
 SelectMethod="GetProjectsByUserName" 
 TypeName="ASPNET.StarterKit.BusinessLogicLayer.Project">
   <SelectParameters>
      <asp:ControlParameter ControlID="UserList" Name="userName" 
       PropertyName="SelectedValue" Type="String" />
   </SelectParameters>
</asp:ObjectDataSource>

It is important to pay attention to a couple of attributes in Listing 3. The first is the TypeName attribute. The TypeName attribute points to the class that will be used. In this case, the Project class is used. The next attribute to notice is the SelectMethod attribute. This references the method that will be used for the Select operation. Since we are not interested in performing any other type of operation, we don't need to use the available UpdateMethod, InsertMethod, or DeleteMethod attributes.

Since the Select operation takes a single parameter, the value of the parameter to use is defined within the <SelectParameters> elements, using an <asp:ControlParameter> server control. The ControlParameter control points to the UserList control through the use of the ControlID attribute, and associates the returned value from this control to the userName parameter value that is needed by the GetProjectsByUserName method. Finally, the PropertyName attribute specifies the property of the UserList control to use for the value, and the Type attribute specifies the type of the value (in this case, String).

Next, let's build the GridView control to work with this ObjectDataSource control.

Building the GridView Server Control

Now that the ObjectDataSource1 control that we just built is in place and ready to be used, you will find that it is rather easy to bind the data that the GetProjectsByUserName method returns to the GridView1 control that we have placed on the page.

The first step is to open up the GridView control's smart tag in the Design view of the page, and select ObjectDataSource1 as the data source you are interested in having this control bind itself to. This is illustrated in Figure 7.

Aa479337.ttextend_fig07(en-us,MSDN.10).gif

Figure 7. Selecting the ObjectDataSource1 control as the data source

Once you select the ObjectDataSource1 option from the drop-down list, you will notice that the GridView control in the Design view of the page will populate itself with all of the values that are found in the Project type used in the GetProjectsByUserName method. The altered GridView is presented in Figure 8.

Click here for larger image.

Figure 8. The auto-generated GridView control (click the image for a larger picture)

As you can see from Figure 8, there are a lot of values that come from the Project type instance. Although this is all of the possible information that we can work with, we are not going to actually need all of this information in order to display some simple properties for the end user on the projects that they are part of. By default, the columns created and the values returned in the columns are auto-generated for us. However, the nice thing about the GridView control is that we can modify this behavior by turning off this auto-generation option and defining for ourselves the columns to use.

A lot of the changes that are going to be required from the GridView control can be done through the wizard that is opened by clicking the Edit Columns link in the GridView control's smart tag. However, we are instead going to make all of our changes through the Source view, so we can get a better understanding of what we are doing to cause these changes to occur.

The first step is to establish the main properties for the GridView control. Listing 4 shows the base of the GridView control.

Listing 4. The main attributes of the GridView control

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
 Caption="Your assigned projects:<p />" DataSourceID="ObjectDataSource1" 
 BorderWidth="0" BorderStyle="None" Width="100%" CellPadding="2">
   <!-- The rest of the code here will be constructed shortly -->
</asp:GridView>

From Listing 4, you can see that the auto-generation of the columns is turned off by setting the value of the AutoGenerateColumns attribute to False. This will cause the GridView control to expect you to define the columns instead. This column definition will be covered shortly.

In addition to the AutoGenerateColumns attribute, the other important attribute to pay attention to is the DataSourceID attribute, which ties this output of this control to what is generated from the ObjectDataSource1 control. Besides these two important attributes, the rest of the attributes presented are styling attributes put in place so that this grid is similar in appearance to the other grid on the page. Once you have this base framework in place, the next step is to define the columns that are to be placed in this GridView control. The code for this is presented in Listing 5.

Listing 5. Defining the columns to use in the GridView control

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
 Caption="Your assigned projects:<p />" DataSourceID="ObjectDataSource1" 
 BorderWidth="0" BorderStyle="None" Width="100%" CellPadding="2">
   <Columns>
      <asp:BoundField DataField="Id" HeaderText="Project ID" />
      <asp:BoundField DataField="Name" HeaderText="Project" />
      <asp:BoundField DataField="ManagerUserName" 
       HeaderText="Project Manager" />
      <asp:BoundField DataField="EstimateDuration" 
       HeaderText="Duration Estimate (hours)">
         <ItemStyle HorizontalAlign="Center" />
      </asp:BoundField>
      <asp:BoundField DataField="CompletionDate" 
       HeaderText="Project Completion Date">
         <ItemStyle Font-Bold="True" HorizontalAlign="Center" />
      </asp:BoundField>
      <asp:BoundField DataField="ActualDuration" 
       HeaderText="Actual Time Allotted (hours)">
         <ItemStyle HorizontalAlign="Center" />
      </asp:BoundField>
   </Columns>
</asp:GridView>

Columns are defined within the <Columns> element by using BoundField server controls. Each column requires a single BoundField control and the two attributes you see being used in each instance of the control—the DataField and HeaderText attributes. The DataField attribute is referencing the data item that is coming from the GetProjectsByUserName method to use in the binding process. The HeaderText attribute is only meant for presentation purposes. This attribute provides a value that is used in the header for the column of the table that is generated from the GridView control's output. Since most databases have either cryptic names or just plain ugly names (for presentation), it usually is good to give your column names a more presentable value.

If the BoundField control is also going to include some styling that is specific only to the column it defines, you are going to want to also use the <ItemStyle> element to define the look and feel of that particular column.

Now that the columns are in place, the final step is to add three more sections to the GridView control's inner code. The code for this is presented in Listing 6.

Listing 6. Adding some style elements to the rows of the GridView control

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
 Caption="Your assigned projects:<p />" DataSourceID="ObjectDataSource1" 
 BorderWidth="0" BorderStyle="None" Width="100%" CellPadding="2">
   <Columns>
      <!-- Removed for clarity -->
   </Columns>
   <RowStyle VerticalAlign="Top" CssClass="row1" BorderStyle="None" />
   <HeaderStyle CssClass="grid-header" HorizontalAlign="Left" 
    VerticalAlign="Top" />
   <EmptyDataTemplate>                                        
      <asp:Label ID="NoProjects01" runat="server">
       There are no assigned projects for this user.
      </asp:Label>                    
   </EmptyDataTemplate>
</asp:GridView>

In this case, the <RowStyle> element defines the look and feel of every row that is defined in the GridView control. Notice that we used the <ItemStyle> element inside of the <asp:BoundField> element to define the look and feel of one specific column, whereas the <RowStyle> works to define each of the rows. As shown in Listing 6, this <RowStyle> simply aligns everything to the top of the cell, while using a CSS class and border style that is used by the other GridView control on the page.

Besides being able to define the look and feel of each of the rows contained in the GridView control, it is also possible to define the look and feel of only the header of the grid. Again, like the other GridView control on the page, this header uses the same style elements.

In addition to the style elements <RowStyle> and <HeaderStyle>, the <EmptyDataTemplate> is there to define what to show in the grid if there isn't a row to show. For this grid, a simple Label server control is presented, which informs the end user that there are no projects assigned to him or her at the moment.

With this ObjectDataSource and GridView control in place, running the code you will get the results illustrated in Figure 9.

Click here for larger image.

Figure 9. Our Time Tracker Starter Kit with the Project Status section (click the image for a larger picture)

As you can see from Figure 9, our Time Tracker application now contains a brief summary of each of the projects assigned. Instead of showing each data point that came from the GetProjectsByUserName method, we instead filtered and modified these data points before presenting them in the browser.

To enhance this list of projects a little more, let's intercept each row before it is drawn, in order to do a couple of things. First, let's make the date/time stamp a little more presentable, by removing the 12:00:00 AM part of the string and color-coding the cell depending on whether the date of completion has expired or not. To accomplish this task, we are going to need to add an event to the GridView control. Listing 7 shows the single change you will first have to make to the GridView control.

Listing 7. Adding an OnRowDataBound attribute to the GridView control

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
 Caption="Your assigned projects:<p />" DataSourceID="ObjectDataSource1" 
 BorderWidth="0" BorderStyle="None" Width="100%" CellPadding="2"
 OnRowDataBound="GridView1_RowDataBound">
   <!-- Inner content removed for clarity -->
</asp:GridView>

From Listing 7, you can see that the OnRowDataBound attribute was added, and that it points to the method that we will create next in the code-behind of the TimeTracker.aspx page (TimeTracker.aspx.cs). The code for this is presented in Listing 8.

Listing 8. The code-behind for the TimeTracker.aspx page

protected void GridView1_RowDataBound(object sender, 
  GridViewRowEventArgs e)
   {
      if (e.Row.RowType == DataControlRowType.DataRow)
      {
         DateTime dtNow = DateTime.Now;
         DateTime proTime = DateTime.Parse(e.Row.Cells[4].Text);

         if (proTime > dtNow)
         {
             e.Row.Cells[4].BackColor = System.Drawing.Color.Green;
             e.Row.Cells[4].ForeColor = System.Drawing.Color.White;
         }
         else
         {
             e.Row.Cells[4].BackColor = System.Drawing.Color.Red;
             e.Row.Cells[4].ForeColor = System.Drawing.Color.Black;
         }
         e.Row.Cells[4].Text = proTime.ToShortDateString();
     }
 }

Using the GridViewRowEventArgs object (instantiated as e), you first check to see whether the row being generated is of type DataRow (as opposed to a header, which would be of type DataControlRowType.Header). If what is being generated is a row, the time is retrieved from the value that is to be placed in the fourth cell, and that time is compared to the server time. If the time from the cell is greater than the present time, the BackColor of the fourth cell (the date/time cell) is changed to green, and the ForeColor (the color of the text) is changed to white. However, if the time found in the cell is less than the present time, this means that the estimated project completion date has passed. In this case, the BackColor is changed to red, and the ForeColor is changed to black. Then, finally, the last line changes the date/time value by using the ToShortDateString() method. The final result of these changes is presented in Figure 10.

Click here for larger image.

Figure 10. Changing the appearance of the row when rendered (click the image for a larger picture)

Conclusion

This article showed you how to work with the foundation of the Time Tracker Starter Kit and how to expand upon it for your own personal use. As you can see, it is rather easy to start incorporating your own features into this simple-to-use starter kit.

Have fun, and happy coding!

 

About the author

Bill Evjen is an active proponent of .NET technologies and community-based learning initiatives for .NET. He has been actively involved with .NET since the first bits were released in 2000. In the same year, Bill founded the St. Louis .NET User Group (http://www.stlnet.org), one of the world's first .NET user groups. Bill is also the founder of the International .NET Association (http://www.ineta.org), which represents more than 375,000 members worldwide.

Based in St. Louis, Missouri, USA, Bill is an acclaimed author and speaker on ASP.NET and XML Web services. He has written or coauthored Professional C#, 4th Edition and Professional VB 2005, 4th Edition (Wrox); XML Web Services for ASP.NET; Web Services Enhancements: Understanding the WSE for Enterprise Applications; Visual Basic .NET Bible; and ASP.NET Professional Secrets (all published by Wiley). In addition to writing, Bill is a speaker at numerous conferences, including DevConnections, VSLive, and TechEd.

Bill is a Technical Director for Reuters, the international news and financial services company, and he travels the world speaking to major financial institutions about the future of the IT industry. He graduated from Western Washington University in Bellingham, Washington, with a Russian language degree. When he isn't tinkering on the computer, he can usually be found at his summer house in Toivakka, Finland. You can reach Bill at evjen@yahoo.com. He presently keeps his weblog at http://www.geekswithblogs.net/evjen.

© Microsoft Corporation. All rights reserved.