By pairing an <asp:GridView> control with an <asp:DetailsView> control you can display a set of master and detail records from a data source. A master list of records is presented in the GridView. Each record is accompanied by a link or a button for selecting that row. On the basis of this selection, the DetailsView displays data fields associated with this same record. An example master/detail display for the eCommerce.mdb database is shown below. Clicking a "Select" link in the GridView retrieves and displays fields for the selected record in the DetailsView.
Linking Display and Data Source Controls
The GridView and DetailsView controls are linked separately to the same data source through their respective <asp:AccessDataSource> controls. Special properties of these controls associate the record chosen from the GridView with the record displayed in the DetailsView. The general formats for setting up master/detail links among these controls are shown below.
The GridView, which displays a set of master records requires, in addition to its DataSourceID property that points to its data source control, two additional properties. The AllowGenerateSelectButton="True" property produces a column of "Select" link buttons (displayed as text links) for selecting individual rows of the GridView. The DataKeyNames="fieldname" property identifies a data field in the data source whose values are associated with the link buttons, and which are used to retrieve matching records for display in the DetailsView. Normally, the DataKeyNames value is the unique record key field in the data source, although any field or combination of fields, separated by commas, can be identified. It is important to remember that a clicked link supplies the GridView's SelectedValue property, needed for locating the matching record for the DetailsView.
The AccessDataSource associated with the GridView is coded in standard fashion, providing a path to the DataFile and a SelectCommand to retrieve a set of records. Partial coding for the GridView and its AccessDataSource to produce the example display is shown below.
<asp:AccessDataSource id="Source1" Runat="Server" DataFile="../Databases/eCommerce.mdb" SelectCommand="SELECT * FROM Products"/> <asp:GridView id="ProductsGrid" DataSourceID="Source1" Runat="Server" AutoGenerateSelectButton="True" DataKeyNames="ItemNumber" SelectedIndex="0" SelectedRowStyle-BackColor="#F0F0F0" ... </asp:GridView>
The SelectedIndex property of the GridView identifies a row that is selected by clicking the "Select" button. When the GridView is initially displayed, no rows are selected, even though the accompanying DetailsView displays, by default, the first record from the same data source. In this example, selected rows in the GridView are "highlighted" with a shaded background. Therefore, the SelectedIndex property is initially set to point to this first row (row index = 0) so that it will be highlighted when the GridView is first displayed. Without this special row formatting the SelectedIndex property is not required.
A separate AccessDataSource is associated with the DetailsView. It requires DataFile and SelectCommand properties to link to the same data source used for the GridView. Also, it requires additional properties and controls in order to display details for the record selected from the GridView. Partial coding for the DetailsView and its AccessDataSource are shown below.
<asp:AccessDataSource id="Source2" Runat="Server" DataFile="../Databases/eCommerce.mdb" SelectCommand="SELECT * FROM Products WHERE ItemNumber = @ItemNumber"> <SelectParameters> <asp:ControlParameter Name="ItemNumber" ControlID="ProductsGrid" PropertyName="SelectedValue"/> </SelectParameters> </asp:AccessDataSource> <asp:DetailsView id="ProductsView" DataSourceID="Source2" Runat="Server" ... </asp:DetailsView>
Note that the AccessDataSource is coded as separate opening and closing tags inside of which are tags to associate a displayed record with the record selected from the GridView.
Parameters for SelectCommands
In order to retrieve the record matching the one selected in the GridView, the AccessDataSource for the DetailsView must issue a SelectCommand to retrieve the record matching the one selected in the GridView. In this example, the matching record is one with an ItemNumber value matching the ItemNumber selected in the GridView.
The SelectCommand issued through the AccessDataSource for the DetailsView cannot know in advance which record to select. It depends on which record is selected in the GridView. Therefore, a parameterized SelectCommand is provided.
SelectCommand="SELECT * FROM Products WHERE ItemNumber = @ItemNumber">
The parameter @ItemNumber is a placeholder for a value. Parameters are programmer-supplied names prefixed with the "@" character. The value of a parameter comes from a source identified in an <asp:ControlParameter> control coded inside a <SelectParameters> tag enclosed in the AccessDataSource.
<SelectParameters> <asp:ControlParameter Name="ItemNumber" ControlID="ProductsGrid" PropertyName="SelectedValue"/> </SelectParameters>
Name="ItemNumber" in the ControlParameter points to the @ItemNumber parameter of the same name in the SelectCommand, indicating that this ControlParameter supplies the replacement value for the SelectCommand parameter. A ControlID identifies the control which is the source of this value, and PropertyName gives the control's property setting whose value becomes the replacement value for the parameter. In this example, ControlID="ProductsGrid identifies the GridView control whose PropertyName="SelectedValue" supplies the value for the parameter. That is, the selected ItemNumber from the GridView (its SelectedValue property) supplies the value for the @ItemNumber parameter of the SelectCommand for the DetailsView.
Filters for SelectCommands
A slightly different tactic can be taken to match the DetailsView display with a product selected in the GridView. The DetailsView's AccessDataSource also can be coded as follows.
<asp:AccessDataSource id="Source2" Runat="Server" DataFile="../Databases/eCommerce.mdb" SelectCommand="SELECT * FROM Products" FilterExpression="ItemNumber = '@ItemNumber'"> <FilterParameters> <asp:ControlParameter Name="ItemNumber" ControlID="ProductsGrid" PropertyName="SelectedValue"/> </FilterParameters> </asp:AccessDataSource> <asp:DetailsView id="ProductsView" DataSourceID="Source2" Runat="Server" ... </asp:DetailsView>
A FilterExpression for a SelectCommand is used to compose a WHERE clause for the SELECT statement. The expression contains a parameterized value (in single quotes) that is retrieved from a ControlParameter coded in the <FilterParameters> section of the data source control. This section contains one or more <asp:ControlParameter> controls to identify data values from a different control for use in the current control for filtering SelectCommands.
In the present example, the FilterExpression identifies "ItemNumber=@ItemNumber" as the filter, supplying the parameter @ItemNumber whose replacement value is used to uniquely identify the record to be retrieved. This parameter value is linked to a matching name in a <asp:ControlParameter> control, namely Name="ItemNumber" in the current example. Thus, the ItemNumber value for the record selected in the GridView (the PropertyName="SelectedValue" property of the ControlID="ProductsGrid" control) becomes the value used in a WHERE clause to filter the SELECT statement in the SelectCommand for the DetailsView, thereby retrieving the matching record.
A WHERE clause can be composed and appended to a SELECT statement by supplying a FilterExpression along with a set of FilterParameters. An alternative is to code the WHERE clause with a value-replacement parameter directly in the SELECT statement itself, and to supply the parameter value through a ControlParameter coded in a set of SelectParameters. This choice of approach normally is a matter of programmer preference.
Master/Detail Coding
Complete coding for the master/detail display at the top of this page is shown below. Much of the coding is the same as for individual GridView and DetailsView controls described in previous tutorials. One of the styling differences is in setting the background color of selected rows in the GridView with the SelectedRowStyle-BackColor property. Also, the GridView has a Style="float:left" CSS style settings so that both controls appear side by side.
<!-- Master Coding --> <asp:AccessDataSource id="Source1" Runat="Server" DataFile="../Databases/eCommerce.mdb" SelectCommand="SELECT * FROM Products"/> <asp:GridView id="ProductsGrid" DataSourceID="Source1" Runat="Server" Style="float:left" AutoGenerateSelectButton="True" DataKeyNames="ItemNumber" SelectedIndex="0" AutoGenerateColumns="False" AllowPaging="True" PageSize="5" CellPadding="3" BorderWidth="3" Width="320" Caption="Products Master" CaptionAlign="Left" BorderStyle="Ridge" HeaderStyle-BackColor="#E0E0E0" SelectedRowStyle-BackColor="#F0F0F0" ItemStyle-Wrap="False"> <Columns> <asp:BoundField DataField="ItemNumber" HeaderText="Number"/> <asp:BoundField DataField="ItemName" HeaderText="Name"/> </Columns> </asp:GridView> <!-- Detail Coding --> <asp:AccessDataSource id="Source2" Runat="Server" DataFile="../Databases/eCommerce.mdb" SelectCommand="SELECT * FROM Products WHERE ItemNumber = @ItemNumber"> <SelectParameters> <asp:ControlParameter Name="ItemNumber" ControlID="ProductsGrid" PropertyName="SelectedValue"/> </SelectParameters> </asp:AccessDataSource> <asp:DetailsView id="ProductsView" DataSourceID="Source2" Runat="Server" AutoGenerateRows="False" BorderWidth="3" CellPadding="3" GridLines="None" Caption="Products Detail" CaptionAlign="Left" BorderStyle="Ridge" HeaderText="Products Item" HeaderStyle-Font-Bold="True" HeaderStyle-HorizontalAlign="Center" HeaderStyle-BackColor="#E0E0E0" RowStyle-VerticalAlign="Top"> <Fields> <asp:BoundField DataField="ItemNumber" HeaderText="Item Number" HeaderStyle-BackColor="#E0E0E0" HeaderStyle-Font-Bold="True" HeaderStyle-Wrap="False"/> <asp:BoundField DataField="ItemName" HeaderText="Item Name" HeaderStyle-BackColor="#E0E0E0"/> <asp:BoundField DataField="ItemType" HeaderText="Item Type" HeaderStyle-BackColor="#E0E0E0"/> <asp:BoundField DataField="ItemSupplier" HeaderText="Item Supplier" HeaderStyle-BackColor="#E0E0E0"/> <asp:TemplateField HeaderStyle-BackColor="#E0E0E0" ItemStyle-Font-Size="9pt"> <HeaderTemplate> Item Description </HeaderTemplate> <ItemTemplate> <asp:Image Runat="Server" Style="float:left" ImageUrl='<%# "../Pictures/" & Eval("ItemNumber") & ".jpg" %>'/> <asp:Label Runat="Server" Style="overflow:auto; width:150px; height:120px" Text='<%# Eval("ItemDescription") %>'/> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="ItemPrice" HeaderText="Item Price" HeaderStyle-BackColor="#E0E0E0" DataFormatString="{0:C}"/> <asp:BoundField DataField="ItemQuantity" HeaderText="Item Quantity" HeaderStyle-BackColor="#E0E0E0"/> <asp:TemplateField HeaderStyle-BackColor="#E0E0E0"> <HeaderTemplate> Item Amount </HeaderTemplate> <ItemTemplate> <asp:Label Runat="Server" Text='<%# FormatCurrency(Eval("ItemPrice") * Eval("ItemQuantity")) %>'/> </ItemTemplate> </asp:TemplateField> <asp:CheckBoxField DataField="ItemSale" HeaderText="On Sale" HeaderStyle-BackColor="#E0E0E0"/> </Fields> </asp:DetailsView>
Select Button Types
When AutoGenerateSelectButton="True" is specified for a GridView, a text "Select" button is created for each row of records. This is the default "Link" style for the buttons. You can, instead, choose a different visual presentation of buttons by coding AutoGenerateSelectButton="False" (or by not coding the property setting) and creating your own button column.
A column of buttons is added to the GridView by creating an <asp:ButtonField> control. The properties set for this control for use as a "Select" button are shown below.
The ButtonType property sets the button type. Use "Button" for a standard button, along with a Text value to serve as the label for the button. Use "Image" to indicate a graphic button, along with an ImageUrl property to give the URL for the image. In both cases, a CommandName="Select" property must be coded for the button to issue the necessary Select command so that post-back to the server retrieves the appropriate record for viewing in the DetailsView display.
The following output is a duplicate of the previous master/detail application in which a button column replaces the "Select" links for displaying associated detail records.
The following partial code shows the recoded sections of the master/detail application to create a ButtonField column for display of detail records.
<asp:GridView id="ProductsGrid" DataSourceID="Source1" Runat="Server" AutoGenerateColumns="False" AutoGenerateSelectButtons="False" ...> <Columns> <asp:ButtonField ButtonType="Button" Text="Select" CommandName="Select" ItemStyle-HorizontalAlign="Center"/> <asp:BoundField DataField="ItemName" HeaderText="Name"/> </Columns> </asp:GridView>
Master/Detail Layout with FormView Control
A FormView control can be used in place of a DetailsView for setting up master/detail display arrangments. The following example uses a GridView for displaying and selecting all products from the Products table; a FormView is used for drill-down display of a selected record.
A slightly different technique is used for selecting records from the GridView for display in the FormView. In this example, a separate ButtonField is not used to display selection buttons; rather, a TemplateField containing an <asp:LinkButton> control both displays item numbers and configures them as command links for selecting products. Changes to previous GridView coding include setting AutoGenerateSelectButton="False" (the default setting which also can be effected by not coding the property) and including the TemplateField in place of a ButtonField. The CommandName for the LinkButton must be "Select" to tie the ItemNumber to the SelectCommand property of the AccessDataSource for the FormView.
<asp:GridView id="ProductsGrid" DataSourceID="Source1" Runat="Server" AutoGenerateColumns="False" AutoGenerateSelectButtons="False" ...> <Columns> <asp:TemplateField HeaderText="Number" HeaderStyle-Width="60px"> <ItemTemplate> <asp:LinkButton Runat="Server" Text='<%# Eval("ItemNumber") %>' CommandName="Select"/> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="ItemName" HeaderText="Name"/> </Columns> </asp:GridView>
Code for the FormView is shown below. Its coding is similar to previous FormView displays except that its AccessDataSource is tied to record selections made from the GridView.
<asp:AccessDataSource id="FormSource" Runat="Server" DataFile="../Databases/eCommerce.mdb" SelectCommand="SELECT * FROM Products WHERE ItemNumber = @ItemNumber"> <SelectParameters> <asp:ControlParameter Name="ItemNumber" ControlId="ProductsGrid" PropertyName="SelectedValue"/> </SelectParameters> </asp:AccessDataSource> <asp:FormView id="FormView" DataSourceID="FormSource" Runat="Server" GridLines="None" Caption="Products Form" CaptionAlign="Left" BorderStyle="Ridge" BorderWidth="3px" Width="430px"> <ItemTemplate> <table border="0" cellpadding="0"> <tr> <th style="text-align:left; background-color:#E0E0E0">Number</th> <td><asp:Label Text='<%# Eval("ItemNumber") %>' Runat="Server"/></td> </tr> <tr> <th style="text-align:left; background-color:#E0E0E0">Type</th> <td><asp:Label Text='<%# Eval("ItemType") %>' Runat="Server"/></td> </tr> <tr> <th style="text-align:left; background-color:#E0E0E0">Name</th> <td><asp:Label Text='<%# Eval("ItemName") %>' Runat="Server"/></td> </tr> <tr> <th style="text-align:left; background-color:#E0E0E0">Supplier</th> <td><asp:Label Text='<%# Eval("ItemSupplier") %>' Runat="Server"/></td> </tr> <tr> <th style="text-align:left; background-color:#E0E0E0; vertical-align:top">Description</th> <td><asp:Image Style="float:left" Runat="Server" ImageUrl='<%# "../Pictures/" & Eval("ItemNumber") & ".jpg" %>'/> <asp:Label Runat="Server" Style="overflow:auto; width:220px; height:120px; font-size:9pt" Text='<%# Eval("ItemDescription") %>'/></td> </tr> <tr> <th style="text-align:left; background-color:#E0E0E0">Price</th> <td><asp:Label Text='<%# FormatCurrency(Eval("ItemPrice")) %>' Runat="Server"/></td> </tr> <tr> <th style="text-align:left; background-color:#E0E0E0">Quantity</th> <td><asp:Label Text='<%# Eval("ItemQuantity") %>' Runat="Server"/></td> </tr> <tr> <th style="text-align:left; background-color:#E0E0E0">Sale</th> <td><asp:CheckBox Checked='<%# Eval("ItemSale") %>' Enabled="False" Runat="Server"/></td> </tr> </table> </ItemTemplate> </asp:FormView>
Use of a TemplateField to make LinkButtons out of data values extracted from a database is not limited to use with a FormView for displaying detail records. A TemplateField can be substituted for the above ButtonField when displaying detail records in a DetailsView. An advantage of using a TemplateField is that it does not produce the extra column of links displayed when using a ButtonField or when links are automatically produced with AutoGenerateSelectButtons="True".