Introduction

The ASP.NET GridView control provides a way to present an HTML table of records from a database table. Data binding is supported to easily populate the GridView. The BoundField class provides the values of columns as text in the HTML table. There are also classes that provide for some of the common controls that you would want to use in the HTML table columns: check boxes, images, buttons, and links. In addition, the TemplateField class is provided for completely customisable representations of data in a GridView.

While the CheckBoxField class that is provided can be used to select multiple records in a GridView control, sometimes, there is a requirement to only allow a single record to be selected. In this case, a radio button makes more sense than a check box. However, there is no class in ASP.NET for putting a radio button in a column of a GridView.

It is very straightforward to add a TemplateField in the page designer to add a column of radio buttons to a GridView. However, adding such a radio button in code dynamically, at runtime, is not as straightforward. This requires creating a new class that inherits from TemplateField and a new class that implements the ITemplate interface in order to provide the customized rendering of the radio buttons.

The SelectorRadioButtonField class presented in this article is a customised TemplateField that can be used to include a radio button column in a GridView. The radio buttons are rendered as standard HTML <input> fields of type "radio". The "value" attribute of each radio button is used to indicate the primary key value of the selected record.

The SelectorRadioButtonField includes the following features:

Allows for data binding of the "value" attribute of the radio button to the primary key column of the database table.

Allows for data binding of other attributes of the radio button.

Allow for client-side enabling and disabling of action buttons based on the record that is currently selected. This can use attributes of the radio button that have been bound to other columns.

Using the code

Download the demo project. This is a web application project that demonstrates the SelectorRadioButtonField being used on a GridView control. The web application consists of a single web page (Default.aspx) with a GridView that displays the records in the Orders table from the Northwind database. The data is stored in an XML file that is part of the project, so you don't need to have a SQL Server database with the Northwind database in order to run the demo project.

Use the demo project code to help follow the information provided on how to use the code.

There are four main steps required to incorporate the SelectorRadioButtonField in your project and use it in a GridView control:

Each of these steps is explained further below. The first two steps only need to be done once in your project. The third and fourth steps need to be done for each GridView control in which you use the SelectorRadioButtonField.

Following the explanation of the steps to incorporate the SelectorRadioButtonField in your project and use it in a GridView control, there is an explanation of the code.

In an appropriate folder in your project, add the SelectorRadioButtonField.cs code file from the demo project. This code file contains the SelectorRadioButtonField class. The SelectorRadioButtonField class contains two private classes that are used internally to the SelectorRadioButtonField class, and two public collection classes used to support two of the properties of the SelectorRadioButtonField class. Each of these classes is described later.

You may want to change the namespace for the SelectorRadioButtonField to something that is compatible with your own project.

In an appropriate folder in your project, add the SelectorRadioButton.js code file from the demo project. This code file contains the SelectRadioButton JavaScript function. This function is used to select the instance of a radio button on a page with a specific name and value.

Alternatively, instead of creating a separate file for the SelectRadioButton JavaScript function, you can add this function to another JavaScript code file that is part of your project. The only requirement is that the SelectRadioButton JavaScript function is loaded for each page with a GridView that uses the SelectorRadioButtonField.

The SelectorRadioButtonField is added to a GridView control in code, rather than in the page designer. The code to add the SelectorRadioButtonField needs to be added at a suitable place in the code-behind class for the page or user control containing the GridView.

Code also needs to be added to support pre-selecting a record when the page containing the GridView is displayed, and to determine the selected record when the page is posted back.

Add a public constant for the value of the Name property of the SelectorRadioButtonField

publicconststring SelectedOrderIDField = "SelectedOrderID";

It is recommended that you use a public class constant for the value of the Name property as it will be used in three places in your project.

In the demo project, SelectedOrderIDField is a public constant that has been defined in the code for the Default.aspx page.

Add a property for the Primary Key of the pre-selected record

By default, when a page containing a GridView control is displayed, none of the radio buttons will be selected. The SelectRadioButton JavaScript function that has been added to your project can be used to pre-select one of the radio buttons. To provide access to the Primary Key value of the record to be pre-selected, we will add a public read-only property (and private backing variable). We will see how this is used with the SelectRadioButton JavaScript function later.

