Click here to Skip to main content
Click here to Skip to main content

SWAT - A simple Web-based Anomalies Tracker - Part 4

, 30 Jun 2003 CPOL
Rate this:
Please Sign up or sign in to vote.
An account of my experience in learning to develop in the .NET environment.

Fig.1 Swat's Main Editing Page

Swat Part 4

This is the fourth article in a series describing the development of an application I devised as a learning project. The purpose of the project was to gain experience developing in the .NET environment. The goal I had given myself was to define a WEB-based application and then develop the application using ASP.NET. The articles describe my implementation solution for the application.

The application being developed is a full-featured bug tracking application. In this article we will be implementing the bug editing page. At the end of the next article we will have a fully functional application and will have completed the first phase's requirements. As in the previous articles I must state that even though the solutions that I present performs the required functionality, in some cases the solution presented may not be the most optimal one. As I stated in the beginning, my expectation is to learn from reader comments where a better solution exists.

SWAT's Place in the BIG Picture

Even though you are supposed to design in quality instead of testing for quality, testing is an essential part of product development. And an integral part of testing is reporting and tracking. That's where SWAT comes in, the controlling tool for the Test phase.

Testing should also be more than just functional verification. Part of the testing function should include ‘requirements verification’ (you did have a requirements document, didn’t you?). One of the purposes of testing then, should be to compare what we said we would do to what we actually did. Check to see if we missed anything not just what’s not working.

Testing is just one of the phases of the development process. How testing is performed will have an impact on the overall success of the project. Likewise, following a development process does not by itself guarantee the success of a product but failure to follow one will certainly increase the probability of failure or as a minimum cost and schedule overruns. The development process does not have to be complex. It just needs to be a series of conscience steps like the following:

1.Concept->2.Validation/Design->3.Development->4.Test->5.Release (repeat 3,4,5 as needed or profitable)

What’s required for each phase will depend on the project and could be a very simple activity depending on the information available. Unfortunately, this is usually what happens on most development projects: 1 and 2 are skipped most of the time, 3 and 4 are usually combined (bad!), and 5 takes place at the point when the customer threatens with a lawsuit after the third missed ship date. This results in a lose-lose-lose situation.

Some pre-requisites

There's two related items that I'd like to cover before we get into the nitty-gritty of the bug editing page. The first is 'user-throttable' paging and the second is a side effect of providing 'user-throttable' paging.

In the admin page we hard coded the page size for the DataList to four entries. That was OK there because the number of items in any of the categories and the amount of time spent in the admin function should be small. For bugs it's a different story. There may be any number of bugs, and depending on the circumstance sometimes it may be beneficial to page five bugs at a time at other times you need to see twenty bugs at once. The paging scheme that we're going to implement for bugs is the same as for the admin page except that here the user will set the page size. The user will determine how fast/slow the paging will be, hence, 'user-throttable' paging.

A problem that comes up with allowing the user to control the size of the DataList is that any controls placed below the list are in the way. As the list grows it will overlay any items placed below it. Not good. One solution I found to resolve the problem was to implement the page as a table so that each section can grow automatically.

I just wanted to answer the question, "Why is the bug editing page laid out as a table?", ahead of time. Actually it's a table of tables. But it does what it's supposed to do.

Swat Bug Editing Page

The bug editing page is where the user enters, edits and changes the state of bugs. The UI for the page has three sections. The first section contains controls that allow the user to 'filter' what will be displayed. The second section consists of a list that will display the items that the user wants to see. And the third section contains a collection of controls that display the data associated with the currently selected item in the list. The filtering options include; the project the bugs were found in or to be added to, bugs assigned to the current user or all bugs, and bugs that are in a specific state. The other action that the user can perform is to change the state of a specific bug.

Refer to Fig.1 and let's start designing the page. Add a new WEB page to the project and name it SwatBugs. From the Visual Studio menu select 'Table->Insert...'. Make the table four rows by 2 columns. Each cel in the table is a sub-table and I’ve identified the configuration for each one below.

Table1(2rows/4cols)

(empty)

