Sanibel Logic LLC

...Scalable Technologiesfor the Enterprise

ListView ItemTemplate and AlternatingItemTemplate

The ASP.NET 3.5 ListView server control provides an ItemTemplate and an AlternatingItemTemplate.  Only the ItemTemplate is required for defining each row's content.  The AlternatingItemTemplate can be used to define row content for odd numbered rows, however quite often the even and odd rows are identical with the exception that a different CSS class definition needs to be used, so to provide a different visible experience to the user.  Rather than duplicate the bulk of the ItemTemplate into the AlternatingItemTemplate, one can provide inline code to examine the container's row number and return a specific even or odd CSS class name, illustrated as follows:

   1:  <asp:ListView ID="listViewSort" 
   2:      DataSourceID="ObjMenu" 
   3:      DataKeyNames="Name"
   4:      runat="server">
   5:      <LayoutTemplate>
   6:          <table runat="server" 
   7:              class="listViewGrid"
   8:              cellspacing="0"
   9:              border="0">
  10:              
  11:              <tr runat="server" id="itemPlaceholder" />
  12:          
  13:          </table>    
  14:      </LayoutTemplate>
  15:      <ItemTemplate>
  16:          <tr class="<%# ((ListViewDataItem)Container).DisplayIndex % 2 == 0 ? "itemRow" : "altItemRow" %>">
  17:              <td align="left" style="width: 200px;">
  18:                  <asp:Label runat="server" 
  19:                      Text='<%# Eval("Name") %>' />
  20:              </td>
  21:              <td align="right" style="width: 100px;">   
  22:                  <asp:Label runat="server" 
  23:                      Text='<%# Eval("Price") %>' />
  24:              </td>
  25:              <td align="left" style="width: 400px;">
  26:                  <asp:Label runat="server" 
  27:                      Text='<%# Eval("Description") %>' />
  28:              </td>
  29:              <td align="right" style="width: 100px;">
  30:                  <asp:Label runat="server" 
  31:                      Text='<%# Eval("Calories") %>' />
  32:              </td>
  33:          </tr>
  34:      </ItemTemplate>
  35:  </asp:ListView>    

If the inline processing is more complicated than one would prefer for inline code, then one can invoke a protected web page method to accomplish similar or more complicated tasks, illustrated as follows:

   1:  <asp:ListView ID="listViewSort" 
   2:      DataSourceID="ObjMenu" 
   3:      DataKeyNames="Name"
   4:      runat="server">
   5:      <LayoutTemplate>
   6:          <table runat="server" 
   7:              class="listViewGrid"
   8:              cellspacing="0"
   9:              border="0">
  10:              
  11:              <tr runat="server" id="itemPlaceholder" />
  12:          
  13:          </table>
  14:      </LayoutTemplate>
  15:      <ItemTemplate>
  16:          <tr class="<%# GetCssName(Container) %>">
  17:              <td align="left" style="width: 200px;">
  18:                  <asp:Label runat="server" 
  19:                      Text='<%# Eval("Name") %>' />
  20:              </td>
  21:              <td align="right" style="width: 100px;">   
  22:                  <asp:Label runat="server" 
  23:                      Text='<%# Eval("Price") %>' />
  24:              </td>
  25:              <td align="left" style="width: 400px;">
  26:                  <asp:Label runat="server" 
  27:                      Text='<%# Eval("Description") %>' />
  28:              </td>
  29:              <td align="right" style="width: 100px;">
  30:                  <asp:Label runat="server" 
  31:                      Text='<%# Eval("Calories") %>' />
  32:              </td>
  33:          </tr>
  34:      </ItemTemplate>
  35:  </asp:ListView>    

The protected web page method in turn can provide simple or more complex logic.  In this illustration, the same logic is provided as the inline code example:

   1:  protected string GetCssName(object container)
   2:  {
   3:      if (container != null)
   4:      {
   5:          if (container.GetType() == typeof(ListViewDataItem))
   6:          {
   7:              if ((((ListViewDataItem)container).DisplayIndex % 2) == 0)
   8:              {
   9:                  return "itemRow";
  10:              }
  11:              else
  12:              {
  13:                  return "altItemRow";
  14:              }
  15:          }
  16:      }
  17:      return null;
  18:  }

 

While the ListView server control AlternatingItemTemplate is quite useful, its use should be limited to instances where there is truly differing content requirements between even and odd numbered rows.  The above illustrations also works equally as well with my previously posted ListViewSort custom control.

Obtain Client Browser Time Zone

Sanibel Logic is offering a free download which may be useful for your software development.  ClientTime is an ASP.NET custom control based on an article entitled "Its About Time" in the January 2007 issue of ASP.NetPro magazine.  The ASP.NetPro article discusses the JavaScript requirements for obtaining the client browser time zone. I take the article a step further by encapsulating C# support methods and JavaScript injection code into the form of an ASP.NET custom control.  ClientTime can be useful for ASP.NET based web applications to ensure that all Date/Times are expressed in the client browser's time zone.

To illustrate, the following Administrative query which monitors ASP.NET Health Monitoring alerts shows all date and times (highlighted within red block) expressed in the local time zone of the client browser (the user).  In the case of Sanibel Logic, and with many corporations, this is significant because the user may be in one time zone, while the ASP.NET web server hosting provider may be in another time zone.  

 ClientTime2_2 Click image to enlarge...

CLICK HERE to review the online ClientTime class documentation.

CLICK HERE to download the ClientTime VS.NET 2008 source project.

To activate ClientTime, simply drop the ClientTime custom control onto the ASP.NET master page or content page, illustrated as follows:

   1:  </head>
   2:  <body>
   3:      <form id="form1" runat="server">
   4:      
   5:      <asp:ScriptManager id="ScriptManager1" runat="server" AsyncPostBackTimeout="999">
   6:          <Scripts>
   7:              <asp:ScriptReference Path="~/Scripts/Ajax/ModalPopup.js" />
   8:              <asp:ScriptReference Path="~/Scripts/Ajax/ModalPopupConfirmation.js" />
   9:          </Scripts>
  10:      </asp:ScriptManager>
  11:      
  12:      <slcms:ClientTime ID="cltClientTime" runat="server" />
  13:      
  14:      <asp:HiddenField id="hflCurrentDiv" runat="server" /> 

The presence of the ClientTime custom control will cause the time zone related JavaScript to be injected.  To examine any of the ClientTime public properties or invoke any of the ClientTime methods simply reference the ClientTime custom control (or use FindControl if necessary).

ListView Header Sort Direction Indicators

The ASP.NET version 3.5 ListView provides developers with a much needed breath of fresh air with respect to simplifying HTML complexity when a data bound grid control is needed.  The ListView enables developers to have full control over generated HTML, yet have simplified data binding, custom paging and column sorting features.  While column sorting is provided with ListView, a clear indication as to which column has been most recently sorted is not provided.  There are many techniques on the Internet for providing DataGrid and GridView column sort indicators.  The purpose of this posting is to provide a custom control for providing a ListView column sort indicator.

 SortIndicator2_3

This ListView column sort indicator consists of two custom controls; a ListViewSort custom control, which inherits from ListView and a ListViewSortColumnHeader composite custom control.  The ListViewSortColumnHeader control is used when defining each sortable header within the ListView LayoutTemplate.  The ListViewSort custom control is used in place of the ListView ASP.NET control and exposes the following extension public properties:

  • ImageUrlAscending - The image file which is used to represent an ascending sort direction in the active sorted header.  If omitted, an embedded web resource is used as the default.
  • ImageUrlDescending - The image file which is used to represent an descending sort direction in the active sorted header.  If omitted, an embedded web resource is used as the default.
  • SortExpressionDefault - The name of the data element which is the default sort expression.
  • SortDirectionDefault - The initial sort direction when a header is sorted.

