GridView Column Sorting - Up/Down Arrows

December 7, 2008 19:32 by wjchristenson2

In this article, I will show you how to manually sort the GridView WebControl and display sort direction arrows.  The GridView has built-in sorting capabilities, however if we want visual feedback as to what column is being sorted and to what direction, we have to perform this ourselves.  While extending the GridView WebControl would be optimal, I'm going to show a quick way to get it done without creating a new GridView control.  Maybe in a future post I'll show how we can create a custom GridView control with sort arrows.  Here is a picture of what our final sorted GridView will look like.

Here is the HTML markup of our GridView:


   1:            <asp:GridView 
   2:              ID="GridView1" 
   3:              runat="server" 
   4:              AutoGenerateColumns="False" 
   5:              DataKeyNames="CustomerID" 
   6:              CssClass="gridview" 
   7:              RowStyle-CssClass="gridview_itm" 
   8:              AlternatingRowStyle-CssClass="gridview_aitm" 
   9:              HeaderStyle-CssClass="gridview_hdr" 
  10:              PagerStyle-CssClass="gridview_pgr">
  11:              <Columns>
  12:                <asp:TemplateField>
  13:                  <HeaderTemplate>
  14:                    <asp:LinkButton ID="CustomerID_SortLnkBtn" runat="server" Text="Customer ID:" ToolTip="Click to Sort Column" CommandName="Sort" CommandArgument="CustomerID" CausesValidation="false" />
  15:                    <asp:ImageButton ID="CustomerID_SortImgBtn" runat="server" Visible="false" ToolTip="Click to Sort Column" CommandName="Sort" CommandArgument="CustomerID" CausesValidation="false" />
  16:                  </HeaderTemplate>
  17:                  <ItemTemplate><%#Eval("CustomerID")%></ItemTemplate>
  18:                </asp:TemplateField>
  19:                
  20:                <asp:TemplateField>
  21:                  <HeaderTemplate>
  22:                    <asp:LinkButton ID="CompanyName_SortLnkBtn" runat="server" Text="Company Name:" ToolTip="Click to Sort Column" CommandName="Sort" CommandArgument="CompanyName" CausesValidation="false" />
  23:                    <asp:ImageButton ID="CompanyName_SortImgBtn" runat="server" Visible="false" ToolTip="Click to Sort Column" CommandName="Sort" CommandArgument="CompanyName" CausesValidation="false" />
  24:                  </HeaderTemplate>
  25:                  <ItemTemplate><%#Eval("CompanyName")%></ItemTemplate>
  26:                </asp:TemplateField>
  27:                
  28:                <asp:TemplateField>
  29:                  <HeaderTemplate>
  30:                    <asp:LinkButton ID="ContactName_SortLnkBtn" runat="server" Text="Contact Name:" ToolTip="Click to Sort Column" CommandName="Sort" CommandArgument="ContactName" CausesValidation="false" />
  31:                    <asp:ImageButton ID="ContactName_SortImgBtn" runat="server" Visible="false" ToolTip="Click to Sort Column" CommandName="Sort" CommandArgument="ContactName" CausesValidation="false" />
  32:                  </HeaderTemplate>
  33:                  <ItemTemplate><%#Eval("ContactName")%></ItemTemplate>
  34:                </asp:TemplateField>
  35:              </Columns>
  36:            </asp:GridView>

The first step is to acquire customers from the Northwind database in the form of a DataTable.  We will then acquire a DataView object from our DataTable, and sort the view.  Once the data in our DataView has been sorted, we will then bind the GridView to the sorted DataView.  To accomplish the data retrieval, sorting, and data binding, I've created the following method:


   1:      Private Sub GridView1_DataBind()
   2:          Dim dt As DataTable = New DataTable()
   3:   
   4:          'fill our datatable w/ customers from the DB
   5:          Using conn As New SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("NorthwindConnectionString").ConnectionString)
   6:              Dim sql As String = "SELECT [CustomerID], [CompanyName], [ContactName] FROM [Customers] WITH (NOLOCK)"
   7:              Dim cmd As SqlCommand = New SqlCommand(sql, conn)
   8:              Dim reader As SqlDataReader = Nothing
   9:   
  10:              Try
  11:                  conn.Open()
  12:                  reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
  13:                  dt.Load(reader)
  14:              Finally
  15:                  If Not reader Is Nothing AndAlso Not reader.IsClosed Then
  16:                      reader.Close()
  17:                  End If
  18:              End Try
  19:          End Using
  20:   
  21:          If dt.Rows.Count > 0 Then
  22:              'get a dataView object from our dataTable of customers
  23:              Dim dv As DataView = dt.DefaultView
  24:   
  25:              'if the user has elected to sort the gridview
  26:              If Not String.IsNullOrEmpty(Me.SortBy("GridView1")) Then
  27:                  'get the sort expression and apply it to our dataView
  28:                  Dim sortExpr As String = Me.SortBy("GridView1") & " " & IIf(Me.SortDirection("GridView1") = WebControls.SortDirection.Ascending, "ASC", "DESC").ToString()
  29:                  dv.Sort = sortExpr
  30:              End If
  31:   
  32:              'bind the dataView to our GridView
  33:              Me.GridView1.DataSource = dv
  34:              Me.GridView1.DataBind()
  35:          End If
  36:      End Sub

The logic is pretty straight forward.  Take note to line 28.  I am using 2 properties to store what column I am sorting by and what direction it is being sorted.  I persist the values in the ViewState and I also pass what GridView I either want to retrieve or store values for.  This allows me to have more that 1 sorting GridView on my page at a time using the same 2 properties.  Here's the code for the 2 properties to assist us with sorting.


   1:      ''' <summary>
   2:      ''' Gets or sets the column name to be sorted.
   3:      ''' </summary>
   4:      ''' <param name="GridViewID">The unique ID of the GridView.</param>
   5:      ''' <value></value>
   6:      ''' <returns></returns>
   7:      ''' <remarks></remarks>
   8:      Private Property SortBy(ByVal GridViewID As String) As String
   9:          Get
  10:              Dim o As Object = ViewState(GridViewID & "_SortBy")
  11:              If Not o Is Nothing Then
  12:                  Return o.ToString()
  13:              Else
  14:                  Return String.Empty
  15:              End If
  16:          End Get
  17:          Set(ByVal value As String)
  18:              ViewState(GridViewID & "_SortBy") = value
  19:          End Set
  20:      End Property
  21:   
  22:      ''' <summary>
  23:      ''' Gets or sets the sort direction.
  24:      ''' </summary>
  25:      ''' <param name="GridViewID">The unique ID of the GridView.</param>
  26:      ''' <value></value>
  27:      ''' <returns></returns>
  28:      ''' <remarks></remarks>
  29:      Private Property SortDirection(ByVal GridViewID As String) As SortDirection
  30:          Get
  31:              Dim o As Object = ViewState(GridViewID & "_SortDirection")
  32:              If Not o Is Nothing Then
  33:                  Return DirectCast(o, SortDirection)
  34:              Else
  35:                  Return WebControls.SortDirection.Ascending
  36:              End If
  37:          End Get
  38:          Set(ByVal value As SortDirection)
  39:              ViewState(GridViewID & "_SortDirection") = value
  40:          End Set
  41:      End Property