Table2(1row/1col)

(empty)

Table3(2rows/1col)

Table4(6rows/2cols)

Table5(2rows/4cols)

(empty)

Place the cursor in each of the table cells and then 'Table->Insert...' a table as defined above. For each sub-table drag-and-drop the items specified below.

Table1

Control

ID

Text

Label

lblSelProj

Select Project

Label

lblSelFilt

Select Filter Options

Label

lblPgSize

#/PG

DropDownList

ddlProjects

 

DropDownList

ddlListFilter

 

DropDownList

ddlBugStates

 

DropDownList

ddlPageSize

 

Button

btnGetBugs

Get Bugs

Note, for the 'Select Filter Options' cell you can set the 'colSpan' property to '2' so that it will span two columns.

The project DropDownList will be loaded with the list of projects that have been defined in the database. The other DropDownLists have values hardcoded in them. If you select 'Items' from the properties for the DropDownList you'll be presented with a dialog allowing you to enter the items for the list as well as a value for each entry. Here's the value assignments for the DropDownLists in Table1.

ddlListFilter

ddlListFilter

ListItem Value
AllMyItems 1
AllItems 2

ddlBugStates

ddlBugStates
ListItem Value
Open Bugs 1
Fixed Bugs 2
Closed Bugs 3

ddlPageSize

ddlPageSize
ListIem Value
5 5
10 10
15 15
20 20

Table2 just contains one item and that's a DataList. Drag one onto the table and accept the default ID. Start the PropertyBuilder for the DataList and set the Layout to ‘Flow’ as we did on the admin page. Edit the Header/Footer Template and set the label to 'Bug Title'. In the footer drag and drop five Buttons. Set their properties as shown below:

ID Text CommandName
btnPrev << Prev
btnCloseBug Close Bug CloseBug
btnFixBug Fix Bug FixBug
btnAddNew Add New AddNew
btnNext >> Next

The buttons in the footer are providing the same functionality as on the admin page. The command name gives you an idea of their purpose. Continue by editing the DataList item templates as follows :

Ctl Type ID CommandName Text
Label lblBugID '<%# DataBinder.Eval(Container.DataItem, "id") %>'
LinkButton lnkBugItem Select '<%# DataBinder.Eval(Container.DataItem, "itemname") %>'

SelectedItem

Ctl Type ID CommandName Text
LinkButton lnkEditBug Edit Edit
Label lblSelID '<%# DataBinder.Eval(Container.DataItem, "itemname") %>'

EditItem

Ctl Type ID CommandName Text
LinkButton lnkEditUpdate Update Update
LinkButton lnkEditCancel Cancel Cancel
TextBox txtBugTitle   '<%# DataBinder.Eval(Container.DataItem, "itemname") %>'

Table3 has two items, a Label and a TextBox. Set the Label's id='lblDescription' and the 'Text' property to: 'Description: Enter all information describing the bug and how to reproduce the bug.'. The TextBox's id='txtDescription' and set the 'TextMode' property to 'MultiLine'.

Drag and Drop the following controls for Table4

CtlType ID Text
Label lblEnteredBy Entered By
TextBox txtEnteredBy
Label lblEnteredDate Entered Date
TextBox txtEnteredDate
Label lblFixedBy Fixed By
TextBox txtFixedBy
Label lblFixedDate Fixed Date
TextBox txtFixedDate
Label lblClosedBy Closed By
TextBox txtClosedBy
Label lblClosedDate Closed Date
TextBox txtClosedDate

The TextBox controls above are set up as read-only but they could just as easily have been implemented using a Label control.

Here's the controls for Table5

CtlType ID Text
Label lblSelMod Select Module
Label lblAssignedTo Assigned To
Label lblSeverity Severity
Label lblPriority Priority
Label lblRevision Revision
DropDownList ddlModules
DropDownList ddlSeverity
DropDownList ddlPriority
TextBox txtRevision

We will load the module DropDownList with the modules that have been defined for the currently selected project. Here's the value assignments for the other DropDownLists in Table5.

ddlListFilter