Other than the above extension public properties, the ListViewSort is identical to the ListView, illustrated as follows:

           
   1:  <myControls:ListViewSort ID="listViewSort" 
   2:      DataSourceID="ObjMenu" 
   3:      DataKeyNames="Name"
   4:      SortExpressionDefault="Name"
   5:      SortDirectionDefault="Ascending"
   6:      runat="server">
   7:      <LayoutTemplate>
   8:          <table runat="server" 
   9:              class="listViewGrid"
  10:              cellspacing="0"
  11:              border="0">
  12:              <tr>
  13:                  <th>
  14:                      <myControls:ListViewSortColumnHeader runat="server"
  15:                          Key="Name"
  16:                          Text="Food Name" />
  17:                  </th>
  18:                  <th>
  19:                      <myControls:ListViewSortColumnHeader runat="server"
  20:                          Key="Price"
  21:                          Text="Price" />
  22:                  </th>
  23:                  <th>
  24:                      <myControls:ListViewSortColumnHeader runat="server"    
  25:                          Key="Description"
  26:                          Text="Description" />
  27:                  </th>
  28:                  <th>
  29:                      <myControls:ListViewSortColumnHeader runat="server"
  30:                          Key="Calories"
  31:                          Text="Calories" />
  32:                  </th>
  33:              </tr>
  34:              
  35:              <tr runat="server" id="itemPlaceholder" />
  36:          
  37:          </table>
  38:          <asp:DataPager ID="dataPager" runat="server">
  39:              <Fields>
  40:                  <asp:NumericPagerField ButtonCount="10"
  41:                      NextPageText="..."
  42:                      PreviousPageText="..." />     
  43:              </Fields>
  44:          </asp:DataPager>
  45:      </LayoutTemplate>
  46:      <ItemTemplate>
  47:          <tr class="<%# ((ListViewDataItem)Container).DisplayIndex % 2 == 0 ? "itemRow" : "altItemRow" %>">
  48:              <td align="left" style="width: 200px;">
  49:                  <asp:Label runat="server" 
  50:                      Text='<%# Eval("Name") %>' />
  51:              </td>
  52:              <td align="right" style="width: 100px;">   
  53:                  <asp:Label runat="server" 
  54:                      Text='<%# Eval("Price") %>' />
  55:              </td>
  56:              <td align="left" style="width: 400px;">
  57:                  <asp:Label runat="server" 
  58:                      Text='<%# Eval("Description") %>' />
  59:              </td>
  60:              <td align="right" style="width: 100px;">
  61:                  <asp:Label runat="server" 
  62:                      Text='<%# Eval("Calories") %>' />
  63:              </td>
  64:          </tr>
  65:      </ItemTemplate>
  66:  </myControls:ListViewSort>    
  67:                                      
  68:  <asp:ObjectDataSource ID="ObjMenu" runat="server" 
  69:      EnablePaging="true"
  70:      SelectMethod="GetRows" 
  71:      SelectCountMethod="GetRowCount" 
  72:      TypeName="SanibelLogic.MenuDataSource.DataIO" 
  73:      DataObjectTypeName="SanibelLogic.MenuDataSource.Elements"
  74:      MaximumRowsParameterName="MaximumRows" 
  75:      StartRowIndexParameterName="StartRowIndex"                
  76:      SortParameterName="SortExpression">
  77:      <SelectParameters>
  78:          <asp:Parameter Name="SortExpression" 
  79:              Type="String" />
  80:          <asp:Parameter Name="MaximumRows" 
  81:              Type="Int32" />
  82:          <asp:Parameter Name="StartRowIndex" 
  83:              Type="Int32" />
  84:      </SelectParameters>
  85:  </asp:ObjectDataSource>

The ListViewSortColumnHeader composite control is used in place of a HyperLink ASP.NET control, normally used to activate column sorting.  ListViewSortColumnHeader exposes the Key  and Text  properties.  The Key  property associates the header with a databound expression, while the Text  property provides for the actual text being displayed in the header.  The ListViewSortColumnHeader control essentially generates the asp:HyperLink control and an asp:PlaceHolder control for an anticipated asp:Image control whenever the header participates in a sorting operation.

The ListViewSort custom control overrides a single event from the inherited ListView control; OnDataBound, illustrated as follows:

   1:  protected override void OnDataBound(EventArgs e)
   2:  {
   3:      if (base.SortExpression.Length == 0)
   4:      {
   5:          if (SortExpressionDefault.Length > 0)
   6:          {
   7:              base.Sort(SortExpressionDefault, SortDirectionDefault);
   8:          }
   9:      }
  10:   
  11:      List<Control> controls = Helpers.GetControlsByType(this, typeof(ListViewSortColumnHeader));
  12:      foreach (Control control in controls)
  13:      {
  14:          ListViewSortColumnHeader header = (ListViewSortColumnHeader)control;
  15:          if (header.HasSortDirectionIndicator() == true)
  16:          {
  17:              header.ResetSortDirectionIndicator();
  18:          }
  19:      }
  20:   
  21:      foreach (Control control in controls)
  22:      {
  23:          ListViewSortColumnHeader header = (ListViewSortColumnHeader)control;
  24:          if (header.Key == base.SortExpression)
  25:          {
  26:              header.SetSortDirectionIndicator(base.SortExpression, base.SortDirection);
  27:              break;
  28:          }
  29:      }
  30:   
  31:      base.OnDataBound(e);
  32:  }


During ListViewSort OnDataBound event processing, the existing ListViewSort headers are examined and any existing sort direction indicator asp:Image controls are removed and then the appropriate sort direction indicator asp:Image control is injected into the new header being sorted.  With the exception of maintaining the four ListViewSort extension public properties within ViewState, that is essentially all that ListViewSort does.  This level of simplicity ensures that sort direction indicator logic does not need to work its way into your application for each and every web page which uses a ListView.  Sample ListViewSort output is illustrated below.  In this illustration (and related project demo file) any of the headers can be clicked and the table data will be sorted in either ascending or descending order.

The project demo file for the above sample HTML and code above also demonstrates usage of an ASP.NET ObjectDataSource control, enabled for custom paging, to ensure that only the currently displayed ListViewSort data is maintained within ViewState at any point in time.

Click HERE to download VS.NET 2008 project demo file.