September 2, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Advanced Data Reporting in ASP.NET

  • October 25, 2002
  • By Dino Esposito
  • Send Email »
  • More Articles »

Allowing the Selection of Multiple Items

The DataGrid control does not support the selection of multiple items in the current page, much less the whole data source. Nevertheless, a lot of Web sites out there provide this functionality. For example, Web sites that let the user create a mailbox show messages in a table of rows. Each row contains a check box for selection, and a link at the bottom of the page allows the user to execute actions on the selected rows. In ASP.NET, the table can easily be obtained using the DataGrid control. After you add an extra column with a check box and figure out how to expose the information behind a row, you are really close to creating a multiselection grid.

A multiselection grid is relatively easy to build as a constituent part of the page. You insert a DataGrid control with a templated column (to provide the check box) and then write all the necessary event handlers. In this section, I'll be doing something slightly different and more complex but a lot more reusable: I'll build a new DataGrid control that automatically provides the check box column, a custom footer with predefined functions, and a collection that returns all the items currently selected in the current page. I have indeed chosen a very fancy name for this new control: the SuperGrid control.

Properties of the SuperGrid Control

Just like the PowerGrid control of Chapter 5, the SuperGrid control is a grid that automatically provides advanced sorting and pagination. In addition, it places at your disposal the extra properties shown in Table 6-1.

Table 6-1 Multiselection Properties of the SuperGrid Control

PropertyDescription
AllowMultiSelectA Boolean value that enables and disables the multiselection feature. False by default.
AllowMultiSelectFooterA Boolean value that enables and disables a custom footer with grid-specific functions. It overrides the user-defined footer, if any. False by default.
SelectedItemsReturns a collection of DataGridItem objects, each of which corresponds to a selected item in the current page. Null by default.

The following directive is needed to enable the use of the SuperGrid control in ASP.NET pages. You can change the TagPrefix attribute when you want to.

<%@ Register TagPrefix="expo" Namespace="BWSLib" Assemb ly="SuperGrid" %>

The following code snippet, instead, demonstrates how to use the control. The output of the code is shown in Figure 6-6.

<expo:SuperGrid id="grid" runat="server" 
AutoGenerateColumns="false"
AllowMultiSelect="true"
AllowMultiSelectFooter="true"

DataKeyField="employeeid"
OnUpdateView="UpdateView">
<Columns>
<asp:TemplateColumn HeaderText="Name" SortExpre ssion="lastname">
<itemtemplate> <%# 
DataBinder.Eval(Container.DataItem, "titleo fcourtesy") + " <b>"
+ DataBinder.Eval(Container.DataItem, "last name") + "</b>, "
+ DataBinder.Eval(Container.DataItem, "firs tname") %>
</itemtemplate>
</asp:TemplateColumn>
<asp:BoundColumn DataField="title" HeaderText=" Position" 
SortExpression="title, employeeid" />
<asp:BoundColumn DataField="hiredate" HeaderTex t="Hired" 
SortExpression="hiredate, employeeid" 
DataFormatString="{0:d}" />
<asp:BoundColumn DataField="country" HeaderText ="Country" 
SortExpression="country" />
</Columns>
</expo:SuperGrid>

Figure 6-6 The SuperGrid control in action. The first column enables selection. The footer bar lets you clear all selections.

Layout of the SuperGrid Control

As you see in Figure 6-6, the DataGrid control has an extra column not mentioned in the code we just examined. It is a templated column that displays a check box. The SuperGrid control adds this column dynamically when the AllowMultiSelect property is set to true. In Figure 6-6, note the customized footer with an Unselect link button. You enable this control-specific footer by using the AllowMultiSelectFooter property. It overrides any footer you might have specified in the grid's declaration. The grid you create in the ASP.NET page is automatically and programmatically completed with a templated column and a footer when multiselection is enabled.

Adding the Select Column

To show a check box in the column's cells, you have two options. You can write a new custom column class or, more simply, you can create a dynamic templated column. (I discussed dynamic columns in Chapter 3.) The .NET Framework provides the Page object's LoadTemplate method, which takes a virtual (not physical) path to a user control (an ASCX file) and uses the control's contents to populate the column. You can also create column templates from in-memory strings by saving them to a temporary file. Finally, you can even create a dynamic template using a new class that inherits from ITemplate. This is how the select column is created in the sample.