We have the sort by and sort direction properties (storing/persistance mechanisms).  We have the data retrieval, sorting of the data, and data binding method in place.  Now what we have to do is think about what events we need to account for.  First, on initial page load we'll want to fetch customer data and bind it to our GridView.  We'll only want to bind our GridView the first time the page loads and not subsequent postbacks.


   1:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   2:          If Not Page.IsPostBack Then
   3:              'bind the gridview on page first load
   4:              GridView1_DataBind()
   5:          End If
   6:      End Sub

Now we are ready to make the magic happen.  We want to handle the GridView's RowDataBound event and either show or hide our up/down arrows if the user has elected to sort a column.


   1:      Private Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
   2:          'if the row being dataBound is the header row - toggle sort image visibility/directions
   3:          If e.Row.RowType = DataControlRowType.Header Then
   4:              ToggleSortArrows(e.Row, "GridView1")
   5:          End If
   6:      End Sub
   7:   
   8:      Private Sub ToggleSortArrows(ByVal headerRow As GridViewRow, ByVal gridViewID As String)
   9:          Dim sortImgBtn As ImageButton = Nothing
  10:   
  11:          'loop through each cell in the header row
  12:          For Each tc As TableCell In headerRow.Cells
  13:              'loop through each control in the cell
  14:              For Each c As Control In tc.Controls
  15:                  'if the control is an image button and is our sort image button
  16:                  If TypeOf c Is ImageButton AndAlso c.ID.EndsWith("SortImgBtn") Then
  17:                      sortImgBtn = DirectCast(c, ImageButton)
  18:   
  19:                      'if the image button is in the column being sorted
  20:                      If Me.SortBy(gridViewID) = sortImgBtn.ID.Split(CChar("_"))(0) Then
  21:                          'show the image button and set its image url (sorted column)
  22:                          sortImgBtn.Visible = True
  23:                          If Me.SortDirection(gridViewID) = WebControls.SortDirection.Ascending Then
  24:                              sortImgBtn.ImageUrl = "~/img/uparrow.gif"
  25:                          Else
  26:                              sortImgBtn.ImageUrl = "~/img/dnarrow.gif"
  27:                          End If
  28:                      Else
  29:                          'hide the image button (not a sorted column)
  30:                          sortImgBtn.Visible = False
  31:                      End If
  32:                  End If
  33:              Next
  34:          Next
  35:      End Sub

Basically what we are doing is detecting if the row being DataBound is the header row or not.  If it is, we want to loop through each cell in the header row and get a handle on the column's associated sort image.  We use the ID of the sort image to acquire what column it represents and compare it to our SortBy property.  If it matches, then we want to show the appropriate sort direction image.  We hide the other sort images in non-sorted columns.

The only task we have left to account for is how to fire our sorting event.  Take a quick look at our GridView HTML markup.  The header row has both a LinkButton and ImageButton that raise a GridView command event to which we pass the column name that the user wants to sort by.  We then handle the event in our code behind.


   1:      Private Sub GridView1_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView1.RowCommand
   2:          If e.CommandName.ToUpper = "SORT" Then
   3:              InitializeSort(e.CommandArgument, "GridView1")
   4:              GridView1_DataBind()
   5:          End If
   6:      End Sub
   7:   
   8:      Private Sub InitializeSort(ByVal sortBy As String, ByVal gridViewID As String)
   9:          If Me.SortBy(gridViewID) = sortBy Then
  10:              If Me.SortDirection(gridViewID) = WebControls.SortDirection.Ascending Then
  11:                  Me.SortDirection(gridViewID) = WebControls.SortDirection.Descending
  12:              Else
  13:                  Me.SortDirection(gridViewID) = WebControls.SortDirection.Ascending
  14:              End If
  15:          Else
  16:              Me.SortBy(gridViewID) = sortBy
  17:              Me.SortDirection(gridViewID) = WebControls.SortDirection.Ascending
  18:          End If
  19:      End Sub

Once we capture our sort row command, we initialize our SortBy and SortDirection properties.  We either toggle the direction of the sorted column or the sorted column is a new column to be sorted to which we default the column to be sorted Ascending.

I hope this article helped a bit.  It's a quick way to get a GridView sorted with visual indicators (sort arrows) without creating a new custom GridView control.

GridViewSorting_Soln.zip (90.61 kb)


Currently rated 2.2 by 6 people

  • Currently 2.166667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Save Scroll Position GridView Control

October 29, 2008 16:09 by wjchristenson2

I've seen a lot of articles on the web describing how to scroll a GridView.  Some of those articles may even go a little deeper and show you how to persist the scroll position across PostBacks.  What I haven't been able to find is how to encapsulate the scrolling and saving/persisting of the scroll position into a custom GridView control.  That is what I am going to show you how to do today.

There are many ways a developer may want to scroll their GridView (horizontal, vertical, freezing the header/footer, etc).  I'm not going to go into creating a bullet proof GridView control to accomplish those tasks.  My objective is to show you a technique to save/set the scroll position across PostBacks within a custom GridView control.

Here is what our end GridView control will look like.  First you can see a scrollable GridView.  When we scroll the GridView and then click the PostBack button, the page will perform a PostBack and the <div> will scroll itself back to where we left off.



So let's get to it.  First thing we do is create a new control and inherit from the GridView.  We then implement the IPostBackDataHandler to get and set our scroll position across PostBacks.  You'll see the LoadPostData function accomplishes this.  Take note that in line 3 I am referencing the name of my hidden field that I'll go over later.  We also tell the page that our new custom GridView (Me) has data to PostBack and we can do this in the GridView's Init.


   1:      Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData
   2:          'get and set the new scroll position
   3:          Dim postedValue As String = postCollection(Me.ClientID & "_scroll")
   4:          If Not postedValue Is Nothing Then
   5:              Dim presentValue As Integer = Me.ScrollPosition
   6:              Me.ScrollPosition = CType(postedValue, Integer)
   7:              Return Not presentValue.Equals(Me.ScrollPosition)
   8:          End If
   9:      End Function
  10:   
  11:      Public Sub RaisePostDataChangedEvent() Implements System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent
  12:          'do nothing
  13:      End Sub
  14:   
  15:      Private Sub ScrollingGridView_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
  16:          If Not Me.Page Is Nothing Then
  17:              'indicates that the control has data to postback
  18:              Me.Page.RegisterRequiresPostBack(Me)
  19:          End If
  20:      End Sub