In the demo project, PreSelectedOrderID is a public property that has been defined in the code for the Default.aspx page. _preSelectedOrderID is the private backing variable for this property.

Add SelectorRadioButtonField to the GridView

At a suitable place in the code-behind class for the page or user control containing the GridView, add code to include a SelectorRadioButtonField.

The SelectorRadioButtonField needs to be added to a GridView control before the data binding is invoked (i.e., before the DataBind method of the GridView is called).

In the demo project, SelectorRadioButtonField is added to the GridView control in the Page_PreRender method for the Default.aspx page.

Declare a new local variable of type SelectorRadioButtonField and set it to a new SelectorRadioButtonField instance.

Note: You may need to add a new using statement to your code file if SelectorRadioButtonField is in a namespace that is not already referenced.

There are two constructors provided for SelectorRadioButtonField. If you use the default constructor (no parameters), you must set the Name and ValueDataField properties. A second constructor takes values for each of these properties so they do not need to be set explicitly.

Set the Name property of the SelectorRadioButtonField

selectorRadioButtonField.Name = SelectedOrderIDField;

The Name property must be set. This will be used as the value of the "name" attribute of the <input> element for the radio button that is rendered for each row in the GridView.

If you have provided the value for the Name property in the SelectorRadioButtonField constructor, you do not need to set the property explicitly.

Note: It is recommended that you use a public class constant for the value of the Name property as it will be used in three places in your project. In the demo project, the SelectedOrderIDField constant is used.

Set the ValueDataField property of the SelectorRadioButtonField

The ValueDataField property must be set. This needs to be the column name of the Primary Key column of the DataTable that will be bound to the GridView control. This will be used as the value of the "value" attribute of the <input> element for the radio button that is rendered for each row in the GridView. When the page is posted back, the Form variables of the request will include a variable with the name of the "name" attribute and the value of the "value" attribute of the radio button that has been selected.

If you have provided the value for the ValueDataField property in the SelectorRadioButtonField constructor, you do not need to set the property explicitly.

Note: It is recommended that you use a constant for the value of the ValueDataField property. Alternatively, if your application uses Typed DataSets, you can use the property for the Primary Key column that is generated by the DataSet Designer. In the demo project, OrderIDColumn.ColumnName is the value generated for OrdersDataSet.OrdersDataTable.

Add the SelectorRadioButtonField to the Columns collection of the GridView

OrdersGridView.Columns.Add(selectorRadioButtonField);

The new instance of the SelectorRadioButtonField needs to be added to the Columns collection of the GridView. If all the columns in the GridView are added in code, you can simply use the Add method of the Columns property of the GridView. If other columns have been added to the GridView in the Page Designer, then you can use the Insert method of the Columns property of the GridView to control where in the column order the SelectorRadioButtonField is included.

Set the OnClick property of the SelectorRadioButtonField (Optional)

If you want to execute a client script each time the user clicks one of the radio buttons, then add the required script to the OnClick property of the SelectorRadioButtonField. This will be used as the value of the "onclick" attribute of the <input> element for the radio button that is rendered for each row in the GridView.

For example, one of the main uses of the SelectorRadioButtonField is to allow the user to select a record on which to perform some further action (e.g., edit the record, delete the record). Buttons are usually provided on the page to invoke each of the possible actions for the currently selected record. If you want to enable and disable some action buttons depending on the record that is currently selected, then include client script to enable and disable buttons in the OnClick property.

In the demo project, the "View Ship Region" button (cmdViewShipRegion) is only enabled if the currently selected Order has a value in the ShipRegion column. A JavaScript function called enableDisableButtons has been included on the Default.aspx page to manage this. For the SelectorRadioButtonField, the OnClick property has been set to the JavaScript required to call the enableDisableButtons function, passing the current instance of the radio button as a parameter.

selectorRadioButtonField.OnClick = "enableDisableButtons(this)";

Add to the AttributeBindings property of the SelectorRadioButtonField (Optional)