ddlSeverity
ListItem Value
ShowStopper 1
Level1 2
Level2 3
Level3 4
Enhancement 5

 

ddlPriority
ListItem Value
1 1
2 2
3 3
4 4
5 5

Whew! I'm glad that's over with, now we can start making things do stuff. The first thing we need to do is provide the user with some means to filter the bugs that s/he wants to see. As a minimum the user is going to be interested in bugs for a specific project so we need to provide a list of available projects to select from. We're also going to give the user the option of seeing all of the bugs for the specified project or only the ones that have been assigned to him/her. The third option we'll provide the user is to select the state of the bugs to be viewed: all open bugs, all fixed bugs, or all closed bugs. Most of the time these options will be the same between sessions so we're going to persist them using cookies. All of this needs to be done when the page gets initialized so here's the code for the PageLoad event:

private void Page_Load(object sender, System.EventArgs e)
{
   pagesize = System.Convert.ToInt16(ddlPageSize.SelectedItem.Value);
   //We know we have this because the user is logged in
   if (Request.Cookies["UserID"] != null)
   {
      Response.Cookies.Add(Request.Cookies["UserID"]);
      Response.Cookies["UserID"].Expires = DateTime.MaxValue;
   }
   
   lblError.Text = "";
   if (!Page.IsPostBack)
   {
      // Get the cookies that we use to persist 
      // the user's selection. This
      // is only needed the first time because 
      // afterwards the controls maintain
      // their own state. And when the user makes 
      // a selection we'll persist it.
      if (Request.Cookies["ListFilter"] != null)
      {
         Response.Cookies.Add(Request.Cookies["ListFilter"]);
         Response.Cookies["ListFilter"].Expires = DateTime.MaxValue;
         Response.Cookies["ListFilter"].Value = 
                   Request.Cookies["ListFilter"].Value;
      }
      if (Request.Cookies["BugState"] != null)
      {
         Response.Cookies.Add(Request.Cookies["BugState"]);
         Response.Cookies["BugState"].Expires = DateTime.MaxValue;
         Response.Cookies["BugState"].Value = 
                  Request.Cookies["BugState"].Value;
      }
      if (Request.Cookies["Project"] != null)
      {
         Response.Cookies.Add(Request.Cookies["Project"]);
         Response.Cookies["Project"].Expires = DateTime.MaxValue;
         Response.Cookies["Project"].Value = 
                  Request.Cookies["Project"].Value;
      }
      
      try
      {
         SqlConnection cnn;
         SqlCommand cmd;
         SqlDataReader dr;
         string ConnectionString = 
            "user id=ASPNET;password=;initial catalog=swatbugs;"
            "data source=localhost;Integrated Security=false;"
            "connect timeout=30;";
         cnn = new SqlConnection(ConnectionString);
         cmd = cnn.CreateCommand();
         cnn.Open();
         //Populate the culprit combo...
         cmd.CommandText = "SELECT id, itemname FROM users";
         dr = cmd.ExecuteReader();
         ddlOwner.DataSource = dr;
         ddlOwner.DataTextField = "itemname";
         ddlOwner.DataValueField = "id";
         ddlOwner.DataBind();
         dr.Close();
         if (Response.Cookies["BugState"].Value != null)
            ddlBugStates.SelectedIndex = 
                ddlBugStates.Items.IndexOf(
                ddlBugStates.Items.FindByValue(
                Response.Cookies["BugState"].Value));
         else
            ddlBugStates.SelectedIndex = 0;
         if (Response.Cookies["ListFilter"].Value != null)
            ddlListFilter.SelectedIndex = ddlListFilter.Items.IndexOf(
                  ddlListFilter.Items.FindByValue(
                  Response.Cookies["ListFilter"].Value));
         else
            ddlListFilter.SelectedIndex = 0;
         dr.Close();
        
         //Populate the projects combo
         cmd.CommandText = "SELECT * FROM projects";
         dr = cmd.ExecuteReader();
         ddlProjects.DataSource = dr;
         ddlProjects.DataTextField = "itemname";
         ddlProjects.DataValueField = "id";
         ddlProjects.DataBind();
         dr.Close();
      }
      catch(Exception ex)
      {
         //We'll improve this...
         lblError.Text = "Database Error.";
      }
      if (Response.Cookies["Project"].Value != null)
         ddlProjects.SelectedIndex = ddlProjects.Items.IndexOf(
               ddlProjects.Items.FindByValue(
               Response.Cookies["Project"].Value));
      else
         ddlProjects.SelectedIndex = 0;
      cnn.Close();
                
      //The state variables that we'll use on this page
      ViewState["curpage"] = 1;
      ViewState["pagecount"] = 0;
        
      //Show the modules for the selected project
      BindModuleCB();
      //Populate the bug list
      BindBugList("0",ScrollMode.UpdateNextPage);
      EnableEditing(false);
      //Set up our paging parameters
      SetTotalPages();
   }
   pagecount = (int)ViewState["pagecount"];
}