In order to scroll my new custom GridView, I need to wrap my GridView with a <div> tag.  To do this, I can override the Render method like so:


   1:      Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
   2:          If Not Me.Page Is Nothing Then
   3:              Me.Page.VerifyRenderingInServerForm(Me)
   4:          End If
   5:   
   6:          Me.PrepareControlHierarchy()
   7:   
   8:          If Not Me.DesignMode Then
   9:              If String.IsNullOrEmpty(Me.ClientID) Then
  10:                  Throw New HttpException("ScrollingGridView must be parented!")
  11:              End If
  12:   
  13:              writer.AddAttribute(HtmlTextWriterAttribute.Id, String.Format("{0}_div", Me.ClientID), True)
  14:              writer.AddAttribute("onScroll", "saveScrollPos('" & String.Format("{0}_scroll", ClientID) & "', '" & String.Format("{0}_div", Me.ClientID) & "');")
  15:              writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowY, "auto")
  16:              writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "300px")
  17:              writer.RenderBeginTag(HtmlTextWriterTag.Div)
  18:          End If
  19:   
  20:          Me.RenderContents(writer)
  21:   
  22:          If Not Me.DesignMode Then
  23:              writer.RenderEndTag()
  24:          End If
  25:      End Sub

Notice that when I render my <div> that I also wire in a call to my JavaScript function to save the scroll position "onScroll".  At this point I have completed the PostBack data handling and I have shown you how to wrap a <div> around our GridView so we can scroll and it will call a JavaScript function "onScroll" that will save the position to a hidden field.

The next phase is to emit our JavaScripts.  I always emit JavaScript in the PreRender phase of a control's lifecycle as the ClientID should be set at that point.  We need a function to set the scroll position and a function to save the scroll position.  Here they are:


   1:          'generate & register javascript blocks
   2:          Dim key As String = "ScrollingGridView"
   3:          Dim script As StringBuilder = New StringBuilder()
   4:          With script
   5:              .AppendLine("function saveScrollPos(whereID, whatID) {")
   6:              .AppendLine("  document.getElementById(whereID).value = document.getElementById(whatID).scrollTop;")
   7:              .AppendLine("}")
   8:              .AppendLine("function setScrollPos(whereID, whatID) {")
   9:              .AppendLine("  document.getElementById(whatID).scrollTop = (document.getElementById(whereID).value.length > 0) ? document.getElementById(whereID).value : 0;")
  10:              .AppendLine("}")
  11:          End With
  12:          If Not sm Is Nothing Then
  13:              ScriptManager.RegisterStartupScript(Me.Page, Me.GetType(), key, script.ToString(), True)
  14:          Else
  15:              Me.Page.ClientScript.RegisterStartupScript(Me.GetType(), key, script.ToString(), True)
  16:          End If

Notice we either acquire or save the scroll position to our hidden field.  Now how do we emit a hidden field inside our GridView?  My first attempts were to override the Render of the GridView control and emit hidden field there.  It didn't work.  I ended up registering the hidden field via .NET's scripting objects instead.  Check it out:


   1:          'register our hidden field to save our scroll position
   2:          If Not sm Is Nothing Then
   3:              ScriptManager.RegisterHiddenField(Me.Page, String.Format("{0}_scroll", Me.ClientID), Me.ScrollPosition.ToString())
   4:          Else
   5:              Me.Page.ClientScript.RegisterHiddenField(String.Format("{0}_scroll", Me.ClientID), Me.ScrollPosition.ToString())
   6:          End If

Notice that I assign the scroll position to the hidden field's value.  Last thing we need to do is register a startup script to get the persisted scroll position from our hidden field and scroll our <div>.  If you remember we created a JavaScript function for this (setScrollPosition).


   1:          'generate & register startup javascripts
   2:          key = String.Format("{0}_setScrollPos", Me.ClientID)
   3:          script = New StringBuilder()
   4:          script.AppendLine("setScrollPos('" & String.Format("{0}_scroll", Me.ClientID) & "','" & String.Format("{0}_div", Me.ClientID) & "');")
   5:          If Not sm Is Nothing Then
   6:              ScriptManager.RegisterStartupScript(Me.Page, Me.GetType(), key, script.ToString(), True)
   7:          Else
   8:              Me.Page.ClientScript.RegisterStartupScript(Me.GetType(), key, script.ToString(), True)
   9:          End If

Putting it all Together:

In summary, we persist the scroll position of the div that wraps our GridView via a hidden field which is registered via the Page.ClientScript or ScriptManager object.  We wrap the GridView with a <div> by overriding the GridView's Render method.  The <div> saves the scroll position to the hidden field as the <div> is scrolled.  We acquire the value stored in the hidden field via the LoadPostData function.  Once a PostBack occurs, we set the scroll position of the <div> with a startup script.