If you want to include additional attributes in the <input> element for the radio button that is rendered for each row in the GridView, and the value of the attribute should be dependant on the current row in the GridView (i.e., is bound to a column in the bound DataTable), then add an item to the AttributeBindings property of the SelectorRadioButtonField.

Adding attributes in this way can be used to set any attribute, either standard attributes for an <input> element (such as "title"), or custom attributes that can be used by the client script.

For example, in conjunction with the client script to enable/disable action buttons, custom attributes can be used to provide values used to determine whether to enable or disable buttons.

In the demo project, the "View Ship Region" button (cmdViewShipRegion) is only enabled if the currently selected Order has a value in the ShipRegion column. A JavaScript function called enableDisableButtons has been included on the Default.aspx page to manage this. For the SelectorRadioButtonField, an item has been added to the AttributeBindings property to include an attribute called "shipRegion" that is bound to the value of the "ShipRegion" column. The "shipRegion" attribute is used by the called enableDisableButtons function.

Add to the UnBoundAttributes property of the SelectorRadioButtonField (Optional)

If you want to include additional attributes in the <input> element for the radio button that is rendered for each row in the GridView, and the value of the attribute will be the same for each row in the GridView, then add an item to the UnBoundAttributes property of the SelectorRadioButtonField.

Adding attributes in this way can be used to set any attribute, either standard attributes for an <input> element (such as "title"), or custom attributes that can be used by the client script.

In the demo project, an item has been added to the UnBoundAttributes property to include an attribute called "title" with a value of "Click to select". "title" is a standard HTML attribute that is used by browsers such as Internet Explorer to provide a tooltip.

Set other TemplateField properties (Optional)

You can set other properties that are inherited from the base TemplateField class.

For example, you may want to provide a value for the HeaderText property. The following code will include the word "Select" as the header for the radio button column:

selectorRadioButtonField.HeaderText = "Select";

You may want to customise the ItemStyle property. By default, the ItemStyle property for SelectorRadioButtonField has centered horizontal alignment and is 40 pixels wide. The following code will change the width to 50 pixels:

Add code to determine the selected record

When the page containing the GridView is posted back, it may be necessary to determine the record in the GridView that is currently selected. For example, if the user has clicked a button to perform some action for the currently selected record, the selected record can be determined based on the particular radio button that was selected by the user.

Note: the radio buttons that are rendered are not ASP server controls, so cannot be accessed in code directly.

When the page is posted back, the Form variables of the request will include a variable with the name of the Name property of the SelectorRadioButton and the value of the Primary Key for the currently selected record.

It is likely that you will need to determine the selected record for more than one action. Therefore, you should create a private method that determines the Primary Key of the selected record and returns it as the appropriate data type. This method can then be called from the Click event handler methods for the action buttons.

Add a private method to get the Primary Key of the selected record

In the demo project, the Default.aspx page includes a private method called GetSelectedOrderID.

privateint GetSelectedOrderID()
{
// Find the selected Order ID in the form variables, ensure it is
// supplied and is an integer, return it as an integer.
string selectedOrderID = Request.Form[SelectedOrderIDField];
int orderId;
if (selectedOrderID == null)
{
// This should not happen.
thrownew ApplicationException(string.Format(
"'{0}' was missing from Form fields.",
SelectedOrderIDField));
}
if (int.TryParse(selectedOrderID, out orderId))
{
return orderId;
}
else
{
thrownew ApplicationException(string.Format(
"The value of the '{0}' Form field must be an integer."
+ " The value '{1}' is invalid.",
SelectedOrderIDField, selectedOrderID));
}
}

The value of the Primary Key of the selected record (the selected Order ID) is retrieved from the collection of Form variables (Request.Form). The same constant that was used to set the Name property of the SelectorRadioButtonField is used as the index into the collection.

A check is made to ensure that the required Form variable was present in the request.

In the demo project, the OrderID column is an integer. The values in the collection of Form variables are provided as strings. The string value of the selected Order ID is converted to an integer. A check is made to ensure that the value of the selected Order ID is an integer.

The selected Order ID is returned as an integer.

Add calls to the private method to get the Primary Key of the selected record

In the demo project, the Default.aspx page includes an event handler method for the "View Ship Region" button (cmdViewShipRegion_Click). The code in this method calls the GetSelectedOrderID method to get the Primary Key of the selected record. The selected Order ID is then used to perform the requested action for the selected record.

protectedvoid cmdViewShipRegion_Click(object sender, EventArgs e)
{
// Get the Order ID of the selected Order, find the Order in the
// Orders that are saved in Session, set the message label to a
// message that includes the Ship Region of the Order.
_preSelectedOrderID = GetSelectedOrderID();
OrdersDataSet.OrdersDataTable ordersDataTable =
(OrdersDataSet.OrdersDataTable)Session[SessionOrders];
OrdersDataSet.OrdersRow ordersRow =
ordersDataTable.FindByOrderID(_preSelectedOrderID.Value);
lblMessage.Text = "You clicked View Ship Region for region " +
ordersRow.ShipRegion;
}

There is JavaScript code that must be included on the page or the user control that will contain a GridView control that uses the SelectorRadioButtonField. This must be added within <script> elements on the page or user control.

Add script to include the SelectRadioButton JavaScript function

The "SelectorRadioButton.js" file was added to your project. This file contains the SelectRadioButton JavaScript function.

This file needs to be referenced in the page or control containing the GridView control. This should be done using a <script> element.

Add script to execute when a radio button is selected (Optional)

If you want to execute a client script each time the user clicks on one of the radio buttons, then add the required script to the OnClick property of the SelectorRadioButtonField. This will be used as the value of the "onclick" attribute of the <input> element for the radio button that is rendered for each row in the GridView. See above for information on adding script to the "onclick" attribute.

Unless the script to be included in the "onclick" attribute is trivial, it is best to add the required script to a JavaScript function on the page or user control containing the GridView, and just add a call to this function in the "onclick" attribute.

For example, in the demo project, the "View Ship Region" button (cmdViewShipRegion) is only enabled if the currently selected Order has a value in the ShipRegion column. A JavaScript function called enableDisableButtons has been included on the Default.aspx page to manage this. For the SelectorRadioButtonField, the OnClick property has been set to the JavaScript required to call the enableDisableButtons function, passing the current instance of the radio button as a parameter. The enableDisableButtons JavaScript function is included in a <script> element:

Note: The custom "shipRegion" attribute of the currently selected radio button is used to determine whether or not to enable the "View Ship Region" button.

Add script to call the SelectRadioButton JavaScript function

When the page containing the GridView control is displayed, the SelectRadioButton JavaScript function needs to be called to cause a radio button to be selected (either the radio button for a pre-selected record, or the first record if there is no pre-selected record or the pre-selected record is not on the page).

The simplest way of achieving this is to include a <script> element after the <asp:GridView> element on the page or user control. The JavaScript provided should call the SelectRadioButton function, passing as parameters the name of the radio button to select and the value of the radio button to be selected. The value of the constant used to set the Name property of the SelectorRadioButtonField should be used for the radio button name parameter. The value of the property used for the pre-selected record should be used as the value parameter.

If you have included a value for the optional OnClick property of the SelectorRadioButtonField, then you need to ensure you include a call to the same JavaScript after calling the SelectRadioButton JavaScript function. The SelectRadioButton JavaScript function returns a reference to the radio button that was selected by the function (null if no button was selected). This reference can be used in the JavaScript that is executed when a radio button is selected.

For example, in the demo project, the enableDisableButtons JavaScript function is called when a radio button is selected. The <script> element that is included on the Default.aspx page is:

In this case, the <script> element must be placed after any other element on the page that will be referenced by the JavaScript that is called. For the demo project, the <script> element must be placed after the "View Ship Region" button (cmdViewShipRegion).

Explanation of the code

The SelectorRadioButtonField class

publicclass SelectorRadioButtonField : TemplateField
{
...
}