So, the first time the page is loaded we check to see if we have any stored preferences for the user by checking if the cookies exists. Then we get a list of all users that have been defined in the database so we can load the 'owners' DropDownList. This list provides several purposes. First, when a bug is being created the bug can be assigned to a person by selecting from this list and when a bug is selected for viewing the owner is displayed on this list. The list is also used as a local cache of the users so we don't have to re-read the database to get the user's name to fill in the 'OpenedBy',etc. fields.

We load the module's DropDownList but we use a helper method for that because we also need to load the list if the users changes the selected project. The other functionality you should recognize as being the same as was done on the admin page.

You may also find interesting how the code above sets the selected index in the DropDownLists if a respective cookie was found.

Since we're using some external classes we need to add the following declarations to the top of the SwatBugs code source.

...
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text

User-throttable paging

The user interface for this page is similar to how the admin page was designed. We have a DataList that displays the title of the bugs so that the user can select the item of interest. The paging mechanism is the same except that we're allowing the user to specify how many items to display on each page. We're keeping track of the user's choice through the 'pagesize' variable. And as we did in the admin page we are using a 'pagecount' and 'currentpage' ViewState variables. The SetTotalPages method performs the same function as it did on the admin page. Here's the code:

protected void SetTotalPages()
{
   try
   {
      SqlConnection cnn;
      string ConnectionString = "user id=ASPNET;"
              "password=;initial catalog=swatbugs;"
              "data source=localhost;Integrated Security=false;"
              "connect timeout=30;";
      cnn = new SqlConnection(ConnectionString);
      cnn.Open();
      StringBuilder sqlString = 
         new StringBuilder("SELECT Count(*) FROM bugs WHERE ");
      sqlString.Append("Project=@projectid AND ");
      sqlString.Append("Status=@statusid ");
      //All or just mine
      if (ddlListFilter.SelectedItem.Text != "All Items")
         sqlString.Append("AND AssignedTo=@ownerid");
      SqlCommand cmd = cnn.CreateCommand();
      cmd.CommandText = sqlString.ToString();
      // Fill our parameters
      cmd.Parameters.Add("@projectid", SqlDbType.Int).Value =
              ddlProjects.SelectedItem.Value;
      cmd.Parameters.Add("@statusid", SqlDbType.Int).Value = 
              ddlBugStates.SelectedItem.Value;
      if (ddlListFilter.SelectedItem.Text != "All Items")
         cmd.Parameters.Add("@ownerid", SqlDbType.Int).Value = 
              Response.Cookies["UserID"].Value;
      int nRecords = (int)cmd.ExecuteScalar();
      if (nRecords%pagesize == 0)
      {
         pagecount = nRecords / pagesize;
      }
      else
      {
         pagecount = (nRecords / pagesize) + 1;
      }
      //Save it
      ViewState["pagecount"] = pagecount;
      ViewState["curpage"] = 1;
      cnn.Close();
   }
   catch(Exception e)
   {
      lblError.Text = "Database Error.";
   }
}