ScrollingGridView_Soln.zip (97.19 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

MultiSelect GridView Control

September 17, 2008 14:18 by wjchristenson2

In this post, I am going to create an ASP .NET GridView that supports multiple selection.  This is a common feature that is desired by many web developers.  The new GridView control I will create will support the following: selection of 1 row at a time, toggling of all rows to be selected or deselected at once, coloring of what row(s) are selected, what column index we want our selection checkboxes to be put in, and a way to access what DataKeys are selected on PostBack.  Here is what our final control will look like.

The first step to creating our control is setting up our solution.  You can download the solution in its entirety below.  I have a project for my new control and a web project to test it in.  I am using the Northwind SQL database for my DataSource.  Now we are ready to start programming our MultiSelectGridView control.  We first setup our class-wide variables and properties.


   1:  <ToolboxData("<{0}:MultiSelectGridView runat=server></{0}:MultiSelectGridView>")> _
   2:  Public Class MultiSelectGridView
   3:      Inherits GridView
   4:   
   5:      Private _tfMultiSelect As TemplateField = Nothing
   6:   
   7:  #Region "Public Properties"
   8:      <DefaultValue(False), Category("MultiSelect"), Description("Indicates whether or not multiselection is enabled.")> _
   9:     Public Property EnableMultiSelect() As Boolean
  10:          Get
  11:              If Not ViewState("EnableMultiSelect") Is Nothing Then
  12:                  Return DirectCast(ViewState("EnableMultiSelect"), Boolean)
  13:              Else
  14:                  Return False
  15:              End If
  16:          End Get
  17:          Set(ByVal value As Boolean)
  18:              ViewState("EnableMultiSelect") = value
  19:          End Set
  20:      End Property
  21:   
  22:      <Bindable(True), Category("MultiSelect"), TypeConverter(GetType(WebColorConverter)), Description("Specifies the background color of a selected row.")> _
  23:      Public Property MultiSelectColor() As Color
  24:          Get
  25:              If ViewState("MultiSelectColor") Is Nothing Then
  26:                  ViewState("MultiSelectColor") = System.Drawing.ColorTranslator.FromHtml("#FFCC99")
  27:              End If
  28:              Return DirectCast(ViewState("MultiSelectColor"), Color)
  29:          End Get
  30:   
  31:          Set(ByVal value As Color)
  32:              ViewState("MultiSelectColor") = value
  33:          End Set
  34:      End Property
  35:   
  36:      <Bindable(True), Category("MultiSelect"), Description("Specifies the where the multiselection column should be placed.")> _
  37:      Public Property MultiSelectColumnIndex() As Integer
  38:          Get
  39:              If Not ViewState("MultiSelectColumnIndex") Is Nothing Then
  40:                  Return DirectCast(ViewState("MultiSelectColumnIndex"), Integer)
  41:              Else
  42:                  Return -1
  43:              End If
  44:          End Get
  45:          Set(ByVal value As Integer)
  46:              ViewState("MultiSelectColumnIndex") = value
  47:          End Set
  48:      End Property
  49:   
  50:      Public ReadOnly Property SelectedDataKeys() As DataKeyArray
  51:          Get
  52:              Return GetSelectedDataKeys()
  53:          End Get
  54:      End Property
  55:  #End Region

We want to extend the GridView control so we first inherit from it.  I've defined 4 properties.  First is to toggle whether or not we want to enable multiselection or not.  The second is used to define what color we want to use to delineate the selected state of a row.  The third property is used to allow the developer to specify where they wish to put the multiple selection column.  The final property (SelectedDataKeys) is an array of selected DataKeys that can be accessed programmatically when a PostBack occurs.

A problem I had at first, was how to add columns in a GridView control.  At first, I tried to add them in the Init and the CreateChildControls and then work with them later on in the control lifecycle.  This *can* work, however you'll soon find out that if you try and access the Columns property, you may find that there are no other columns outside the one you added if you set AutoGenerateColumns = True.  For instance, if you are trying to do a GridView.Columns.Count, you may have more columns than what the property returns.  The Columns property only returns what columns are defined.  So if you are trying to programmatically insert columns or move them around, you may run into issues if you are trying to do that in the Init or PreRender or Load events of your control lifecycle.  The key to adding our column is to Override the CreateColumns function.  We want to let the GridView create its normal columns and then we want to insert our multiselect column wherever the column index tell us to.


   1:      Protected Overrides Function CreateColumns(ByVal dataSource As System.Web.UI.WebControls.PagedDataSource, ByVal useDataSource As Boolean) As System.Collections.ICollection
   2:          'let the GridView create the default set of columns
   3:          Dim columnList As ICollection = MyBase.CreateColumns(dataSource, useDataSource)
   4:          Dim extendedColumnList As ArrayList = New ArrayList(columnList)
   5:   
   6:          If Me.EnableMultiSelect Then
   7:              'add our multi-select checkbox column
   8:              _tfMultiSelect = New TemplateField()
   9:              With _tfMultiSelect
  10:                  .HeaderTemplate = New MultiSelectColumnTemplate(DataControlRowType.Header)
  11:                  .ItemTemplate = New MultiSelectColumnTemplate(DataControlRowType.DataRow)
  12:              End With
  13:   
  14:              If Me.MultiSelectColumnIndex < 0 Or Me.MultiSelectColumnIndex > extendedColumnList.Count Then
  15:                  extendedColumnList.Add(_tfMultiSelect)
  16:              Else
  17:                  extendedColumnList.Insert(Me.MultiSelectColumnIndex, _tfMultiSelect)
  18:              End If
  19:          End If
  20:   
  21:          Return extendedColumnList
  22:      End Function

Above is the overridden CreateColumns function.  We first see if multiselection is enabled.  If it is, we insert our multiselect column (template field) into the collection of GridView columns.  We do some logic here to ensure the column is inserted in the appropriate position.  This gets our multiselect column into the GridView, however now we need to wire in some Javascript.


   1:      Private Sub MultiSelectGridView_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
   2:          If Me.EnableMultiSelect Then
   3:              'declare variables used to initialize onclick client scripts
   4:              Dim cbxMultiSelect As CheckBox = Nothing
   5:              Dim cbxSingleSelect As CheckBox = Nothing
   6:              Dim hidCheckBoxIDs As HiddenField = Nothing
   7:              Dim checkboxIDs As ArrayList = New ArrayList()
   8:              Dim selectColor As String = System.Drawing.ColorTranslator.ToHtml(Me.MultiSelectColor)
   9:   
  10:              'set header checkbox onclick
  11:              If Me.ShowHeader And Not Me.HeaderRow Is Nothing Then
  12:                  cbxMultiSelect = CType(Me.HeaderRow.FindControl("cbxMultiSelect"), CheckBox)
  13:                  hidCheckBoxIDs = CType(Me.HeaderRow.FindControl("hidCheckBoxIDs"), HiddenField)
  14:                  cbxMultiSelect.Attributes.Add("onClick", "toggleCheckBox(this, '" & cbxMultiSelect.ClientID & "', '" & hidCheckBoxIDs.ClientID & "', '" & selectColor & "');")
  15:              End If
  16:   
  17:              'set data row checkbox onclick
  18:              For Each gvr As GridViewRow In Me.Rows
  19:                  cbxSingleSelect = CType(gvr.FindControl("cbxMultiSelect"), CheckBox)
  20:   
  21:                  If cbxMultiSelect Is Nothing Or hidCheckBoxIDs Is Nothing Then
  22:                      cbxSingleSelect.Attributes.Add("onClick", "toggleCheckBox(this, '', '', '" & selectColor & "');")
  23:                  Else
  24:                      cbxSingleSelect.Attributes.Add("onClick", "toggleCheckBox(this, '" & cbxMultiSelect.ClientID & "', '" & hidCheckBoxIDs.ClientID & "', '" & selectColor & "');")
  25:                  End If
  26:   
  27:                  If cbxSingleSelect.Checked Then
  28:                      gvr.Style.Add("background-color", selectColor)
  29:                  Else
  30:                      gvr.Style.Add("background-color", "")
  31:                  End If
  32:                  checkboxIDs.Add(cbxSingleSelect.ClientID)
  33:              Next
  34:   
  35:              'set hidden field value w/ checkbox client ids
  36:              If Not hidCheckBoxIDs Is Nothing Then
  37:                  hidCheckBoxIDs.Value = Join(checkboxIDs.ToArray(), ",")
  38:              End If
  39:   
  40:              RegisterClientScriptBlock()
  41:          End If
  42:      End Sub

I wire in client-side script in the control's PreRender phase of its lifecycle.  Basically when a checkbox is clicked, I call a Javascipt function to do perform background color changes and toggle the check or uncheck of other checkboxes.  So if I check the toggle all checkbox, all rows will be selected.  If I uncheck the toggle all checkbox, all rows will be deselected.  If I select each row manually 1 at a time and all are checked, I check the "toggle all" checkbox with Javascript and vice versa.  Here's the Javascript code that performs this logic.


   1:  var lastColorUsed;
   2:  function toggleCheckBox(thisCheckBox, multiSelectID, checkBoxIDs, selectedColor) {
   3:    var arrayIDs;
   4:    if (checkBoxIDs.length > 0) 
   5:      arrayIDs = document.getElementById(checkBoxIDs).value.split(',');
   6:    var cbxMultiSelect = document.getElementById(multiSelectID);
   7:    if (thisCheckBox == cbxMultiSelect) {
   8:      var cbx;
   9:      for (var i = 0; i < arrayIDs.length; i++) {
  10:        cbx = document.getElementById(arrayIDs[i]);
  11:        if (cbx) {
  12:          if (!cbx.disabled) {
  13:              cbx.checked = thisCheckBox.checked;
  14:              if (cbx.checked) {
  15:                cbx.parentNode.parentNode.parentNode.style.backgroundColor = selectedColor;
  16:                lastColorUsed = selectedColor;
  17:              } else {
  18:                cbx.parentNode.parentNode.parentNode.style.backgroundColor = '';
  19:                lastColorUsed = '';
  20:              }
  21:          }  
  22:        }
  23:      }
  24:    } else {
  25:      if (thisCheckBox.checked) {
  26:        thisCheckBox.parentNode.parentNode.parentNode.style.backgroundColor = selectedColor;
  27:        lastColorUsed = selectedColor;
  28:        
  29:        if (cbxMultiSelect) {
  30:          var allChecked = true;
  31:          for (var i = 0; i < arrayIDs.length; i++) {
  32:            allChecked = document.getElementById(arrayIDs[i]).checked;
  33:            if (!(allChecked))
  34:              break;
  35:          }
  36:          if (allChecked)
  37:            cbxMultiSelect.checked = true;
  38:        }
  39:      } else {
  40:        thisCheckBox.parentNode.parentNode.parentNode.style.backgroundColor = '';
  41:        lastColorUsed = '';
  42:        if (cbxMultiSelect) 
  43:          cbxMultiSelect.checked = false;
  44:      }
  45:    }
  46:  }

Now we need to acquire what rows are selected when the page performs a PostBack.  I've created a public property that returns an array of DataKeys that were selected.  The DataKeys can be 1 or a combination of values to uniquely identify the row.  Make sure you define what column(s) or object(s) of each row make up the DataKey by setting the DataKeyNames property of the GridView.  The SelectedDataKeys property calls our function below.


   1:      Private Function GetSelectedDataKeys() As DataKeyArray
   2:          Dim keys As ArrayList = New ArrayList()
   3:   
   4:          If Me.EnableMultiSelect Then
   5:              Dim cbxMultiSelect As CheckBox = Nothing
   6:   
   7:              If Me.DataKeys.Count > 0 Then
   8:                  For Each gvr As GridViewRow In Me.Rows
   9:                      cbxMultiSelect = CType(gvr.FindControl("cbxMultiSelect"), CheckBox)
  10:   
  11:                      If Not cbxMultiSelect Is Nothing AndAlso cbxMultiSelect.Checked Then
  12:                          keys.Add(Me.DataKeys(gvr.RowIndex))
  13:                      End If
  14:                  Next
  15:              End If
  16:          End If
  17:   
  18:          Return New DataKeyArray(keys)
  19:      End Function

What we do here is loop through each row of the GridView and get a handle on the multiselect checkbox.  If it is checked, we get the associated DataKeys for the row and add it to our ArrayList.  We return all selected DataKeys when finished looping through all rows of the GridView.  Now we are ready to use our control.


   1:      <cc1:MultiSelectGridView 
   2:        ID="MultiSelectGridView1" 
   3:        runat="server" 
   4:        DataSourceID="SqlDataSource1" 
   5:        DataKeyNames="RegionID" 
   6:        EnableMultiSelect="True" 
   7:        MultiSelectColor="#9EC630" 
   8:        MultiSelectColumnIndex="0" />

Here is a simple use of our MultiSelectGridView control.  Remember I am using the Northwind database via my SqlDataSource1.  What's important here is the MultiSelect properties.  I enable multiselection, set the selected color, and then place the multiselect column on the far left of my GridView.  After I've selected what rows I want, I click a button and display the SelectedDataKeys on my page.  Here is how I use the SelectedDataKeys property programmatically.


   1:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
   2:          Dim selectedKeys As StringBuilder = New StringBuilder()
   3:          For Each key As DataKey In Me.MultiSelectGridView1.SelectedDataKeys
   4:              selectedKeys.Append(", " & key(0).ToString())
   5:          Next
   6:   
   7:          If selectedKeys.Length > 0 Then selectedKeys.Remove(0, 2)
   8:          lblSelectedItems.Text = selectedKeys.ToString()
   9:      End Sub

That's all there is to it.  You now have a GridView that allows you to multiselect rows!


GridViewMultiSelect_Soln.zip (111.25 kb)

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Bind a Collection to a GridView

August 8, 2008 10:41 by wjchristenson2

Over the past few months I've had some requests to show how you can bind a custom object collection to a GridView.  When looking deeper into the collections, I noticed that the developers were working way to hard to create their collection class.   The collections were inheriting from System.Collections.Specialized.NameObjectCollectionBase and implementing IEnumerator, IEnumerable, & IUpdatable, etc to accomplish some simple tasks (ie: add, remove, for each loops, etc).  When implementing these interfaces, the developers were manually having to wire everything in.  On top of this, they were having problems binding to .NET controls.  Another quick point to make here is that the specialized collection does type casting at run time.

What I proposed was to inherit from System.Collections.Generic.List instead.  What this does is allow them to do their basic add, remove, sort, and manipulate their lists.  It also is strongly typed and can be accessed by index.  Generics provide better type safety and performance than non-generic collections (ie NameObjectCollectionBase mentioned above).

I'm going to create a simple vehicle collection and bind a GridView to it using the Generic.List.


   1:  Public Class Vehicle
   2:      Private _Make As String = String.Empty
   3:      Private _Model As String = String.Empty
   4:   
   5:      Public Property Make() As String
   6:          Get
   7:              Return _Make
   8:          End Get
   9:          Set(ByVal value As String)
  10:              _Make = value
  11:          End Set
  12:      End Property
  13:   
  14:      Public Property Model() As String
  15:          Get
  16:              Return _Model
  17:          End Get
  18:          Set(ByVal value As String)
  19:              _Model = value
  20:          End Set
  21:      End Property
  22:   
  23:      Public Sub New()
  24:   
  25:      End Sub
  26:   
  27:      Public Sub New(ByVal make As String, ByVal model As String)
  28:          _Make = make
  29:          _Model = model
  30:      End Sub
  31:  End Class

Here is the vehicle class (object) that we'll reference in our collection.  Now for the collection.


   1:  Imports System.Collections.Generic
   2:   
   3:  Public Class Vehicles
   4:      Inherits List(Of Vehicle)
   5:   
   6:  #Region "Constructors"
   7:      Public Sub New()
   8:          MyBase.New()
   9:      End Sub
  10:   
  11:      Public Sub New(ByVal capacity As Integer)
  12:          MyBase.New(capacity)
  13:      End Sub
  14:   
  15:      Public Sub New(ByVal collection As IEnumerable(Of Vehicle))
  16:          MyBase.New(collection)
  17:      End Sub
  18:  #End Region
  19:   
  20:  End Class

You'll notice how we inherit from System.Collections.Generic.List and cast the collection as a type of Vehicle.  It's as simple as that.  I added 3 constructors for your own reference.  Now all we have to do is add some data to the collection and bind a GridView to it.


   1:  Imports CollectionDataBinding.BLL
   2:   
   3:  Partial Public Class _Default
   4:      Inherits System.Web.UI.Page
   5:   
   6:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   7:          If Not Page.IsPostBack() Then
   8:              GridView1_DataBind()
   9:          End If
  10:      End Sub
  11:   
  12:      Private Sub GridView1_DataBind()
  13:          Dim myVehicles As Vehicles = New Vehicles()
  14:          myVehicles.Add(New Vehicle("Ford", "Mustang"))
  15:          myVehicles.Add(New Vehicle("Pontiac", "Grand AM"))
  16:   
  17:          Me.GridView1.DataSource = myVehicles
  18:          Me.GridView1.DataBind()
  19:      End Sub
  20:  End Class

On page load, I bind the GridView if it is not a postback.  I create a new instance of the Vehicles collection and add 2 vehicles to it.  I then simply set the data source of the GridView to the Vehicle collection and call DataBind().  Now, each public property will be mapped and shown in your GridView if you have AutoGenerateColumns="true" set.  If you don't do this, you'd approach the custom column mappings the same way as with a DataTable, DataView, etc (TemplateField, Eval expressions).

CollectionDataBinding_Soln.zip (89.23 kb)

>>>>> Edit 8/21/2008 <<<<<

I found another article that relates to this post.  It's a great reference as to the benefits of using generic collections.  The article lists the following reasons why you should use generic collections.

  1. Readability and simplicity of your code. Let’s take the case of getting the first string in a list of strings. With generics you would declare List<T> myList = new List<T>(); and then say string firstString = myList[0] just like working with arrays. This syntax is much more simple and readable than what you used to have to write which was ArrayList myList = new ArrayList(); and then string firstString = myList[0] as string;
  2. Performance. Every time you add a value type to a non-generic collection you have to box it to make it an object and every time you retrieve a value type from a non-generic collection you have to unbox it back from object. Even with reference type you still need to cast back to the correct type when retrieving items.
  3. Working against new smaller Framework SKUs. In Silverlight we decided to remove all the concrete non-generic collections completely from the codebase. This is mostly because the Silverlight core managed libraries is set to be the smallest useful set of classes. It’s also possible that other future small frameworks will not have the non-generic collections in store. If you ever plan to write code for those frameworks — you should convert it to use the generic collections.
  4. Better type-safe libraries. If you are developing libraries to be consumed by 3rd parties you should most definitely use generic collections when possible. This would allow consumer of your libraries to quickly figure out what’s expected to be stored in the collections instead of having to guess or figure out from documentation.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Custom GridView Paging

August 7, 2008 07:00 by wjchristenson2

One of the first topics I searched for when working with the ASP.NET GridView control was how to page data with it.  I quickly discovered that the GridView control has built-in paging capabilities.  However, I wanted to customize my GridView pager.  The built-in pager just didn't cut it for me.  Not only did I want to customize the pager, I didn't want to have to define my pager template and wire everything in each time I used my GridView control because it would be used 100's of times.

In order to encapsulate the paging features I desire, I am going to make my own custom GridView control.  In my pager I want the following:
1) A "Page X of Y" caption
2) Basic first, previous, next, and last navigation
3) A page selector drop down list so the user can jump to a page quickly

If you've used the GridView before, you may have utilized the built-in paging modes.  I don't necessarily want to remove that functionality.  I want to add to it.  So what I am going to do is create a public property called "PagerType" where we can specify if we want to use the GridView's built-in paging or if we want to use our custom paging.


   1:  Public Class PagingGridView
   2:      Inherits GridView
   3:   
   4:      Public Enum PagerTypes
   5:          Regular = 0
   6:          Custom = 1
   7:      End Enum
   8:   
   9:      <DefaultValue(PagerTypes.Custom), Category("Paging"), Description("Indicates whether to use the built-in custom pager or not.")> _
  10:      Public Property PagerType() As PagerTypes
  11:          Get
  12:              If Not ViewState("PagerType") Is Nothing Then
  13:                  Return DirectCast(ViewState("PagerType"), PagerTypes)
  14:              Else
  15:                  Return PagerTypes.Custom
  16:              End If
  17:          End Get
  18:          Set(ByVal value As PagerTypes)
  19:              ViewState("PagerType") = value
  20:          End Set
  21:      End Property

From this code snippet we are inheriting from the GridView control, defining our 2 paging types (regular & custom), and we define our pager type property.  In order for our pager to be initialized, we need to override the InitializePager method.  What this method does is initialize the pager row when paging is enabled.  We need to determine if we want to allow the GridView to use its built-in pager or if we need to use our custom pager.  A simple select case statement will do the trick here.


   1:      Protected Overrides Sub InitializePager(ByVal row As System.Web.UI.WebControls.GridViewRow, ByVal columnSpan As Integer, ByVal pagedDataSource As System.Web.UI.WebControls.PagedDataSource)
   2:          Select Case Me.PagerType
   3:              Case PagerTypes.Custom
   4:                  InitCustomPager(row, columnSpan, pagedDataSource)
   5:              Case Else
   6:                  MyBase.InitializePager(row, columnSpan, pagedDataSource)
   7:          End Select
   8:      End Sub

You'll notice that we have an InitCustomPager method we call to initialize our pager controls.  I'm not going to post the code snippet for this.  You can download the code and sift through it if you want.  The things to note is the wiring of events.  Our first, previous, next, and last image buttons throw a Command event and the drop down list throws a SelectedIndexChanged event.  Our PagingGridView control will handle these events and fire a the OnPageIndexChanging event which can be handled by the page.


   1:      Protected Sub ddlPages_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
   2:          Dim newPageIndex As Integer = CType(sender, DropDownList).SelectedIndex
   3:          OnPageIndexChanging(New GridViewPageEventArgs(newPageIndex))
   4:      End Sub
   5:   
   6:      Protected Sub PagerCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.CommandEventArgs)
   7:          Dim curPageIndex As Integer = Me.PageIndex
   8:          Dim newPageIndex As Integer = 0
   9:   
  10:          Select Case e.CommandName
  11:              Case "First"
  12:                  newPageIndex = 0
  13:              Case "Previous"
  14:                  If curPageIndex > 0 Then
  15:                      newPageIndex = curPageIndex - 1
  16:                  End If
  17:              Case "Next"
  18:                  If Not curPageIndex = Me.PageCount Then
  19:                      newPageIndex = curPageIndex + 1
  20:                  End If
  21:              Case "Last"
  22:                  newPageIndex = Me.PageCount
  23:          End Select
  24:   
  25:          OnPageIndexChanging(New GridViewPageEventArgs(newPageIndex))
  26:      End Sub

