How to create an ASP.NET AJAX Toolkit Extender Control to Extend a GridView
How to create an ASP.NET AJAX Toolkit Extender Control to Extend a GridView
In a previous post I showed examples of how CSS and JavaScript code could be used to freeze the header row of a GridView control and add scrolling capabilities for Internet Explorer and Firefox. Here’s an example of a GridView with a frozen header:
In this post I’m going to discuss how to package up CSS and JavaScript files into a custom control that can be used on the server-side to extend the GridView. Doing this allows you to continue using the standard GridView that ships with ASP.NET 2.0 in applications while extending it’s functionality through a custom extender control.
There are multiple ways to go about creating an AJAX control extender. You can use native classes available in the ASP.NET AJAX Extensions to build a control that would get the job done. However, doing so requires that you write custom code to map server-side properties to client-side properties which isn’t hard. Additional details on how to do this can be found in the ASP.NET 2.0 Professional AJAX book Matt Gibbs and I co-authored. You can download the code from the book here if you’d like to see the sample code (see the chapter 11 code).
To simplify things as much as possible, I’m going to discuss how to build a custom control that is based upon the ASP.NET AJAX Control Toolkit available from http://www.codeplex.com/AtlasControlToolkit (that’s not a typo, they never changed the Atlas name to AJAX in the URL for some reason). The ASP.NET AJAX Control Toolkit provides a base class named ExtenderControlBase along with several attribute classes that minimize the amount of code that has to be written create a control extender. Here's the steps I went through to make the control.
1. Create a new Class Library Project in VS.NET
Create a class library project in VS.NET. Add the AjaxControlToolkit.dll file to the bin folder and add a reference to the assembly using the "Add Reference" dialog. I also added references to the System.Web and System.Web.Extensions assemblies.
2. Create the Behavior Script that the Extender Control will Output.
Add a new JavaScript file into the project (I named mine GridViewHeaderBehavior.js). Here's the entire script I used for the frozen header extender control. I started out by writing JavaScript in a page to get the code working properly first and then converted the code to the "prototype design pattern" as shown next. This leverages functionality in the ASP.NET AJAX and ASP.NET AJAX Toolkit script libraries.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | Type.registerNamespace(<span style="color: #a31515;">'WahlinControlToolkit'</span>); WahlinControlToolkit.GridViewHeaderBehavior = <span style="color: #0000ff;">function</span>(element) { <span style="color: #008000;">/// <summary> </span> <span style="color: #008000;">/// The GridViewHeaderBehavior is used to fix a GridView control header and make the /// control scrollable </span> <span style="color: #008000;">/// </summary> </span> <span style="color: #008000;">/// <param name="element" type="Sys.UI.DomElement"> </span> <span style="color: #008000;">/// The GridView element this behavior is associated with </span> <span style="color: #008000;">/// </param> </span> WahlinControlToolkit.GridViewHeaderBehavior.initializeBase(<span style="color: #0000ff;">this</span>, [element]); <span style="color: #0000ff;">this</span>._WrapperDivCssClass = <span style="color: #0000ff;">null</span>; } WahlinControlToolkit.GridViewHeaderBehavior.prototype = { initialize : <span style="color: #0000ff;">function</span>() { <span style="color: #008000;">/// <summary> </span> <span style="color: #008000;">/// Initialize the behavior </span> <span style="color: #008000;">/// </summary> </span> WahlinControlToolkit.GridViewHeaderBehavior.callBaseMethod(<span style="color: #0000ff;">this</span>, <span style="color: #a31515;">'initialize'</span>); <span style="color: #0000ff;">var</span> element = <span style="color: #0000ff;">this</span>.get_element(); <span style="color: #0000ff;">this</span>._FreezeGridViewHeader(); }, dispose : <span style="color: #0000ff;">function</span>() { <span style="color: #008000;">/// <summary> </span> <span style="color: #008000;">/// Dispose the behavior </span> <span style="color: #008000;">/// </summary> </span> <span style="color: #0000ff;">var</span> element = <span style="color: #0000ff;">this</span>.get_element(); WahlinControlToolkit.GridViewHeaderBehavior.callBaseMethod(<span style="color: #0000ff;">this</span>, <span style="color: #a31515;">'dispose'</span>); }, _FreezeGridViewHeader : <span style="color: #0000ff;">function</span> () { <span style="color: #0000ff;">var</span> grid = <span style="color: #0000ff;">this</span>.get_element(); <span style="color: #0000ff;">if</span> (grid != <span style="color: #a31515;">'undefined'</span>) { grid.style.visibility = <span style="color: #a31515;">'hidden'</span>; <span style="color: #0000ff;">var</span> div = <span style="color: #0000ff;">null</span>; <span style="color: #0000ff;">if</span> (grid.parentNode != <span style="color: #a31515;">'undefined'</span>) { div = grid.parentNode; <span style="color: #0000ff;">if</span> (div.tagName == <span style="color: #a31515;">'DIV'</span>) { div.className = <span style="color: #0000ff;">this</span>._WrapperDivCssClass; div.style.overflow = <span style="color: #a31515;">'auto'</span>; } } <span style="color: #0000ff;">var</span> tags = grid.getElementsByTagName(<span style="color: #a31515;">'TBODY'</span>); <span style="color: #0000ff;">if</span> (tags != <span style="color: #a31515;">'undefined'</span>) { <span style="color: #0000ff;">var</span> tbody = tags[0]; <span style="color: #0000ff;">var</span> trs = tbody.getElementsByTagName(<span style="color: #a31515;">'TR'</span>); <span style="color: #0000ff;">var</span> headerHeight = 8; <span style="color: #0000ff;">if</span> (trs != <span style="color: #a31515;">'undefined'</span>) { headerHeight += trs[0].offsetHeight; <span style="color: #0000ff;">var</span> headTR = tbody.removeChild(trs[0]); <span style="color: #0000ff;">var</span> head = document.createElement(<span style="color: #a31515;">'THEAD'</span>); head.appendChild(headTR); grid.insertBefore(head, grid.firstChild); } tbody.style.height = (div.offsetHeight - headerHeight) + <span style="color: #a31515;">'px'</span>; tbody.style.overflowX = <span style="color: #a31515;">'hidden'</span>; tbody.overflow = <span style="color: #a31515;">'auto'</span>; tbody.overflowX = <span style="color: #a31515;">'hidden'</span>; } grid.style.visibility = <span style="color: #a31515;">'visible'</span>; } }, get_WrapperDivCssClass : <span style="color: #0000ff;">function</span>() { <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span>._WrapperDivCssClass; }, set_WrapperDivCssClass : <span style="color: #0000ff;">function</span>(value) { <span style="color: #0000ff;">this</span>._WrapperDivCssClass = value; <span style="color: #0000ff;">this</span>.raisePropertyChanged(<span style="color: #a31515;">'WrapperDivCssClass'</span>); } } WahlinControlToolkit.GridViewHeaderBehavior.registerClass (<span style="color: #a31515;">'WahlinControlToolkit.GridViewHeaderBehavior'</span>, AjaxControlToolkit.BehaviorBase); |
3. Mark the Script as an Embedded Resource in VS.NET
Right-click on the JavaScript file in the Solution Explorer and select Properties from the menu. Change the Build Action property to "Embedded Resource". This will embed the script into the extender control assembly so that you don't have to worry about deploying it as a standalone file.
4. Add the Control Extender Class into the Project
Add a new class into the project (I named mine GridViewHeaderExtender.cs). The class imports the AjaxControlToolkit namespace and derives from ExtenderControlBase which is a class provided by the ASP.NET AJAX Toolkit assembly (AjaxControlToolkit.dll). You can have the control automatically output the script mentioned in the previous steps to the client by using the WebResource attribute along with the ClientScriptResource attribute as shown next. The control (named GridViewHeaderExtender) exposes a single property named WrapperDivCssClass that represents the CSS class that will be applied to the div that wraps the GridView control.
The control also overrides OnPreRender() in order to dynamically output two CSS classes into the page that are needed to help freeze the GridView header row. This is done by calling the CreateStyleRule() method which relies on two custom style classes to generate the necessary CSS data. The complete code for the GridViewHeaderExtender is shown next:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <span style="color: #0000ff;">using</span> System; <span style="color: #0000ff;">using</span> System.Web.UI.WebControls; <span style="color: #0000ff;">using</span> System.Web.UI.HtmlControls; <span style="color: #0000ff;">using</span> System.Web.UI; <span style="color: #0000ff;">using</span> System.ComponentModel; <span style="color: #0000ff;">using</span> System.ComponentModel.Design; <span style="color: #0000ff;">using</span> AjaxControlToolkit; [assembly: <span style="color: #2b91af;">WebResource</span>(<span style="color: #a31515;">"WahlinControlToolkit.GridViewHeaderBehavior.js"</span>, <span style="color: #a31515;">"text/javascript"</span>)] <span style="color: #0000ff;">namespace</span> WahlinControlToolkit { [<span style="color: #2b91af;">TargetControlType</span>(<span style="color: #0000ff;">typeof</span>(<span style="color: #2b91af;">GridView</span>))] [<span style="color: #2b91af;">ClientScriptResource</span>(<span style="color: #a31515;">"WahlinControlToolkit.GridViewHeaderBehavior"</span>, <span style="color: #a31515;">"WahlinControlToolkit.GridViewHeaderBehavior.js"</span>)] [<span style="color: #2b91af;">ToolboxItem</span>(<span style="color: #a31515;">"System.Web.UI.Design.WebControlToolboxItem, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"</span>)] <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> <span style="color: #2b91af;">GridViewHeaderExtender</span> : <span style="color: #2b91af;">ExtenderControlBase </span> { [<span style="color: #2b91af;">DefaultValue</span>(<span style="color: #a31515;">""</span>)] [<span style="color: #2b91af;">Description</span>(<span style="color: #a31515;">"CSS class to apply to the GridView wrapper div element. Example of a wrapper div style: .WrapperDiv {width:800px;height:400px;border: 1px solid black;}"</span>)] [<span style="color: #2b91af;">ExtenderControlProperty</span>] [<span style="color: #2b91af;">ClientPropertyName</span>(<span style="color: #a31515;">"WrapperDivCssClass"</span>)] <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> WrapperDivCssClass { <span style="color: #0000ff;">get</span> { <span style="color: #0000ff;">return</span> GetPropertyValue<<span style="color: #0000ff;">string</span>>(<span style="color: #a31515;">"WrapperDivCssClass"</span>, <span style="color: #0000ff;">string</span>.Empty); } <span style="color: #0000ff;">set</span> { SetPropertyValue<<span style="color: #0000ff;">string</span>>(<span style="color: #a31515;">"WrapperDivCssClass"</span>, <span style="color: #0000ff;">value</span>); } } <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> OnPreRender(<span style="color: #2b91af;">EventArgs</span> e) { <span style="color: #0000ff;">base</span>.OnPreRender(e); <span style="color: #0000ff;">if</span> (<span style="color: #2b91af;">String</span>.IsNullOrEmpty(<span style="color: #0000ff;">this</span>.WrapperDivCssClass)) { <span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> <span style="color: #2b91af;">ApplicationException</span>(<span style="color: #a31515;">"WrapperDivCssClass property value must be defined for the GridViewHeaderExtender control. Here's an example of a wrapper div style that should be defined in the page and referenced with the WrapperDivCssClass property: .WrapperDiv {width:800px;height:400px;border: 1px solid black;}"</span>); } <span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.Enabled && !<span style="color: #0000ff;">this</span>.Page.Items.Contains(<span style="color: #a31515;">"GridViewHeaderStyles"</span>)) { <span style="color: #0000ff;">this</span>.Page.Items[<span style="color: #a31515;">"GridViewHeaderStyles"</span>] = <span style="color: #0000ff;">true</span>; <span style="color: #0000ff;">this</span>.Page.Header.StyleSheet.CreateStyleRule(<span style="color: #0000ff;">new</span> <span style="color: #2b91af;">GridViewThCssClass</span>(), <span style="color: #0000ff;"> null</span>, <span style="color: #a31515;">"."</span> + <span style="color: #0000ff;">this</span>.WrapperDivCssClass + <span style="color: #a31515;">" TH"</span>); <span style="color: #0000ff;">this</span>.Page.Header.StyleSheet.CreateStyleRule(<span style="color: #0000ff;">new</span> <span style="color: #2b91af;">GridViewTrCssClass</span>(), <span style="color: #0000ff;"> null</span>, <span style="color: #a31515;">"."</span> + <span style="color: #0000ff;">this</span>.WrapperDivCssClass + <span style="color: #a31515;">" TR"</span>); } } <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">class</span> <span style="color: #2b91af;">GridViewTrCssClass</span> : <span style="color: #2b91af;">Style </span> { <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> FillStyleAttributes(<span style="color: #2b91af;">CssStyleCollection</span> attributes, <span style="color: #2b91af;"> IUrlResolutionService</span> urlResolver) { <span style="color: #0000ff;">base</span>.FillStyleAttributes(attributes, urlResolver); attributes[<span style="color: #2b91af;">HtmlTextWriterStyle</span>.Height] = <span style="color: #a31515;">"0px"</span>; } } <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">class</span> <span style="color: #2b91af;">GridViewThCssClass</span> : <span style="color: #2b91af;">Style </span> { <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> FillStyleAttributes(<span style="color: #2b91af;">CssStyleCollection</span> attributes, <span style="color: #2b91af;"> IUrlResolutionService</span> urlResolver) { <span style="color: #0000ff;">base</span>.FillStyleAttributes(attributes, urlResolver); attributes[<span style="color: #2b91af;">HtmlTextWriterStyle</span>.Position] = <span style="color: #a31515;">"relative"</span>; } } } } |
5. Create a Website and Reference the Extender Control Assembly
Once the extender control is compiled and the assembly is ready to use, you need to reference it in the page that needs to extend a GridView (or you can define it in web.config if you'd like all pages to use it). This is done by adding the following code into the page:
1 | <span style="background: #ffee62;"><%<span style="color: #0000ff;"> </span>@</span> <span style="color: #a31515;">Register</span> <span style="color: #ff0000;">Assembly</span><span style="color: #0000ff;">="WahlinControlToolkit"</span> <span style="color: #ff0000;">Namespace</span><span style="color: #0000ff;">="WahlinControlToolkit"</span> <span style="color: #ff0000;"> TagPrefix</span><span style="color: #0000ff;">="toolkit"</span> <span style="background: #ffee62;">%> </span> |
6. Define the Wrapper Div CSS Class Used by the Extender Control
Once the extender control has been referenced you'll need to define the CSS class that will be used to wrap the GridView control (add borders around it if desired, set height and width, etc.) in the page or in an external CSS stylesheet:
1 2 3 4 | <span style="color: #0000ff;"><</span><span style="color: #a31515;">style</span> <span style="color: #ff0000;">type</span><span style="color: #0000ff;">="text/css"> </span> <span style="color: #a31515;">.WrapperDiv</span> { <span style="color: #ff0000;">width</span>:<span style="color: #0000ff;">800px</span>;<span style="color: #ff0000;">height</span>:<span style="color: #0000ff;">400px</span>;<span style="color: #ff0000;">border</span>: <span style="color: #0000ff;">1px</span> <span style="color: #0000ff;">solid</span> <span style="color: #0000ff;">black</span>; } <span style="color: #0000ff;"></</span><span style="color: #a31515;">style</span><span style="color: #0000ff;">></span> |
7. Use the Extender Control in the Page to Extend the GridView Control and Freeze the Header Row
Add the extender control into to the page:
1 2 | <span style="color: #0000ff;"><</span><span style="color: #a31515;">toolkit</span><span style="color: #0000ff;">:</span><span style="color: #a31515;">GridViewHeaderExtender</span> <span style="color: #ff0000;">ID</span><span style="color: #0000ff;">="gvExtender"</span> <span style="color: #ff0000;">runat</span><span style="color: #0000ff;">="server"</span> <span style="color: #ff0000;">TargetControlID</span><span style="color: #0000ff;">="GridView1"</span> <span style="color: #ff0000;">WrapperDivCssClass</span><span style="color: #0000ff;">="WrapperDiv"/></span> |
And there you have it! The hardest part of building an extender control is getting the script to do what you want. After you get the script working properly, the actual extender control class is pretty simple to write since you can leverage existing functionality in the ASP.NET AJAX Toolkit. I'm considering making a video that covers these concepts so if I get some free time I'll post it here (no promises though :-)).
All of the code discussed in this post can be downloaded here. If you enhance it in some way, fix any bugs that are discovered, or add additional functionality please let me know about it and I'll get the code base updated with your changes.
You May Also Like
AJAX, ASP.NET, Developer, HTML, JavaScript, VB.NET, Visual Basic, XML
A Simple Introduction to Cisco CML2
0 3703 0Mark Jacob, Cisco Instructor, presents an introduction to Cisco Modeling Labs 2.0 or CML2.0, an upgrade to Cisco’s VIRL Personal Edition. Mark demonstrates Terminal Emulator access to console, as well as console access from within the CML2.0 product. Hello, I’m Mark Jacob, a Cisco Instructor and Network Instructor at Interface Technical Training. I’ve been using … Continue reading A Simple Introduction to Cisco CML2
How to Build in a PSMethod to your PowerShell Code
0 68 0In this video, PowerShell instructor Jason Yoder shows how to add Methods (PSMethod) to your code using free software that’s added into the PSObject. For instructor-led PowerShell courses, see our course schedule. Microsoft Windows PowerShell Training Download the Building Methods PowerShell script</a> used in this video. <# ╔══════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ Building Methods ║ ╟──────────────────────────────────────────────────────────────────────────────╢ … Continue reading How to Build in a PSMethod to your PowerShell Code
Configuring Windows Mobility Center and How to Turn it On and Off
1 1413 1Video transcription Steve Fullmer: In our Windows training courses, we often share information about the Windows 8.1 Mobility Center. Mobility Center was introduced for mobile and laptop devices in Windows 7. It’s present and somewhat enhanced in Windows 8. Since we don’t have mobile devices in our classrooms, I decided to take a little bit … Continue reading Configuring Windows Mobility Center and How to Turn it On and Off