Each project is composed of one or more modules. Modules can be anything the user defines them to be, an executable, a page, a control, etc. The user defined the modules that make up a project in the admin page. Now we need to show the list of modules available for the selected project so that the user can enter bugs for that module or indicate in which module a bug was found. The BindModuleCB() method does that. As I mentioned above, the paging mechanism used on this page is almost identical to how it was implemented on the admin page. We keep track of which direction we’re paging or if we need to ‘page in place’ as a result of the user simply editing an item or deleting an entry, we keep track of how many items we are to display on a page, what our current page is, and how many pages are actually in the database. When we query the database we extract the just the number we need for a page and base it on the current top item being displayed (ID). Here's the code for BindModuleCB() and BindBugList() methods.

protected void BindModuleCB()
{
   try
   {
      SqlConnection cnn;
      SqlCommand cmd;
      SqlDataReader dr;
            
      string ConnectionString = "user id=ASPNET;password=;"
                 "initial catalog=swatbugs;data source=localhost;"
                 "Integrated Security=false;connect timeout=30;";
      cnn = new SqlConnection(ConnectionString);
      cmd = cnn.CreateCommand();
        
      cnn.Open();
      StringBuilder sqlString = new StringBuilder(
          "Select * from Modules where Project=");
      sqlString.Append(ddlProjects.SelectedItem.Value);
      cmd.CommandText = sqlString.ToString();
      dr = cmd.ExecuteReader();
      ddlModules.DataSource = dr;
      ddlModules.DataTextField = "itemname";
      ddlModules.DataValueField = "id";
      ddlModules.DataBind();
      cnn.Close();
   }
   catch(Exception e)
   {
      //We'll improve this...
      lblError.Text = "Database Error.";
   }
}
private void BindBugList(string sRecNum, ScrollMode direction)
{
   try
   {
      //Load the list by getting the top (n) records 
      // based on the position
      //indicated and the movement desired
      SqlConnection cnn;
      string strDirection = ">=";
      StringBuilder sqlString = new StringBuilder("SELECT TOP ");
      sqlString.Append(pagesize.ToString());
      sqlString.Append(" id, itemname FROM ");
      switch(direction)
      {
         case ScrollMode.UpdateInPlace:
            strDirection = ">=";
            break;
         case ScrollMode.UpdateNextPage:
            strDirection = ">";
            break;
         case ScrollMode.UpdatePrevPage:
            strDirection = "<";
            break;
      }
      string ConnectionString = "user id=ASPNET;password=;"
           "initial catalog=swatbugs;data source=localhost;"
           "Integrated Security=false;connect timeout=30;";
      cnn = new SqlConnection(ConnectionString);
      cnn.Open();
      sqlString.Append("bugs WHERE ID");
      sqlString.Append(strDirection);
      sqlString.Append(sRecNum);
      sqlString.Append(" AND Project=@projectid AND ");
      sqlString.Append("Status=@statusid ");
      //All or just mine
      if (ddlListFilter.SelectedItem.Text != "All Items")
         sqlString.Append("AND AssignedTo=@ownerid");
      if (direction == ScrollMode.UpdatePrevPage)
         sqlString.Append(" ORDER BY id DESC");
      SqlCommand cmd = cnn.CreateCommand();
      cmd.CommandText = sqlString.ToString();
      // Fill our parameters
      cmd.Parameters.Add("@projectid", SqlDbType.Int).Value =
           ddlProjects.SelectedItem.Value;
      cmd.Parameters.Add("@statusid", SqlDbType.Int).Value = 
           ddlBugStates.SelectedItem.Value;
      if (ddlListFilter.SelectedItem.Text != "All Items")
         cmd.Parameters.Add("@ownerid", SqlDbType.Int).Value = 
                Response.Cookies["UserID"].Value;
        
      SqlDataAdapter da = new SqlDataAdapter(cmd);
      DataSet ds = new DataSet();
      da.Fill(ds,"BUGS");
      ds.Tables["BUGS"].DefaultView.Sort = "id ASC";
      DataList1.DataSource = ds.Tables["BUGS"].DefaultView;
      DataList1.DataKeyField = "ID";
      DataList1.DataBind();
      cnn.Close();
   }
   catch(Exception e)
   {
      //We'll improve this...
      lblError.Text = "Database error.";
   }
}

 Bugs have more data than just the title so we're going to programmatically control access to these additional fields. When the user is ‘browsing’ the bugs, the controls that contain the data will be set to ‘read-only’ mode. Only when the user is editing a specific bug or entering a new bug will these controls be enabled. Here’s the method that controls the visibility of these controls.

 private void EnableEditing(bool bShow)
{
   btnGetBugs.Enabled = !bShow;
 
   txtDescription.Enabled = bShow;
   txtRevision.Enabled = bShow;
   ddlPriority.Enabled = bShow;
   ddlSeverity.Enabled = bShow;
   ddlOwner.Enabled = bShow;
   ddlModules.Enabled = bShow;
}

Just as we did with the admin DataList we need to control the visibility of the controls embedded in the footer. The code is slightly different here (for no reason) in that I’m maintaining a reference to the controls in the class definition. The controls are ‘found’ when the DataList creates it’s children (in the DataList ItemCreated() event) and we control their visibility in the Pre-Render() method. Here’s the code for those two methods (which you can add as we did on the admin page), but first add the following to the SwatBugs class definition:

public class SwatBugs : System.Web.UI.Page
{
   public enum BugState
   {
      Bug_Open = 1,
      Bug_Fixed,
      Bug_Closed
   }

   private Control ctlCloseBug; 
   private Control ctlFixBug; 
   private Control ctlAddNew; 
   private Control ctlPrev;
   private Control ctlNext;
 
   int pagesize = 5;
   int pagecount;
   ...
}

private void Page_PreRender(object sender, System.EventArgs e)
{
   bool bEditing = false;
   if (DataList1.EditItemIndex == -1)
   {
      ctlAddNew.Visible = true;
      ctlPrev.Visible = true;
      ctlNext.Visible = true;
   }
   else
   {
      bEditing = true;
      ctlPrev.Visible = false;
      ctlNext.Visible = false;
      ctlAddNew.Visible = false;
   }
 
   //If there's an item selected...
   if (DataList1.SelectedIndex >= 0 && bEditing == false)
   {
      //If it's open we can only fix it
      if (System.Convert.ToInt16(
          ddlBugStates.SelectedItem.Value) == 
                 (int)BugState.Bug_Open)
      {
         ctlCloseBug.Visible = false;
         ctlFixBug.Visible = true;
      }
      else
      {
         //If it's fixed we can close it
         if (System.Convert.ToInt16(
              ddlBugStates.SelectedItem.Value) == 
                 (int)BugState.Bug_Fixed)
         {
            ctlCloseBug.Visible = true;
            ctlFixBug.Visible = false;
         }
         else
         {
            //It's already closed, can't do anything else to it
            ctlCloseBug.Visible = false;
            ctlFixBug.Visible = false;
         }
      }
   }
   else
   {
      ctlCloseBug.Visible = false;
      ctlFixBug.Visible = false;
   }
 
   int curpage = (int)ViewState["curpage"];
 
   if (curpage > 1)
      ((Button)ctlPrev).Enabled = true;
   else
      ((Button)ctlPrev).Enabled = false;
   if (curpage < pagecount && curpage != pagecount)
      ((Button)ctlNext).Enabled = true;
   else
      ((Button)ctlNext).Enabled = false;
}
private void DataList1_ItemCreated(object sender, 
        System.Web.UI.WebControls.DataListItemEventArgs e)
{
   if (e.Item.ItemType == System.Web.UI.WebControls.ListItemType.Footer)
   {
      //Remember these guys so we can set them during PreRender
      ctlCloseBug = ((Control)(e.Item)).FindControl("btnCloseBug");
      ctlFixBug = ((Control)(e.Item)).FindControl("btnFixBug");
      ctlAddNew = ((Control)(e.Item)).FindControl("btnAddNew");
      ctlPrev = ((Control)(e.Item)).FindControl("btnPrev");
      ctlNext = ((Control)(e.Item)).FindControl("btnNext");
   }
}