Here are the even handlers for my image buttons and the page drop down list.  You'll see that in each handler, I raise the OnPageIndexChanging event passing in the new page index to navigate to.

So let's use our new PagingGridView control.  I'm going to dynamically create a DataTable object on the fly to bind my GridView to for this example.  I also created some css and images to make the GridView look good.  You can find them in the download package if you need them.


   1:        <cc1:PagingGridView ID="PagingGridView1" runat="server" 
   2:          CssClass="paging_gridview" PageSize="5" AllowPaging="true" Width="400">
   3:          <RowStyle CssClass="paging_gridview_itm" />
   4:          <PagerStyle CssClass="paging_gridview_pgr" />
   5:          <HeaderStyle CssClass="paging_gridview_hdr" />
   6:          <AlternatingRowStyle CssClass="paging_gridview_aitm" />
   7:        </cc1:PagingGridView>

Here is the HTML markup for our new PagingGridView.  Note that we enabled paging and set the page size to 5.  We did not specify the pager type to custom because the default value is set to our custom pager.  If we wanted to revert to the GridViews built-in paging, we would set PagerType="Regular".  The only thing left to do is handle the OnPageIndexChanging to rebind our GridView to the new page via our code behind and that's it.


   1:      Private Sub PagingGridView1_PageIndexChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewPageEventArgs) Handles PagingGridView1.PageIndexChanging
   2:          Me.PagingGridView1.PageIndex = e.NewPageIndex
   3:          PagingGridView1_DataBind()
   4:      End Sub

I hope you enjoyed this post.  It definitely has improved developer productivity without having to define a pager template and wire in the controls everytime I use the GridView.  The final screenshot of the PagingGridView is below.

GridViewPaging_Soln.zip (108.06 kb)


Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Row Clickable GridView

June 2, 2008 10:25 by wjchristenson2

In this post, I am going to show how we can make a GridView row clickable.  What I mean by this is when the user clicks on a row, it will fire a postback to the server that the programmer can handle.

There are endless uses for this functionality.  You may want to toggle a row into edit mode or display/bind another GridView on your page when a user clicks on a row.  I actually use this feature a lot.  In my opinion it is easier on the user if they can simply click on a row to perform a desired action.

  

RowClickGridView Class/Control:

In order to make rows clickable, we are going to create a custom GridView control.  We are going to inherit from System.Web.UI.WebControls.GridView because we want all the functionality of the basic GridView plus our own enhancements.  We will name the control "RowClickGridView".

 

   1:      'key that is used for our RowClicked event handler
   2:      Private Shared ReadOnly RowClickedEventKey As Object = New Object()
   3:   
   4:      <DefaultValue(False), Description("Indicates whether or not rows are clickable.")> _
   5:      Public Property RowClick_Enabled() As Boolean
   6:          Get
   7:              If Not IsNothing(ViewState("RowClick_Enabled")) Then
   8:                  Return DirectCast(ViewState("RowClick_Enabled"), Boolean)
   9:              Else
  10:                  Return False
  11:              End If
  12:          End Get
  13:          Set(ByVal value As Boolean)
  14:              ViewState("RowClick_Enabled") = value
  15:          End Set
  16:      End Property
  17:   
  18:      <DefaultValue(""), Description("Specifies the ToolTip for clickable rows."), Category("Appearance")> _
  19:      Public Property RowClick_ToolTip() As String
  20:          Get
  21:              If Not IsNothing(ViewState("RowClick_ToolTip")) Then
  22:                  Return ViewState("RowClick_ToolTip").ToString()
  23:              Else
  24:                  Return String.Empty
  25:              End If
  26:          End Get
  27:          Set(ByVal value As String)
  28:              ViewState("RowClick_ToolTip") = value
  29:          End Set
  30:      End Property

 

Here we create 2 properties and 1 class variable.  The RowClickedEventKey in line 2 is later used for our event handling.  We have 2 properties.  I want the RowClicked functionality to not trigger postbacks unless I enable it.  I would also like to be able to set ToolTips on the row if it is clickable.  We persist the properties via the ViewState so that if the default values get changed across postbacks programmatically, our control will remember them.

 

   1:  Protected Overrides Sub PrepareControlHierarchy()
   2:          MyBase.PrepareControlHierarchy()
   3:   
   4:          'wire RowClick event & RowClick tooltip for rows if RowClicked is enabled
   5:          If Me.RowClick_Enabled Then
   6:              Dim argsData As String = String.Empty
   7:              For Each row As GridViewRow In Me.Rows
   8:                  'if row is a data row & it's not the edit row
   9:                  If row.RowType = DataControlRowType.DataRow And Me.EditIndex <> row.RowIndex Then
  10:                      'create argument - pass the row index
  11:                      argsData = "rc" & row.RowIndex.ToString()
  12:   
  13:                      'wire javascript to the row for the RowClicked event
  14:                      row.Attributes.Add("onClick", Me.Page.ClientScript.GetPostBackEventReference(Me, argsData))
  15:   
  16:                      'if the RowClick ToolTip is supplied
  17:                      If Not String.IsNullOrEmpty(Me.RowClick_ToolTip) Then
  18:                          'apply tooltip to row
  19:                          row.ToolTip = Me.RowClick_ToolTip
  20:                      End If
  21:                  End If
  22:              Next
  23:          End If
  24:      End Sub

 

The PrepareControlHierarchy() method is new in .NET 2.0.  What this method does is basically establishes the control hierarchy.  So before we tap into the GridView's control hierarchy, we call the MyBase.PrepareControlHierarchy().  We only want to make rows clickable if we've enabled it, so we check that irst on line 5.  After the hierarchy is ready, we loop through the rows of the GridView and acquire the rows we wish to make clickable (line 9).  Once we have a row we wish to make clickable, we form the argument data and wire the row to fire a postback when clicked.  We also add our row click tooltip if supplied.

 

   1:      Protected Overrides Sub RaisePostBackEvent(ByVal eventArgument As String)
   2:          'if a row clicked event
   3:          If eventArgument.StartsWith("rc") Then
   4:              'get the index of the row that raised the event
   5:              Dim index As Integer = Int32.Parse(eventArgument.Substring(2))
   6:   
   7:              'create our instance of GridViewRowClickedEventArgs passing the GridViewRow clicked
   8:              Dim args As GridViewRowClickedEventArgs = New GridViewRowClickedEventArgs(Me.Rows(index))
   9:   
  10:              'raise the RowClicked event
  11:              OnRowClicked(args)
  12:          Else
  13:              'raise any other event (not row clicked)
  14:              MyBase.RaisePostBackEvent(eventArgument)
  15:          End If
  16:      End Sub

 

In order to raise our custom event if the row clicked, we need to override the GridView's RaisePostBackEvent method.  This method raises the GridView's events when it posts back to the server.  We check to see if the row clicked event was the trigger in line 3.  We then acquire the row index that fired the event by stripping it out of the eventArgument string in line 5.  We want to be able to tap into the entire row when the event is fired later, so we are going to create a custom event argument class (posted below).  We instantiate the GridViewRowClickedEventArgs class by passing in the GridViewRow. and then call our OnRowClicked() method to raise the event.

 

   1:      Protected Overridable Sub OnRowClicked(ByVal e As GridViewRowClickedEventArgs)
   2:          'raise the RowClicked event
   3:          RaiseEvent RowClicked(Me, e)
   4:      End Sub

 

Here is the OnRowClicked() helper method mentioned above.  We simply raised the RowClicked event.

 

   1:      'setup our EventHandler for RowClicked
   2:      Public Custom Event RowClicked As EventHandler(Of GridViewRowClickedEventArgs)
   3:          AddHandler(ByVal value As EventHandler(Of GridViewRowClickedEventArgs))
   4:              Events.AddHandler(RowClickedEventKey, value)
   5:          End AddHandler
   6:   
   7:          RemoveHandler(ByVal value As EventHandler(Of GridViewRowClickedEventArgs))
   8:              Events.RemoveHandler(RowClickedEventKey, value)
   9:          End RemoveHandler
  10:   
  11:          RaiseEvent(ByVal sender As Object, ByVal e As GridViewRowClickedEventArgs)
  12:              Dim ev As EventHandler(Of GridViewRowClickedEventArgs) = TryCast(Events(RowClickedEventKey), EventHandler(Of GridViewRowClickedEventArgs))
  13:              If Not IsNothing(ev) Then
  14:                  ev(sender, e)
  15:              End If
  16:          End RaiseEvent
  17:      End Event

 

This is the EventHandler for our RowClicked event.  You can see the add, remove, and raise event implementations above.  The important thing to note here is the use of our custom event argument class (GridViewRowClickedEventArgs).

  

GridViewRowClickedEventArgs Class:

In our RowClickGridView control, you noticed we used a custom event argument class for our event handler.  Here is the code:

 

   1:  'our custom event argument class for the RowClicked event
   2:  Public Class GridViewRowClickedEventArgs
   3:      Inherits EventArgs
   4:   
   5:      Private _row As GridViewRow
   6:   
   7:      Public ReadOnly Property Row() As GridViewRow
   8:          Get
   9:              Return _row
  10:          End Get
  11:      End Property
  12:   
  13:      Public Sub New(ByVal row As GridViewRow)
  14:          _row = row
  15:      End Sub
  16:  End Class

 

You notice that we inherit from EventArgs and add a property to tap into the GridViewRow that was clicked.  The class requires the row clicked in its constructor.

 

RowClickGridView In Action:

Now that we have our control developed, we can use it.  We first add it to our aspx page and setup our 2 properties:

 

   1:          <p>Row Index Clicked: <asp:Label ID="Label1" runat="server" /></p>
   2:          <cc1:RowClickGridView ID="RowClickGridView1" runat="server" 
   3:              AutoGenerateColumns="False" DataKeyNames="CategoryID" 
   4:              DataSourceID="SqlDataSource1" RowClick_Enabled="true" RowClick_ToolTip="Row Click ToolTip">
   5:              <Columns>
   6:                  <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
   7:                      InsertVisible="False" ReadOnly="True" SortExpression="CategoryID" />
   8:                  <asp:BoundField DataField="CategoryName" HeaderText="CategoryName" 
   9:                      SortExpression="CategoryName" />
  10:                  <asp:BoundField DataField="Description" HeaderText="Description" 
  11:                      SortExpression="Description" />
  12:              </Columns>
  13:          </cc1:RowClickGridView>

 

Notice we enable row clicking and also set the tooltip we want on clickable rows.   To see what index we clicked, we want to capture the event and set a label control in the code behind.

 

   1:      Protected Sub RowClickGridView1_RowClicked(ByVal sender As Object, ByVal e As GridViewRowClickedEventArgs) Handles RowClickGridView1.RowClicked
   2:          Me.Label1.Text = e.Row.RowIndex.ToString()
   3:      End Sub

 

And that's it!  We've created a custom GridView control that has row click functionality.  I've attached the code for your pleasure.  Enjoy!

Examples_Soln.zip (102.13 kb)


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Highlight GridView Row OnMouseOver

May 31, 2008 14:58 by wjchristenson2

A common request I see for the GridView control is to highlight the row when the user hovers over it.  The GridView does not come pre-packaged with this functionality and it is up to us to make it happen.  I am going to assume that we already have a GridView present on our web page and we have data binding to it.

What we basically want to do is add some Javascript to the row so when the user hovers over the row it changes colors.  Here's the code:

 

   1:      Private Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
   2:          'ensure the row is a datarow and is not the edit row
   3:          If e.Row.RowType = DataControlRowType.DataRow And e.Row.RowIndex <> Me.GridView1.EditIndex Then
   4:              e.Row.Attributes.Add("onmouseover", "changeBackColor(this, true, ""#DFE8F6"");")
   5:              e.Row.Attributes.Add("onmouseout", "changeBackColor(this, false, """");")
   6:          End If
   7:      End Sub

 

First we handle the RowDataBound event of the GridView.  The RowDataBound event fires when a data row is bound to data.  At this point we can tap into every row that is created by the GridView control.  However, we only desire to highlight data rows that are not in edit mode.  Due to this, in line 3 we filter out rows we don't want.  In lines 4 & 5 we add attributes to the row to call our changeBackColor() javascript function. 

Here is our javascript on the .aspx page:

 

   1:      var lastColorUsed;
   2:      function changeBackColor(row, highlight, color) {
   3:          if (highlight) {
   4:              lastColorUsed = row.style.backgroundColor;
   5:              row.style.backgroundColor = color;
   6:          } else {
   7:              row.style.backgroundColor = lastColorUsed;
   8:          }
   9:      }

 

In line 1 we declare a variable to remember what the last color was.  The reason we do this is so that when the mouse pointer exits the scope of the row, we want to change the background color of the row to what it was previously.  So, if we are highlighting, we save the current color of the row and change the background color of the row to the highlight color.  When the mouse exits the row, we change the background color of the previously highlighted row back to what it was originally.

Examples_Soln.zip (70.47 kb)


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

ASP.NET GridView Introduction

May 31, 2008 14:20 by wjchristenson2

In ASP.NET 1.0/1.1 Microsoft introduced the DataGrid control which was used to display a collection of data in tabular form.  You could use the control for data sorting, paging, and inline editing.  Although I enjoyed the control very much, it required a lot of code for simple tasks.

With ASP.NET 2.0, Microsoft has taken the DataGrid control and improved upon it.  The new DataGrid control is now taken on the form of the GridView control.  The GridView control can essentially display data from a data source without having to write one line of code.

 If you are new or even a veteran to ASP.NET development, you'll definitely be working with the GridView control.  Therefore I've added a "GridView" category to the site as there are many enhancements and techniques that I've come across that I'd like to share with all of you.


Currently rated 3.0 by 1 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5