5,550,131 members and growing! (19,701 online)
Email Password   helpLost your password?
Web Development » Ajax and Atlas » Controls     Intermediate License: The Code Project Open License (CPOL)

Paging, Selecting, Deleting, and Editing in the ASP.NET 2.0 GridView Control with Keyboard Shortcuts

By Rolf Cerff

An ASP.NET 2.0 AJAX Extender to enhance the ASP.NET GridView to page, select, delete, and edit rows with keyboard shortcuts.
C# (C# 2.0, C#), Javascript, CSS, XHTML, HTML, .NET (.NET, .NET 2.0), ASP.NET, Visual Studio (VS2005, Visual Studio), ADO.NET, Ajax, Dev

Posted: 4 Jul 2008
Updated: 4 Jul 2008
Views: 10,823
Bookmarked: 41 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
15 votes for this Article.
Popularity: 5.36 Rating: 4.56 out of 5
1 vote, 6.7%
1
1 vote, 6.7%
2
0 votes, 0.0%
3
1 vote, 6.7%
4
12 votes, 80.0%
5

GridViewKeyboardExtender_src

Introduction

The ASP.NET 2.0 framework comes with the GridView, a feature-rich ASP.NET server control to display, page through, and edit data on a webpage. Paging, selecting, and editing of the GridView is achieved by declaratively adding properties to the markup of the GridView. This adds link buttons to the data rows of the GridView. You have to click on one of the link buttons to delete or edit a row. One feature I missed is the ability to use these features via keyboard shortcuts.

To achieve that, I developed an ASP.NET 2.0 AJAX Extensions Extender Control for use with ASP.NET 2.0 GridView on ASP.NET 2.0 AJAX enabled web pages.

What You Can Do with the Extender and How to Use It

You can use this extender control in existing ASP.NET 2.0 AJAX Extension application web pages which contain the ASP.NET 2.0 GridView server control.

In the current version, the extender control must be placed at the same level in the control tree as the GridView it belongs to.

In the current release, the extender provides the following navigation and editing features, and the following default keyboard shortcut scheme:

Feature Feature Description Default Shortcut Key
Paging If paging is enabled for the GridView, you have shortcuts to navigate to the next, previous, first, and last pages. You can navigate to first and last page by keyboard even if the link buttons for the first and last page aren't currently visible.
  • Next page: Right arrow key
  • Previous page: Left arrow key
  • First Page: CTRL + Pos1
  • Last Page: CTRL + End
Selecting If selecting is enabled and a row is preselected, you have shortcuts to navigate to the next and previous row.
  • Select next row: Down key
  • Select previous row: Up key
Edit If editing is enabled and a row is preselected, you have shortcuts to all editing features.
  • Switch selected row to edit mode: CTRL + m key
  • Cancel editing: CTRL + d key
  • Update changes: CTRL + u key
Delete If deleting is enabled and a row is preselected, you have shortcuts to delete that row.
  • Delete row: Del key

You don't need to display the corresponding link buttons in order to select, delete, and edit a data row by keyboard shortcuts.

This default keyboard scheme can easily be changed by changing the default key values in the extender class.

To configure the keyboard scheme for a GridView, you can set the corresponding public key values XXXKey to a value of the Keys enumeration.

For paging, the extender works with both the numeric and the next/previous pager scheme. It works for all four existing GridView.PagerSettings.Mode values. If the grid doesn't support paging at all, nothing happens when pressing the keyboard shortcuts.

Background

The solution is based on an ASP.NET 2.0 AJAX Extender WebControl. This makes it easy to change the keyboard scheme on server-side and minimize work in the client-side JavaScript.

The extender control works by simulating the corresponding postback commands of the GridView's link buttons. So, the keyboard shortcuts are compatible to the default client/server based GridView navigating and editing scheme, and the ViewState of the GridView remains in sync.

The article will not cover the general steps in developing Extenders and Behaviour controls, but will describe the solution specific details. For a comprehensive documentation on how to develop an ASP.NET 2.0 AJAX Extensions Extender Control, read the documentation on AJAX.ASP.NET, and particulary the article about developing an Extender control.

In a few words, to develop an Extender control which extends the client behavior of an ASP.NET server control on the client side, you must create an ASP.NET 2.0 AJAX Extender Control for the server side, and a corresponding JavaScript class, derived from the "Sys.UI.Behavior" class, for client side handling. You need to register the JavaScript class in the server side Extender class so the script is emitted at runtime.

Server-side Code

In the following section, I will describe the important server side steps to build up the Extender control. In the following snippet, you see the definition of the Extender class on the server-side.

[assembly: System.Web.UI.WebResource("AjaxSamples.GridViewKeyBoardPagerBehavior.js",
                                     "application/x-javascript")]

namespace AjaxSamples
{

    [TargetControlType(typeof(System.Web.UI.WebControls.GridView))]
    public class GridViewKeyBoardPagerExtender : System.Web.UI.ExtenderControl
    {
    ...

The extender makes only sense in conjunction with a GridView control. We can limit the use of the extender control to the target type GridView by adding the TargetControlType attribute to the class definition. A System.ArgumentNullException is thrown when the control, targeted by the TargetControlID property of the Extender control, is not of type GridView.

As stated before, the extender works by triggering the postbacks of the GridView. So, we need three properties for each action: the unique ID value of the GridView control on the page, the key value of the keyboard which is intended to fire the postback, and the command argument of the corresponding action on the GridView.

The following code snippet shows the server side properties for handling the delete event. All other events have analogous properties.

private string _delCmdArgument = string.Empty;

[Browsable(false)]
protected string DeleteCmdArgument
{
    get { return _delCmdArgument; }
    set { _delCmdArgument = value; }
}

private Keys _delKey = Keys.Delete;

[Browsable(false)]
protected string DeleteKeyCode
{
    get { return Convert.ToInt32(_delKey).ToString(); }
}

[Browsable(true), DefaultValue(Keys.Delete)]
public Keys DeleteKey
{
    get { return _delKey; }
    set { _delKey = value; }
}

...

The property DeleteCmdArgument holds the value for the command argument of the corresponding GridView event. The actual value depends on the current state of the GridView, and is determined later.

The public property DeleteKey is used for the key value so we can configure it declaratively, e.g., in a User Control. For attaching this key value to a corresponding key value on the client side, we use a property which gives back the string representation of the ASCII key value of the selected public key enumeration value.

The key values are defined an an enumeration found in the file KeyCodeEnums.cs.

We need the unique id of the GridView on the client side to fire a postback on it.

[Browsable(false)]
protected string PostBackCtrlID
{
    get { return Grid.UniqueID; }
}

The GridView object the extender belongs to is determined by gaining a reference to the control with the ID value of the TargetControlID of the extender control. The extender property TargetControlID is the ID of that control the extender is associated with.

private GridView _grid;
[Browsable(false)]
protected GridView Grid
{
    get
    {
        if (_grid == null)
        {
            _grid = Parent.FindControl(
                TargetControlID) as System.Web.UI.WebControls.GridView;
            if (_grid == null)
            {
                throw new NullReferenceException(string.Format(
                    "{0} is not of type GridView or the GridView is no initialized.",
                    TargetControlID));
            }
        }

        return _grid;
    }
}

In the OnPreRender override, we set up the command arguments for postback of the GridView. The current values of the command arguments depend on the current state of the GridView, e.g., the index of the selected page or row.

protected override void OnPreRender(EventArgs e)
{
// Grid is in edit mode, so we set corresponding command arguments.
// canceling or updating editing are the only allowed actions in this state.
// Grid is in edit mode so we set corresponding command values.
if (Grid.EditIndex > -1)
{
    _editCancelCmdArgument = "Cancel$" + Grid.EditIndex.ToString();
    _editUpdateCmdArgument = "Update$" + Grid.EditIndex.ToString();
}
else
{
    // Is a row selected
    if (Grid.SelectedIndex > -1)
    {
        // Deleting this row is possible
        _delCmdArgument = "Delete$" + Grid.SelectedIndex.ToString();
        // Editing// switch row to Editing mode is possible
        _editBeginCmdArgument = "Edit$" + Grid.SelectedIndex.ToString();

        // Selecting is possible if selecting is enabled
        if (Grid.SelectedIndex == 0)
        {
            _prevSelectCmdArgument = String.Empty;
        }
        else
        {
            _prevSelectCmdArgument = "Select$" + Convert.ToString(Grid.SelectedIndex - 1);
        }

        if ((Grid.SelectedIndex == Grid.PageSize - 1) || (
            Grid.SelectedIndex == Grid.Rows.Count - 1))
        {
            _nextSelectCmdArgument = String.Empty;
        }
        else
        {
            _nextSelectCmdArgument = "Select$" + Convert.ToString(Grid.SelectedIndex + 1);
        }
    }

    #region Paging
    // depending of the PagerSettings.Mode value the CommandArgument value of
    // the Pager LinkButtons differ.
    switch (Grid.PagerSettings.Mode)
    {
        case PagerButtons.Numeric:
        case PagerButtons.NumericFirstLast:
            #region Prev/Next Paging Arguments

            // claculate Next/Previous Pager Arguments
            // there is no previous page, the grid shows the first page
            if (Grid.PageIndex == 0)
            {
                _prevPageCmdArgument = string.Empty;
            }
            else
            {
                _prevPageCmdArgument = "Page$" + Convert.ToString(Grid.PageIndex);
            }

            // there is no next page. The grid shows the last page
            if (Grid.PageIndex + 2 > Grid.PageCount)
            {
                _nextPageCmdArgument = string.Empty;
            }
            else
            {
                _nextPageCmdArgument = "Page$" + Convert.ToString(Grid.PageIndex + 2);
            }
            #endregion

            break;
        case PagerButtons.NextPrevious:
        case PagerButtons.NextPreviousFirstLast:
            #region Prev/Next Paging
            // there is no previous page, the grid shows the first page
            if (Grid.PageIndex == 0)            {
                _prevPageCmdArgument = String.Empty;
            }
            else
            {
                _prevPageCmdArgument = "Page$Prev";
            }

            // there is no next page. The grid shows the last page
            if (Grid.PageIndex == Grid.PageCount - 1)
            {
                _nextPageCmdArgument = String.Empty;
            }
            else
            {
                _nextPageCmdArgument = "Page$Next";
            }
            #endregion
            break;
    }

    #region First/Last Paging Settings

    if (Grid.PageIndex == 0)
    {
        _firstPageCmdArgument = String.Empty;
    }
    else
    {
        _firstPageCmdArgument = "Page$First";
    }

    if (Grid.PageIndex == Grid.PageCount - 1)
    {
        _lastPageCmdArgument = String.Empty;
    }
    else
    {
        _lastPageCmdArgument = "Page$Last";
    }

    #endregion
    #endregion
}

base.OnPreRender(e);
}

During postback, for security purposes, the ASP.NET runtime checks whether the control ID and the command argument is a valid combination that is allowed to fire postback events. For that, we need to register the GridView's control ID and the command arguments the GridView could post back. This is achieved by calls to the method RegisterForEventValidation of the ClientScriptManager class. This step must be done in the override of the Render method of our extender control. After registering the combination of the ID and the command arguments, the extender is allowed to post back these commands of the GridView.

protected override void Render(HtmlTextWriter writer)
{
    ClientScriptManager csm = Page.ClientScript;

    for (int i = 0; i < Grid.PageSize; i++)
    {
        csm.RegisterForEventValidation(Grid.UniqueID, "Select$" + i.ToString());
        csm.RegisterForEventValidation(Grid.UniqueID, "Edit$" + i.ToString());
        csm.RegisterForEventValidation(Grid.UniqueID, "Cancel$" + i.ToString());
        csm.RegisterForEventValidation(Grid.UniqueID, "Delete$" + i.ToString());
        ...

To bridge the server side part of the extender with the client side part, you have to override the method GetScriptDescriptors of the extender class. There you map the server properties with properties on the client side Behavior class.

protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors(
                   Control targetControl)
{
    ScriptBehaviorDescriptor descriptor = new ScriptBehaviorDescriptor(
        "AjaxSamples.GridViewKeyBoardPagerBehavior", targetControl.ClientID);
    descriptor.AddProperty("_firstCmdArgument", this.FirstPageCmdArgument);
    descriptor.AddProperty("_lastCmdArgument", this.LastPageCmdArgument);
    ...
    return new ScriptDescriptor[] { descriptor };
}

In the method GetScriptReferences override of the extender class, we register the client side script of the extender.

protected override IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference reference = new ScriptReference();
    reference.Name = "AjaxSamples.GridViewKeyBoardPagerBehavior.js";
    reference.Assembly = "AjaxSamples";

    return new ScriptReference[] { reference };
}

Now, we have finished the server side part of the extender control. The following part describes the important details on the client-side.

Client-side Code

As the first important step, we register a KeyDown event handler in the initialization method of the behavior class.

AjaxSamples.GridViewKeyBoardPagerBehavior.prototype = {
initialize : function() {
        AjaxSamples.GridViewKeyBoardPagerBehavior.callBaseMethod(this, 'initialize');
        // we catch the keydown event at the document level of the HTML DOM tree
    $addHandler(document, 'keydown', Function.createDelegate(this, this._onKeyDown));
    window.onkeydown = function(e)
    {
        if (keyEvent.ctrlKey == true) {
            if (e.keyCode == "68") {
                return false;
            }
        }
    }
},

dispose : function() {
        AjaxSamples.GridViewKeyBoardPagerBehavior.callBaseMethod(this, 'dispose');
        
        $clearHandlers(document);
},

Another thing we do here is deactivate any keyboard shortcuts of the browser that conflicts with one of our keyboard shortcuts. In this case, we deactivate the "Add to Favorites" (CTRL+D) of the Firefox browser that conflicts with our Save Changes shortcut when editing a row in the GridView. We have to dispose the event handler to avoid memory leaks. We use the dispose function for that.

The next interesting thing is the handler itself.

 // Event Handler that catches the keyboard event
_onKeyDown : function(keyEvent) {

    var cmdArgument = "";

    if (keyEvent.ctrlKey == true) {
    
        if (keyEvent.keyCode == this._firstKeyCode) {
            cmdArgument = this._firstCmdArgument;
        }

        if (keyEvent.keyCode == this._lastKeyCode) {
            cmdArgument  = this._lastCmdArgument;
        }
        ...
        if (keyEvent.keyCode == this._editUpdateKeyCode) {
            window.onkeypress = function(keyEvent){return false;}
            if (this._editUpdateCmdArgument != "") {
                feedBack = confirm("Save Changes?");
                if (feedBack) {
                    cmdArgument = this._editUpdateCmdArgument;
                }
            }
        }
        if (cmdArgument != "") {
            __doPostBack(this._postBackCtrlID, cmdArgument);
            return;
        }
    }
    ...

In the handler, you can edit the code to adapt the behavior of the extender when pressing a key (combination) to your needs.

In the KeyDown handler on the client side, we check if the pressed key (combination) is one of the keyboard shortcuts for the GridView actions we defined on the server side. If this is the case, we call the __doPostBack function with the ID of the GridView and the command argument which fires the corresponding command on the server side. The __doPostBack function is emitted by the ASP.NET framework to the HTML markup of the page. The purpose is for posting back (submitting the HTML form) as a response to a user action or another client side event which calls for server side handling.

Using the code

To use the extender in your project, unzip the code and add it to your solution.

Now, your are ready to use the extender in your existing or upcoming projects. You can add it in the ASP.NET Toolbox if you use it regularly.

The following ASP.NET markup shows how the GridView and the extender control coexist. One important property is the TargetControlID. This is the ID of the the control, in our case the GridView control, the extender control adds client behavior to. TargetControlID is a property of the System.Web.UI.Extender class.

<%@ Register Assembly="AjaxSamples" Namespace="AjaxSamples" TagPrefix="cc1" %>
...
<asp:ScriptManager runat="server" ID="ScripManager1">
</asp:ScriptManager>
<asp:UpdatePanel runat="server" ID="UpdatePanel1" ChildrenAsTriggers="true"
    UpdateMode="Conditional">
    <ContentTemplate>
        <div>
        <asp:AccessDataSource ID="Northwind" runat="server" 
            DataFile="~/App_Data/Nwind.mdb"
            SelectCommand="SELECT [CustomerID], [CompanyName], [ContactName],
            [Address], [City], [Country] FROM [Customers]">
        </asp:AccessDataSource>
        <asp:GridView ID="GridView1"
             runat="server" 
             DataSourceID="Northwind" 
             DataKeyNames="CustomerID" 
             AutoGenerateColumns="true" AllowPaging="True" >
        </asp:GridView>
        <cc1:GridViewKeyBoardPagerExtender NextKey="Up" PreviousKey="Down"
        ID="GridViewKeyBoardPagerExtender1" runat="server" TargetControlID="GridView1" />
        </div>
    </ContentTemplate>
</asp:UpdatePanel>

History

  • 3 July 2008, Version 1.0.

License

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

About the Author

Rolf Cerff


I'am a freelance software developer, located at Freiburg i. Br., Germany. My main interest is developing Internet application based on the ASP.NET technology, AJAX, Web 2.0 related issues and Software Design and Architecture.
Occupation: Software Developer
Company: Dipl.-Ing. Rolf Cerff Software Development and IT-
Location: Germany Germany

Other popular Ajax and Atlas articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 7 of 7 (Total in Forum: 7) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralJavascript errors with the gridview extendermemberMember 225753222:46 7 Aug '08  
GeneralGood articlemembersrinath g nath22:17 6 Jul '08  
GeneralRe: Good articlememberRolf Cerff23:39 6 Jul '08  
GeneralWhy did someone vote this a 1?memberMike Ellison8:48 4 Jul '08  
GeneralRe: Why did someone vote this a 1?memberRolf Cerff0:19 5 Jul '08  
GeneralRe: Why did someone vote this a 1?memberMike Ellison10:28 6 Jul '08  
GeneralRe: Why did someone vote this a 1?memberDaniel Winata1:31 13 Aug '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 4 Jul 2008
Editor: Smitha Vijayan
Copyright 2008 by Rolf Cerff
Everything else Copyright © CodeProject, 1999-2008
Web20 | Advertise on the Code Project