SelectorRadioButtonField (in the SelectorRadioButtonField.cs code file) inherits from the TemplateField class. The TemplateField class uses the object assigned to its ItemTemplate property to render the appropriate content for each row in the GridView. The ItemTemplate property must be assigned an instance of a class that implements the ITemplate interface.

SelectorRadioButtonField has a private field (_selectorRadioButtonItem) whose type is a private class: SelectorRadioButtonItem (see below).

private SelectorRadioButtonItem _selectorRadioButtonItem;

SelectorRadioButtonItem implements the ITemplate interface. In the SelectorRadioButtonField constructor, _selectorRadioButtonItem is set as the ItemTemplate property for the base TemplateField class.

SelectorRadioButtonField has a number of public properties that wrap the same properties of the SelectorRadioButtonItem instance.

The purpose of the properties is described in the section below for SelectorRadioButtonItem.

SelectorRadioButtonField overrides the ItemTemplate property of the base TemplateField class. This helps prevent the ItemTemplate property being accessed, as the ItemTemplate for the SelectorRadioButtonField class is going to be managed internally (note that the ItemTemplate property is not virtual, so can still be accessed via the base TemplateField interface).

The first constructor creates a new instance of the SelectorRadioButtonItem class and assigns it to the _selectorRadioButtonItem instance variable and to the ItemTemplate property of the base TemplateField class. The ItemStyle of the TemplateField is set to be center aligned horizontally, and set to a width of 40 pixels.

The second constructor extends the first constructor, and allows the values of the Name and ValueDataField properties to be set.

SelectorRadioButtonField includes two private classes that are used internally: SelectorRadioButtonItem and SelectorRadioButton. These are described below.

SelectorRadioButtonField also includes two public collection classes that are used to support two properties of the SelectorRadioButtonField class: AttributeBindings and UnBoundAttributes. These are described below.

The SelectorRadioButtonItem class

privateclass SelectorRadioButtonItem : ITemplate
{
...
}

SelectorRadioButtonItem implements the ITemplate interface. SelectorRadioButtonItem is used to control the rendering of the appropriate content for each row in the GridView.

Properties

To support rendering of the appropriate content for the SelectorRadioButtonField, the class has five properties, with associated private fields. These are:

Name. This is used as the "name" attribute of the HTML <input> field that is rendered for the radio button.

ValueDataField. This is the name of a column in the data source that will be bound to the GridView control. The value should be the name of the Primary Key column. This will be used as the value of the "value" attribute of the HTML <input> field that is rendered for the radio button.

OnClick. This is used as the value of the "onclick" attribute of the HTML <input> field that is rendered for the radio button. This allows a JavaScript function to be called when the user selects one of the radio buttons.

AttributeBindings. This is a Dictionary with string keys and string values. This can be used to add other data bound attributes to the HTML <input> field that is rendered for the radio button. The key of an AtrributeBindings item will be the name of the attribute that is rendered. This can be either a known attribute for an HTML <input> field, or it can be a custom attribute. The value of an AtrributeBindings item will be the name of a column in the data source that will be bound to the GridView control. The value of this column will be the value of the attribute that is rendered.

UnBoundAttributes. This is a Dictionary with string keys and string values. This can be used to add other attributes to the HTML <input> field that is rendered for the radio button. The key of an UnBoundAttributes item will be the name of the attribute that is rendered. This can be either a known attribute for an HTML <input> field, or it can be a custom attribute. The value of an UnBoundAttributes item will be the string value of the attribute that is rendered (i.e., it will be the same for each radio button that is rendered in the column of the GridView).

The ITemplate Interface

SelectorRadioButtonItem implements the ITemplate interface. The only member of the ITemplate interface is the InstantiateIn method. You do not need to call this method. The InstantiateIn method is invoked by ASP.NET when an instance of the template needs to be created inside its parent (container) control.

In the InstantiateIn method, we create a SelectorRadioButton control (see below) to be rendered as the content of the GridView column. This will be added to the child controls of the container control in which we are being rendered.

For the content of the column, we need to use a control that supports data binding. Our SelectorRadioButton control inherits from the HtmlInputRadioButton, which supports data binding. We add an event handler for the DataBinding event of the SelectorRadioButton control.

Using the values that have been set for the Name, OnClick, and UnBoundAttributes properties, we add attributes to the SelectorRadioButton control.

Data-binding

In the DataBinding event of the SelectorRadioButton control, we will add attributes to the SelectorRadioButton control for each of the bound fields.

First, we get the sender object of the event as a SelectorRadioButton. This allows us to access the properties of the SelectorRadioButton. We get the value of the NamingContainer property. As the SelectorRadioButton is being rendered in a GridView, the NamingContainer will be a GridViewRow. This allows us to access the DataItem property, which will be a DataRowView object for the bound DataTable.

We set the Value property of the SelectorRadioButton to the value of the column that is set as the ValueDataField.

We add attributes to the SelectorRadioButton control that has been added to AttributeBindings, with values from the bound fields.

The SelectorRadioButton class

privateclass SelectorRadioButton : HtmlInputRadioButton
{
...
}

SelectorRadioButton is used to render the content for each row in the GridView.

SelectorRadioButton inherits from the HtmlInputRadioButton class. SelectorRadioButton needs to be inherited at least from Control in order to support data-binding. Inheriting from HtmlControl adds support for the Attributes property. HtmlInputRadioButton is used as the base class as it already supports the Name and Value properties that we want to use.

We override the protectedRender method to control the precise rendering of the content.

All of the attributes that have been defined for the SelectorRadioButton are included in the <input> element that is rendered. This will include the "name" and "value" attributes that have been defined using the Name and Value properties of the HtmlInputRadioButton class.

Note: We cannot use HtmlInputRadioButton directly as ASP.NET will render each radio button with a unique name (fully qualified with all the container names), which does not allow us to determine the selected value associated with the radio button when the page is posted back.

The AttributeBindingsCollection and UnBoundAttributesCollection classes

AttributeBindingsCollection and UnBoundAttributesCollection are used to provide customised Dictionary classes for the AttributeBindings and UnBoundAttributes properties of the SelectorRadioButtonField class.

AttributeBindingsCollection and UnBoundAttributesCollection both inherit from Dictionary<string, string>. The only method added to these classes is a new Add method that hides the Add method of the Dictionary. This serves two purposes:

The names of the key and value parameters have customised names (attribute and dataField for AttributeBindingsCollection, and attribute and value for UnBoundAttributesCollection) and are documented with comments that appear in Intellisense. This improves developer understanding of how to use the AttributeBindings and UnBoundAttributes properties of the SelectorRadioButtonField class.

The attribute name that is used as the Dictionary key is validated to ensure it is not one of the attributes for which a specific property is provided (name, value, and onclick).

The SelectRadioButton JavaScript function

The SelectRadioButton JavaScript function (in the SelectorRadioButton.js code file) is used to select the instance of a radio button on a page with a specific name and value.

When the SelectorRadioButtonField is used in a GridView, each row in the HTML table that is rendered will have a radio button with the same value for the "name" attribute, and a "value" attribute with a value of the Primary Key of the record displayed in the table row. By default, when the containing page is displayed, none of the radio buttons will be selected. The SelectRadioButton function allows one of the radio buttons to be selected by default.

SelectorRadioButton.js is a reusable JavaScript file. It needs to be included on any page in your project that uses a GridView control with a SelectorRadioButtonField. You only need to add this code file once to your project.

Instead of creating a separate file for the SelectRadioButton JavaScript function, you can add this function to another JavaScript code file that is part of your project. The only requirement is that the SelectRadioButton JavaScript function is loaded for each page with a GridView that uses the SelectorRadioButtonField.

We use the getElementsByName method of the current document object, to get an array of all the radio buttons with the given radioButtonName.

Provided there is at least one radio button with this name, we iterate through the array of radio buttons and examine the value attribute of each. If the value attribute matches the given value parameter, then that radio button will be selected (checked attribute set to true).

If there is at least one radio button with the given name, but no radio buttons with the given value, then the first radio button with the given name will be selected.

If a radio button has been selected, the selected radio button is returned by the function.