The creation of the extra column takes place during the grid's initialization phase and is fired from within the Init event handler.

public SuperGrid() {
   
Init += new EventHandler(OnInit);
}

When the Init event is raised, the control has already been associated with the page, making it possible for you to access properties and methods on the Page object.

public void OnInit(Object sender, EventArgs e)
{
if (AllowMultiSelect)
AddSelectColumn();
}

AddSelectColumn is an internal member function that creates a column with two templates: ItemTemplate and FooterTemplate. The former provides the check box for selecting the row. The latter provides a custom button bar with selection commands such as Unselect. The following code listing demonstrates the creation of the select column:

private void AddSelectColumn()
{
TemplateColumn tc = new TemplateColumn();
tc.ItemStyle.BackColor = Color.SkyBlue;
tc.ItemTemplate = new SuperGridColumnTemplate();
Columns.AddAt(0, tc);
}

Adding the Footer Template

In our example, a footer template is needed to host the link buttons that will execute grid-specific actions—for example, deselecting all the selected items in the current page. The following code shows the steps for adding a custom footer to the select column. It creates an instance of an ITemplate-based class for the footer template and assigns the resulting object to the column's FooterTemplate property.

tc.FooterTemplate = new SuperGridFooterTemplate();

At this point in our example, the Unselect link button would appear at the bottom of the select column and look pretty dull, as shown in Figure 6-7.

Figure 6-7 A straightforward but not very visually stimulating link button.

You might want to manipulate the footer quite a bit to turn it into a more specific status bar with links, labels, and—why not—drop-down lists. I designed the SuperGrid control to provide a made-to-measure footer with an Unselect link button. This footer clears all other footer cells you might have. You enable the grid-specific footer by using the Boolean property AllowMultiSelectFooter. The next code listing shows how the SuperGrid control sets up the grid's footer to make it look like the footer in Figure 6-6.

if (itemType==ListItemType.Footer 
&& AllowMultiSelect 
&& AllowMultiSelectFooter)

{
// Force ShowFooter to true
ShowFooter = true;
 
// Removes all the cells but the first 
TableCell cell = e.Item.Cells[0];
for (int i=1; i<Columns.Count; i++)
e.Item.Cells.RemoveAt(1);
 
// Span the first cell to cover the whole grid's width
cell.ColumnSpan = Columns.Count;
 
   
}

The Unselect button in the built-in footer invokes the SuperGrid control's public method named ClearSelection. So to easily integrate your existing footer with the deselect feature of the SuperGrid control, add a new link button that explicitly invokes the ClearSelection method.

Binding the OnClick Event Handler

So far in our example, the link button defined in the footer template isn't bound to code. The link button declaration is incomplete and lacks an event handler for the OnClick attribute:

<asp:linkbutton runat=server text=Unselect id=lnkUnselect />

Unfortunately, you cannot bind the OnClick attribute by using a method within the template code. If you add an attribute such as OnClick=OnUnselect to the previous declaration, the control compiles successfully but a run-time error occurs as soon as you open the ASP.NET page. The rub is that the ASP.NET run time expects to find the definition of the method within the template. Having the method defined as a public member of the SuperGrid class is not enough. You can try inserting into the template string a block such as the following:

<script runat=server>
public void OnUnselect(object sender, EventArgs e)
{
// Do something

</script>

The problem, however, is just shifted. Now the ASP.NET run time locates the click handler but still fails with any object or method code you invoke that is not part of the template. What is really needed here is a binding between the link button in the footer template and code defined within the SuperGrid control. This code can't take the form of a declaration.

Any template is seen as a container control separated from the hosting page, so you cannot see methods and objects within the context of the DataGrid control, and you are denied access to the link button via its ID. The ID lnkSelect means nothing to the ASP.NET run time if it is called from within the grid's context. To get hold of the living instance of the link button, the SuperGrid code needs to search for it within the footer item container. This can be easily done in the ItemCreated handler when the item being created is the footer.

if (itemType==ListItemType.Footer && 
AllowMultiSelectFooter && AllowMultiSelect)
{
//Look for a link button called "lnkSelect" in the conte xt 
// of the grid item that represents the footer
LinkButton lb = (LinkButton) e.Item.FindControl("ln kSelect");
 
// Now you hold the living instance of the link 
//button in the footer and can bind it to any code in the 
// context of the SuperGrid control
lb.Click += new EventHandler(OnUnselect);
 
// Other code here...
}

Calling FindControl on the DataGridItem object that represents the footer template returns a valid LinkButton object with the specified ID. Utilizing the DataGridItem object is the key to bringing a reference to the link button in the context of the SuperGrid control. Now its Click event can be easily bound with any method of the SuperGrid control. The OnUnselect handler ends up calling the public method ClearSelection.

public void OnUnselect(Object sender, EventArgs e) {
ClearSelection();
}
 
public void ClearSelection()
{
foreach(DataGridItem dgi in Items)
{
// The check box is the first control in the first cell
CheckBox cb = (CheckBox) dgi.Cells[0].Controls[ 0];
cb.Checked = false;
}
}

Clearing the selection is as easy as locating the check box control in the controls hierarchy and unchecking it.

Retrieving the Selected Items

The SuperGrid control exposes the items selected in the current page by using a custom property named SelectedItems, which returns an array of DataGridItem objects. SelectedItems is implemented as a read-only data member. The get accessor walks through the grid's Items collection and adds to the ArrayList object only those items whose CheckBox control is checked.

public ArrayList SelectedItems {
get {
if (!AllowMultiSelect) return null;
 
ArrayList a = new ArrayList();
foreach(DataGridItem dgi in Items)
{
CheckBox cb = (CheckBox) dgi.Cells[0].Controls[0];
if (cb.Checked)
a.Add(dgi);
}
return a;
}
}

A client page uses the SelectedItems property as follows:

(Code Unavailable)

public void OnAddToCart(Object sender, EventArgs e)
{
foreach(DataGridItem dgi in grid.SelectedItems)
{
String strItemIndex = grid.DataKeys[dgi.ItemIndex].ToString();
ListItem li = new ListItem("ID=" + strItemIndex , strItemIndex);
if (!listboxCart.Items.Contains(li))
listboxCart.Items.Add(li);
}
   
}

Figure 6-8 (on page 195 in the next section) shows a sample page that adds the selected items to a cart. The cart is represented by a list box control that has some extra code for removing items. When the user adds items to the cart, the code also checks to see whether the item is already present in the cart.

Using the DataGridItem Object

Each displayed item in a DataGrid control is programmatically exposed using the DataGridItem class. This exposure applies to client rows as well as to nonclient items such as the header, footer, and pager bar. The client items are stored in the Items collection. The SelectedItems property of the SuperGrid control returns a subset of the Items collection. A DataGridItem object, though, is not a DataRow object and does not owe its content to a data source. How can you retrieve the actual row of data behind a DataGrid control item?

The DataGridItem has a number of interesting properties. One of the first that is likely to get your attention is ItemIndex, but it merely returns a 0based index indicating the position of the row in the page. Another cool property is DataSetIndex, which returns the absolute index of a row in the data source. You can then use this index on the DataTable object to selectively access the DataRow used to populate the grid item.

// ds is the DataSet that has just been filled up
DataTable dt = ds.Tables["MyTable"];
DataRow row = dt.Rows[dgi.DataSetIndex];

Alternatively, as you learned in previous chapters, you can use the ItemIndex property to access the key value for a row if you use both the DataKeyField and DataKeys properties.

You expect the DataGridItem property to represent only the row object you are searching. However, handle this property with extreme care because in a few event handlers, it is not yet initialized and points to null objects.

Maintaining Selections Across Pages

The SuperGrid control does not support selection across pages. The structure of the control and its way of using postback events makes effective coding difficult. Although you can implement selection across pages in a pageable control, I recommend that you code cross-page selection according to the design of your application.

The sample application shown in Figure 6-8 handles cross-page selection. It saves references to the items selected in a given page to an external control, for instance, a list box. This control is then used as an intermediate cart that serves as the real application repository for selections. By controlling the insert process (to prevent the addition of the same item twice) and adding the ability to remove items from the cart, you can obtain an effective selection solution without too much effort—and without spending too much time changing the state management policies of the DataGrid control. The full source for the supergrid.cs, multiselect.aspx, and multiselect.cs applications is available on the companion CD.



Click here for larger image

Figure 6-8 A multiple selection application that uses the SuperGrid control.





Page 2 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel