Master/Detail Display

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 BooksDB.mdb database is shown below. Clicking a "Select" link in the GridView retrieves and displays fields for the selected record in the DetailsView.

Book Master
 ID
SelectDB111
SelectDB222
SelectDB333
SelectDB444
SelectDB555
SelectGR111
SelectGR222
SelectGR333
SelectGR444
SelectGR555
SelectHW111
123
Book Detail
IDDB111
TitleOracle Database
TypeDatabase
AuthorK. Loney
Description Get thorough coverage of Oracle Database 10g from the most comprehensive reference available, published by Oracle Press. With in-depth details on all the new features, this powerhouse resource provides an overview of database architecture and Oracle Grid Computing technology, and covers SQL, SQL*Plus, PL/SQL, dynamic PL/SQL, object-oriented features, and Java programming in the Oracle environment. You'll also find valuable database administration and application development techniques, plus an alphabetical reference covering major Oracle commands, keywords, features, and functions, with cross-referencing of topics.
Price$69.99
Quantity10
Amount $699.90
On Sale

Figure 7-29. Display of master/detail records.

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, however, 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.

-- Master --

<asp:AccessDataSource id="id1" Runat="Server"
  DataFile="path"
  SelectCommand="SQL statement1"
/>

<asp:GridView id="id" Runat="Server"
  DataSourceID="id1"
  AllowGenerateSelectButton="False|True"
  DataKeyNames="field1,..."
  SelectedIndex="rowindex"
    SelectedRowStyle-"property="value"...
  ...
/>

-- Detail --

<asp:AccessDataSource id="id2" Runat="Server"
  DataFile="path"
  SelectCommand="SQL statement2">
	
  <SelectParameters>
    <asp:ControlParameter Name="parameter" ControlID="GridViewId" 
                          PropertyName="property"/>
  </SelectParameters>
	
</asp:AccessDataSource>

<asp:DetailsView id="id" Runat="Server"
  DataSourceID="id2"
  ...
/>

or

<asp:AccessDataSource id="id2" Runat="Server"
  DataFile="path"
  SelectCommand="SQL statement2">
  FilterExpression="field='@parameter'"
	
  <FilterParameters>
    <asp:ControlParameter Name="parameter" ControlID="GridViewId" 
                          PropertyName="property"/>
  </FilterParameters>
	
</asp:AccessDataSource>

<asp:DetailsView id="id" Runat="Server"
  DataSourceID="id2"
  ...
/>
Figure 7-30. General formats for master/detail display.

The GridView, which displays a set of master records, requires two 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 one or more data fields 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="BookSource1" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT * FROM Books ORDER BY BookID"/>
	
<asp:GridView id="BookGrid" DataSourceID="BookSource1" Runat="Server"
  AutoGenerateSelectButton="True"
  DataKeyNames="BookID"
  SelectedIndex="0"
  SelectedRowStyle-BackColor="#F0F0F0"
  ...
</asp:GridView>
Listing 7-25. Code to link GridView with AccessDataSource.

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="BookSource2" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT * FROM Books WHERE BookID = @BookID">
	
  <SelectParameters>
    <asp:ControlParameter Name="BookID" ControlID="BookGrid" 
                          PropertyName="SelectedValue"/>
  </SelectParameters>
		
</asp:AccessDataSource>

<asp:DetailsView id="BookView" DataSourceID="BookSource2" Runat="Server"
  ...
</asp:DetailsView>
Listing 7-26. Code to link DetailsView with AccessDataSource.

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 a BookID value matching the BookID 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 used.

SelectCommand="SELECT * FROM Books WHERE BookID = @BookID">
Listing 7-27. Parameterized SelectCommand for AccessDataSource.

The parameter @BookID 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="BookID" ControlID="BookGrid"
                        PropertyName="SelectedValue"/>
</SelectParameters>
Listing 7-28. Control parameters for a SelectCommand for AccessDataSource for DetailsView.

Name="BookID" in the ControlParameter points to the @BookID 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="BookGrid" identifies the GridView control whose SelectedValue supplies the value for the parameter. That is, the selected BookID from the GridView supplies the value for the @BookID parameter of the SelectCommand for the DetailsView.

Filters for SelectCommands

A slightly different tactic can be taken to match the DetailsView display with a book selected in the GridView. The DetailsView's AccessDataSource also can be coded as follows.

<asp:AccessDataSource id="BookSource2" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT * FROM Books"
  FilterExpression="BookID = '@BookID'">
	
  <FilterParameters>
    <asp:ControlParameter Name="BookID" ControlID="BookGrid" 
                          PropertyName="SelectedValue"/>
  </FilterParameters>
	 
</asp:AccessDataSource>

<asp:DetailsView id="BookView" DataSourceID="BookSource2" Runat="Server"
  ...
</asp:DetailsView>
Listing 7-29. Filtering a SelectCommand for AccessDataSource.

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 for string data) that is retrieved from an <asp:ControlParameter> coded in the <FilterParameters> section of the data source control. A ControlParameter identifies a data value from a different control for use in the current control for filtering SelectCommands.

In the present example, the FilterExpression identifies "BookID='@BookID'" as the filter, supplying the parameter @BookID 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="BookID" in the current example. Thus, the BookID value for the record selected in the GridView (the PropertyName="SelectedValue" property of the ControlID="BookGrid" 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.

Whether you use SelectParameters to supply a value for a SELECT statement with a WHERE clause (as in the first example), or you use FilterParameters to compose a SELECT statement without a WHERE clause (as in the second example), is mostly 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; margin-right:20px" CSS style settings so that both controls appear side by side.

<!-- Master Coding -->

<asp:AccessDataSource id="BookSource1" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT * FROM Books ORDER BY BookID"/>

<asp:GridView id="BookGrid" DataSourceID="BookSource1" Runat="Server"
  AutoGenerateSelectButton="True"
  DataKeyNames="BookID"
  SelectedIndex="0"
  AutoGenerateColumns="False"
  AllowPaging="True"
  PageSize="11"
  CellPadding="1"
  Caption="<b>Book Master</b>"
  CaptionAlign="Left"
  HeaderStyle-BackColor="#E0E0E0"
  SelectedRowStyle-BackColor="#F0F0F0"
  Style="float:left; margin-right:20px"
  Width="110">
  
  <Columns>
  
  <asp:BoundField
    DataField="BookID"
    HeaderText="ID"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"/>

  </Columns>

</asp:GridView>


<!-- Detail Coding -->

<asp:AccessDataSource id="BookSource2" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT * FROM Books WHERE BookID = @BookID">
  
  <SelectParameters>
    <asp:ControlParameter Name="BookID" ControlId="BookGrid" 
                          PropertyName="SelectedValue"/>
  </SelectParameters>
  
</asp:AccessDataSource>

<asp:DetailsView id="BookView" DataSourceID="BookSource2" Runat="Server"
  AutoGenerateRows="False"
  BorderWidth="1"
  CellPadding="1"
  GridLines="None"
  Caption="<b>Book Detail</b>"
  CaptionAlign="Left"
  Width="385"
  RowStyle-VerticalAlign="Top">
  
  <Fields>
  
  <asp:BoundField
    DataField="BookID"
    HeaderText="ID"
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"/>
  
  <asp:BoundField
    DataField="BookTitle"
    HeaderText="Title"
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"/>
  
  <asp:BoundField
    DataField="BookType"
    HeaderText="Type"
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"/>	
  
  <asp:BoundField
    DataField="BookAuthor"
    HeaderText="Author"
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"/>	
  
  <asp:TemplateField
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="10pt">
    <HeaderTemplate>
      Description
    </HeaderTemplate>
    <ItemTemplate>
      <asp:Image Runat="Server"
        Style="float:left"
        ImageUrl='<%# "../BookPictures/" & Eval("BookID") & ".jpg" %>'/>
      <asp:Label Runat="Server"
        Style="overflow:auto; width:195px; height:105px"
        Text='<%# Eval("BookDescription") %>'/>
    </ItemTemplate>
  </asp:TemplateField>
  
  <asp:BoundField
    DataField="BookPrice"
    HeaderText="Price"
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"
    HtmlEncode="False"
    DataFormatString="{0:C}"/>
  
  <asp:BoundField
    DataField="BookQty"
    HeaderText="Quantity"
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"
    HtmlEncode="False"
    DataFormatString="{0:D}"/>
  
  <asp:TemplateField
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt">
    <HeaderTemplate>
      Amount
    </HeaderTemplate>
    <ItemTemplate>
      <asp:Label Runat="Server"
        Text='<%# FormatCurrency(Eval("BookPrice") * Eval("BookQty")) %>'/>
    </ItemTemplate>
  </asp:TemplateField>
  
  <asp:CheckBoxField
    DataField="BookSale"
    HeaderText="On Sale"
    HeaderStyle-BackColor="#E0E0E0"
    HeaderStyle-Font-Bold="True"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"/>
  
  </Fields>

</asp:DetailsView>
Listing 7-30. Code for master/detail display of GridView and 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.

<asp:ButtonField
  ButtonType="Link|Button|Image"
  Text="string"
  ImageUrl="url"
  CommandName="Select"
  ItemStyle-property="value"
/>
Figure 7-31. General format for <asp:ButtonField> control used as selection link.

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.

It is not necessary to use the special <asp:ButtonField> control to supply a standard button for making selections. You can use a TemplateField enclosing a standard <asp:Button> control. Just remember to set the CommandName property of the button to "Select". The following output is a duplicate of the previous master/detail application in which a button replaces the text "Select" links for displaying associated detail records. Here, a standard Button (rather than a ButtonField) is used to trigger selections.

Book Master
 ID
DB111
DB222
DB333
DB444
DB555
GR111
GR222
GR333
GR444
GR555
HW111
123
Book Detail
IDDB111
TitleOracle Database
TypeDatabase
AuthorK. Loney
Description Get thorough coverage of Oracle Database 10g from the most comprehensive reference available, published by Oracle Press. With in-depth details on all the new features, this powerhouse resource provides an overview of database architecture and Oracle Grid Computing technology, and covers SQL, SQL*Plus, PL/SQL, dynamic PL/SQL, object-oriented features, and Java programming in the Oracle environment. You'll also find valuable database administration and application development techniques, plus an alphabetical reference covering major Oracle commands, keywords, features, and functions, with cross-referencing of topics.
Price$69.99
Quantity10
Amount $699.90
On Sale

Figure 7-32. Display of master/detail records.

The following partial code shows the recoded sections of the master/detail application to create a column of Buttons for display of detail records.

<asp:GridView id="BookGrid" DataSourceID="BookSource1" Runat="Server"
  AutoGenerateColumns="False"
  AutoGenerateSelectButtons="False"
  DataKeyNames="BookID"
  SelectedIndex="0"
  ...>

  <Columns>

  <asp:TemplateField>
    <ItemTemplate>
      <asp:Button Text="Select" CommandName="Select" 
       Font-Size="7pt" Runat="Server"/>
    </ItemTemplate>
  </asp:TemplateField>
  
  <asp:BoundField
    DataField="BookID"
    HeaderText="ID"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="11pt"/>
  
  </Columns>

</asp:GridView>
Listing 7-31. Code for Button column of GridView to selecte records for DetailsView display.

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 books from the Books table; a FormView is used for drill-down display of a selected record.

Book Master
IDTitle
DB111 Oracle Database
DB222 Databases in Depth
DB333 Database Processing
DB444 Access Database Design
DB555 SQL Server 2005
GR111 Adobe Photoshop CS2
GR222 Learning Web Design
GR333 Macromedia Flash Professional
GR444 Digital Photographer Handbook
GR555 Creating Motion Graphics
123
Book Detail
Database - DB111
Oracle Database
K. Loney
$69.99
10


Get thorough coverage of Oracle Database 10g from the most comprehensive reference available, published by Oracle Press. With in-depth details on all the new features, this powerhouse resource provides an overview of database architecture and Oracle Grid Computing technology, and covers SQL, SQL*Plus, PL/SQL, dynamic PL/SQL, object-oriented features, and Java programming in the Oracle environment. You'll also find valuable database administration and application development techniques, plus an alphabetical reference covering major Oracle commands, keywords, features, and functions, with cross-referencing of topics.

Figure 7-33. Display of master/detail records with FormView.

A slightly different technique is used for selecting records from the GridView for display in the FormView. In this example, a separate button column is not used to display selection buttons; rather, a TemplateField containing an <asp:LinkButton> control both displays BookIDs and configures them as command links for selecting books. 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. The CommandName for the LinkButton must be "Select" to tie the BookID to the SelectCommand property of the AccessDataSource for the FormView. 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".

<asp:AccessDataSource id="BookSource1" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT * FROM Books ORDER BY BookID"/>

<asp:GridView id="BookGrid" DataSourceID="BookSource1" Runat="Server"
  AutoGenerateSelectButton="False"
  DataKeyNames="BookID"
  SelectedIndex="0"
  AutoGenerateColumns="False"
  AllowPaging="True"
  PageSize="10"
  CellPadding="1"
  Caption="<b>Book Master</b>"
  CaptionAlign="Left"
  HeaderStyle-BackColor="#E0E0E0"
  SelectedRowStyle-BackColor="#F0F0F0"
  Style="float:left; margin-right:10px"
  Width="220">

  <Columns>

  <asp:TemplateField 
    HeaderText="ID"
    HeaderStyle-Width="40px"
    HeaderStyle-Font-Size="10pt"
    ItemStyle-Font-Size="10pt">
    <ItemTemplate>
    <asp:LinkButton Runat="Server"
      Text='<%# Eval("BookID") %>'
      CommandName="Select"/>
    </ItemTemplate>
  </asp:TemplateField>
  
  <asp:BoundField
    DataField="BookTitle"
    HeaderText="Title"
    HeaderStyle-Font-Size="11pt"
    ItemStyle-Font-Size="10pt"/>

  </Columns>

</asp:GridView>
Listing 7-32. Code for GridView template column for master/detail link buttons.

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="BookSource2" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT * FROM Books Where BookID = @BookID">
	
  <SelectParameters>
    <asp:ControlParameter Name="BookID" ControlId="BookGrid" 
                          PropertyName="SelectedValue"/>
  </SelectParameters>
		
</asp:AccessDataSource>

<asp:FormView id="BookForm" DataSourceID="BookSource2"  Runat="Server"
  AllowPaging="True"
  GridLines="None"
  Width="280"
  HeaderStyle-Font-Bold="True"
  RowStyle-Font-Size="10pt">
	
  <HeaderTemplate>
    Book Detail
  </HeaderTemplate>
  
  <ItemTemplate>
    <asp:Image Style="float:left; margin-right:10px" Runat="Server"
    ImageUrl='<%# "../BookPictures/" & Eval("BookID") & ".jpg" %>'/>
    
    <asp:Label Text='<%# Eval("BookType") & " - " %>' Runat="Server"/>
    <asp:Label Text='<%# Eval("BookID") %>' Runat="Server"/><br/>
    <asp:Label Text='<%# Eval("BookTitle") %>' Font-Bold="True" Runat="Server"/><br/>
    <asp:Label Text='<%# Eval("BookAuthor") %>' Runat="Server"/><br/>
    <asp:Label Text='<%# Get_Sale_Price(Eval("BookPrice"), Eval("BookSale")) %>' Runat="Server"/><br/>
    <asp:Label Text='<%# String.Format("{0:D}", Eval("BookQty")) %>' Runat="Server"/>
    <asp:CheckBox Checked='<%# Eval("BookSale") %>' Enabled="False" Text="On Sale" Runat="Server"/><br/>
    <br clear="left"><br/>
    
    <asp:Label Runat="Server"
      Width="280" Height="103" Style="overflow:auto"
      Text='<%# Eval("BookDescription") %>'/><br/>
  </ItemTemplate>
	
</asp:FormView>
Listing 7-33. Code for FormView used for master/detail display.

As was done in a previous FormView, display of book prices results from a function call to determine if a discounted price should be displayed. The same Get_Sale_Price function is used in this application.

<SCRIPT Runat="Server">

Function Get_Sale_Price (Price As Decimal, Sale As Boolean)

  If Sale = True Then
    Price = Price - (.10 * Price)
    Return String.Format("<span style=""color:red"">{0:C} " & _
                         "<b>Special</b></span>", Price)
  Else
    Return String.Format("{0:C}", Price)
  End If

End Function

</SCRIPT>
Listing 7-34. Code for Get_Sale_Price function called from FormView.