When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, July 21, 2004

Creating a Row-Selectable DataGrid Control

By Scott Mitchell


Introduction


One of the main benefits of ASP.NET is the ease with which its functionality can be extended. Since all of the ASP.NET Web controls are, in actuality, classes in the .NET Framework, any developer can extend the functionality of a built-in Web control by creating a new class that derives from the Web control. This topic has been discussed previously on 4Guys in an article titled Easily Adding Functionality to ASP.NET Server Controls. In this earlier article we saw how to extend the Calendar Web control so that when the mouse moved over a particular day in the month, the day became highlighted. (See the live Calendar demo for more information.)

Sometime ago I created a custom Web control that extended the DataGrid, adding similar highlighting functionality on a row-by-row basis. That is, when the user moves his mouse over a DataGrid row, the entire row becomes highlighted. Furthermore, the control allows for the row to be "clickable." That is, a user can move his mouse anywhere over a row and click their mouse button. This will cause a postback and will trigger a specified DataGrid event.

In this article we'll take a look at the code for this custom control. We'll also discuss how to start using this control on your ASP.NET Web pages. If you've ever wanted a DataGrid that was selectable by row and offered row-level highlighting, read on! (Also, be sure to give the live demo a whirl!)

- continued -

Examining the Client-Side Source Code


The DataGrid Web control renders as an HTML <table>, with each row of the DataGrid as a <tr> tag and each column a <td>. HTML elements can trigger client-side JavaScript code to execute when a mouse moves over the element via the onmouseover and onmouseout client-side events. For example, you could have a messagebox displayed whenever someone moved their mouse over a paragraph by using the following mix of HTML and script:

<p onmouseover="alert('Hello, World!');">
   If you move your mouse over this text, a messagebox will appear.
</p>

To have a table row become highlighted when the mouse moves over it we will need to have the DataGrid control emit HTML and script so that the rendered content looks something like:

<script language="JavaScript">
var lastColorUsed;
function prettyDG_changeBackColor(row, highlight)
{
  if (highlight)
  {
    lastColorUsed = row.style.backgroundColor;
    row.style.backgroundColor = 'pink';
  }
  else
    row.style.backgroundColor = lastColorUsed;
}
</script>

<table ...>
  <tr onmouseover="javascript:prettyDG_changeBackColor(this, true);"
      onmouseout="javascript:prettyDG_changeBackColor(this, false);"
      ...>
         <td>...</td>
         <td>...</td>
         ...
  </tr>
  <tr onmouseover="javascript:prettyDG_changeBackColor(this, true);"
      onmouseout="javascript:prettyDG_changeBackColor(this, false);"
      ...>
         <td>...</td>
         <td>...</td>
         ...
  </tr>
</table>

The call to the client-side function prettyDG_changeBackColor sets the background color of the passed-in <tr> to pink if the highlight parameter is true, and sets it back to its previously saved color if highlight is false. A thorough discussion of this client-side code is beyond the scope of this article; to learn more about dynamic HTML, refer to the DHTML Tutorials at W3 Schools.

Implementing a Row-Highlighting DataGrid Class


Now that we know the markup the DataGrid must emit in order to provide row-level highlighting, the next challenge is to implement this functionality in a custom server control. If you were building this control from scratch you'd start by creating a new Visual Studio .NET Project of type Web Control Library. (The screenshot to the right shows the New Project dialog box in Visual Studio .NET 2003.) However, the complete source code and VS.NET 2003 Project files are downloadable at the end of this article.

To create a custom control with the desired features, we can create a class that inherits the DataGrid class. This means that our new class will automatically have all of the public and protected properties, methods, and events of the DataGrid, without us having to write one line of code! We can simply focus on adding the extended functionality.

To provide row-highlighting functionality for the DataGrid we'll need two properties in our class:

  • RowHighlightColor - specifies the color to highlight the row.
  • RowSelectionEnabled - a Boolean value indicating whether or not row highlighting should be used.
The following code shows the progress thus far:

public class PrettyDataGrid : System.Web.UI.WebControls.DataGrid
{
   public Color RowHighlightColor
   {
      get
      {
         object o = ViewState["RowHighlightColor"];
         if (o == null)
            return Color.Empty;
         else
            return (Color) o;
      }
      set
      {
         ViewState["RowHighlightColor"] = value;
      }
   }

   public bool RowSelectionEnabled
   {
      get
      {
         object o = ViewState["RowSelectionEnabled"];
         if (o == null)
            return true;
         else
            return (bool) o;
      }
      set
      {
         ViewState["RowSelectionEnabled"] = value;
      }
   }
}

One thing to notice is that the property values are being stored and retrieved from the ViewState state bag. By saving these properties to the ViewState, any programmatic changes to these properties are persisted across postback. A thorough discussion of why this code is needed, or how view state works underneath the covers, is a bit off-topic for this article. For more information refer to Understanding ASP.NET View State.

We now need to inject the necessary client-side script. There are two bits of client-side code that need to be added to the DataGrid - the onmouseover / onmouseout event handlers in the <tr> tags, and the <script> block with the prettyDG_changeBackColor function.

To emit the onmouseover / onmouseout event handlers we need to override the DataGrid's CreateItem() method. CreateItem() is called once for every record in the DataSource being bound to the DataGrid. CreateItem() creates a new DataGridItem class (which is what is eventually rendered as a <tr>) and returns it. What we need to do is add the onmouseover / onmouseout event handlers, which can be accomplished by using the Attributes collection of the DataGridItem class like so:

public class PrettyDataGrid : System.Web.UI.WebControls.DataGrid
{
   ...
   
   protected override DataGridItem CreateItem(int itemIndex, 
                                  int dataSourceIndex, ListItemType itemType)
   {
      // Create the DataGridItem
      DataGridItem item = new DataGridItem(itemIndex, dataSourceIndex, itemType);

      // Set the client-side onmouseover and onmouseout if RowSelectionEnabled == true
      if (RowSelectionEnabled && itemType != ListItemType.Header && 
           itemType != ListItemType.Footer && itemType != ListItemType.Pager)
      {
         item.Attributes["onmouseover"] = "javascript:prettyDG_changeBackColor(this, true);";
         item.Attributes["onmouseout"] = "javascript:prettyDG_changeBackColor(this, false);";
      }

      return item;
   }
}

This overridden CreateItem() method starts by creating a new DataGridItem instance. It then adds the client-side onmouseover / onmouseout event handlers to the DataGridItem if the DataGridItem is not a Header, Footer, or Pager, and if the RowSelectionEnabled property is true.

The DataGrid must also emit the client-side <script> block that contains the prettyDG_changeBackColor function. Typically <script> blocks are emitted in control's OnPreRender() method. (For a more in-depth look at this process be sure to read: Injecting Client-Side Script from an ASP.NET Server Control.) The following code shows the extended DataGrid's OnPreRender() method.

public class PrettyDataGrid : System.Web.UI.WebControls.DataGrid
{
   ...
   
     protected override void OnPreRender(EventArgs e)
     {
        base.OnPreRender (e);

        if (!RowSelectionEnabled) return;

        // add the client-side script to change the background color
        const string SCRIPT_KEY = "prettyDGscript";
        const string SCRIPT = @"<script language=""JavaScript"">
<!--
var lastColorUsed;
function prettyDG_changeBackColor(row, highlight)
{{
if (highlight)
{{
  lastColorUsed = row.style.backgroundColor;
  row.style.backgroundColor = '#{0:X2}{1:X2}{2:X2}';
}}
else
  row.style.backgroundColor = lastColorUsed;
}}
// -->
</script>";

        if (RowHighlightColor != Color.Empty && 
                !Page.IsClientScriptBlockRegistered(SCRIPT_KEY))
           Page.RegisterClientScriptBlock(SCRIPT_KEY, 
                    String.Format(SCRIPT, RowHighlightColor.R, 
                           RowHighlightColor.G, RowHighlightColor.B));
   }
}

Adding Row-Level Clicking


At this point, the extended DataGrid provides capabilities for a page developer to specify a color for the row-level highlighting, and have the rendered DataGrid emit the right mix of HTML and client-side script to have a row become "highlighted" when the user's mouse rests over the row. An additional feature that would be nice to add would be row-level clicking. With row-level clicking, if a user clicks anywhere in a row over which the mouse hovers, a postback occurs and a specified DataGrid event fires on the server-side. This can be particularly useful in parent/child scenarios, when clicking on a row should display detail records about the clicked row, or whisk the user to a URL that has more information on that particular row.

For maximum flexibility, the page developer should be able to specify what DataGrid event fires when a row is clicked. To account for this, let's add a RowClickEventCommandName property to the extended DataGrid. When a row is clicked, a postback will ensue and an event will be bubbled up to the DataGrid based on the value of this RowClickEventCommandName property. For example, if you want the DataGrid's DeleteCommand event to fire whenever a row is clicked, simply set the RowClickEventCommandName property to "Delete". If you want the EditCommand event to fire, set RowClickEventCommandName to "Edit".

To implement row-level clicking we need to create a custom DataGridItem class, one that knows what event to raise when it was clicked. This extended DataGridItem class, which I call PrettyDataGridItem, inherits the DataGridItem class and only adds one method: RaisePostBackEvent(). RaisePostBackEvent() is the method that the ASP.NET Page class will automatically call when it detects that the postback transpired due to the row being clicked. In this method we simply want to bubble the event up to the DataGrid, which will then raise its ItemCommand event. An additional DataGrid event might be raised as well (DeleteCommand, EditCommand, etc.), based on the value of the RowClickEventCommandName property.

public class PrettyDataGridItem : DataGridItem, IPostBackEventHandler
{
   public PrettyDataGridItem(int itemIndex, int dataSetIndex, 
              ListItemType itemType) : base(itemIndex, dataSetIndex, itemType) {}

   public void RaisePostBackEvent(string eventArgument)
   {
      CommandEventArgs commandArgs = new CommandEventArgs(eventArgument, null);
      DataGridCommandEventArgs args = 
                     new DataGridCommandEventArgs(this, this, commandArgs);
      base.RaiseBubbleEvent(this, args);
   }
}

Finally, we need to inject some client-side script code that causes a postback when the <tr> is clicked. This can be accomplished by adding a client-side onclick event handler for the PrettyDataGridItem. This is accomplished by a call to the CreateClickEvent() method from the OnPreRender() method. The CreateClickEvent() method simply iterates through the set of PrettyDataGridItems and for each PrettyDataGridItem that's not a Header, Footer, or Pager, it adds the onclick attribute.

protected virtual void CreateClickEvent()
{
   foreach(PrettyDataGridItem dgi in this.Items)
      if (dgi.ItemType != ListItemType.Header
            && dgi.ItemType != ListItemType.Footer 
            && dgi.ItemType  != ListItemType.Pager)
         dgi.Attributes["onclick"] = 
               Page.GetPostBackClientHyperlink(dgi, RowClickEventCommandName);
}

And that's all there is to it!

Using PrettyDataGrid in an ASP.NET Web Page


At the end of this article you'll find the complete source code for the PrettyDataGrid control. To use it in your ASP.NET Web pages, you can do one of two things:
  1. You can simply add the assembly in the download (the .dll file) to the Toolbox in Visual Studio .NET. To accomplish this, right-click on the Toolbox and choose to Add/Remove Items, and then Browse to the appropriate file. From there, you can drag-and-drop the PrettyDataGrid onto a Web page, set its properties, and off you go! (Be sure to use the right version of the assembly.)
  2. If you want to investigate the code in further detail, you can compile the code yourself. If you are using Visual Studio .NET you can simply open the Project file in the download. (Be sure to open the right version for your version of Visual Studio .NET.) Following that, you'll want to Build the Solution, which will compile the project and create the assembly.
To get a better feel for this control, take a moment to try out the live demo.

Happy Programming!

  • By Scott Mitchell


    Attachments


  • Download the complete source code (in ZIP format)



  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article