OK, now we’re ready to start coding the user’s actions which are all going to come through the DataList’s command messages. Add an event handler for the DataList’s ItemCommand event (from the DataList's properties pane select events and then double-click the desired event). This is literally 'command central'.

private void DataList1_ItemCommand(object source, 
        System.Web.UI.WebControls.DataListCommandEventArgs e)
{
   EnableEditing(false);
   ViewState["selitem"] = -1;
 
   if (e.CommandName == "Prev")
   {
//      prevPage();
//      ClearBugData();
   }
   if (e.CommandName == "Next")
   {
//      nextPage();
//      ClearBugData();
   }
   if (e.CommandName == "AddNew")
   {
//      AddNew();
   }
   if (e.CommandName == "FixBug")
   {
//      FixBug();
//      ClearBugData();
   }
   if (e.CommandName == "CloseBug")
   {
//      CloseBug();
//      ClearBugData();
   }
 
   if (e.CommandName == "Select")
   {
      ViewState["selitem"] = e.Item.ItemIndex;
 
      DataList1.SelectedIndex = e.Item.ItemIndex;
      DataList1.EditItemIndex = -1;
 
      BindBugList(DataList1.DataKeys[0].ToString(),
               ScrollMode.UpdateInPlace);
 
//      BindBugData(e.Item.ItemIndex);
   }
   if (e.CommandName == "Edit")
   {
      DataList1.EditItemIndex = e.Item.ItemIndex;
      BindBugList(DataList1.DataKeys[0].ToString(),
              ScrollMode.UpdateInPlace);
      //Show edit posting buttons, enable all fields, 
      //and disable all others
//      EnableEditing(true);
   }
   if (e.CommandName == "Cancel")
   {
      DataList1.SelectedIndex = -1;
      DataList1.EditItemIndex = -1;
      BindBugList(DataList1.DataKeys[0].ToString(),
              ScrollMode.UpdateInPlace);
 
//      ClearBugData();
   }
   if (e.CommandName == "Update")
   {
//      updateBug(e);
   }
}

As we did on the admin page we'll remove the comments as we enable the functionality.

Conclusion

Well, I need a break and this is as good a stopping point as any. You can compile and run the application to check for any editing mistakes. If you had created any users with 'Developer' role, then when you run the application it will automatically start on the bug editing screen. You won't be able to do much but you can verify the code that we have so far. The only button enabled on the DataList footer should be the 'AddNew' button. Which is the first thing the user is going to want to do. If you added any projects they should appear on the projects DropDownList. And all of the bug data controls should be disabled. That's actually quite a bit. You can also check to see that if you defined a user with 'Developer' only role that he can't access the admin page.

The next article will complete the design for the bug editing page and at that point we'll have a fully functional bug tracking application.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Al Alberto
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
QuestionHow can i sharing an vb.net(.exe) which containing database using Asp.net within LAN? PinmemberjackNwan14-Jun-05 20:13 
QuestionHow to create a exit event to web page PinmemberjackNwan12-Jun-05 17:24 
AnswerRe: How to create a exit event to web page PinmemberAl Alberto13-Jun-05 10:26 
GeneralRe: How to create a exit event to web page PinsussAnonymous14-Jun-05 2:18 
Generalthis is just a test PinsussAnonymous13-Jul-05 12:18 
GeneralVB.NET Conversion PinmemberRand6264-Feb-05 11:33 
GeneralRe: VB.NET Conversion PinmemberAl Alberto5-Feb-05 2:34 
GeneralRe: VB.NET Conversion PinmemberRand6268-Feb-05 5:27 
GeneralNicely done!! PinmemberTom Archer9-Jul-03 15:59 
GeneralChanged naming convention for pages! Pinmemberdavidqxo9-Jul-03 5:14 
GeneralRe: Changed naming convention for pages! PinmemberAl Alberto9-Jul-03 6:28 
GeneralRe: Changed naming convention for pages! Pinmembermariahayek19-Sep-07 3:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141022.2 | Last Updated 1 Jul 2003
Article Copyright 2003 by Al Alberto
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid