The GridView server control exposes the following three important events that meet that description, as shown in Listing 20-1 : ❑ SelectedIndexChanged : The GridView server control rai
Trang 1Using UpdatePanel in User
Controls and Custom
Controls The previous chapter developed two partial-rendering-enabled custom controls named
BaseMasterDetailControl and BaseMasterDetailControl2 , which I will use in this chapter
to develop partial-rendering-enabled custom server controls I’ll then use examples to show you how to use ASP.NET AJAX partial page rendering in your own Web applications
MasterDetailControl
MasterDetailControl is a server control that inherits from BaseMasterDetailControl2 and extends its functionality to use the ASP.NET GridView as a master server control, as shown in Listing 20-1
Listing 20-1: The MasterDetailControl Server Control
Trang 3{ EnsureChildControls();
((GridView)Master).DataKeyNames = value;
((DetailsView)Detail).DataKeyNames = value;
} }
protected override void Master_DataBound(object sender, EventArgs e) {
for (int i = 0; i < ((GridView)Master).Rows.Count; i++) {
if (((GridView)Master).DataKeys[i].Value == this.SelectedValue) {
((GridView)Master).SelectedIndex = i;
break;
} }
((GridView)Master).SelectedIndex = -1;
Master_SelectedIndexChanged(null, null);
} }
protected virtual void Master_SelectedIndexChanged(object sender, EventArgs e) {
if (((GridView)Master).SelectedIndex == -1) this.Detail.Visible = false;
else this.Detail.Visible = true;
this.SelectedValue = ((GridView)Master).SelectedValue;
UpdateDetail(sender, e);
} }}
I’ll discuss the methods and properties of the MasterDetailControl server control in the following sections
CreateBaseDataBoundControlMaster
As Listing 20-1 shows, the MasterDetailControl server control overrides the
CreateBaseDataBoundControlMaster method of its base class to create and return a GridView server control as the master server control As you can see, this method instantiates a GridView server control and sets its AllowPaging , AllowSorting , AutoGenerateColumns , and AutoGenerateSelectButton properties
Trang 4RegisterMasterEventHandlers
The main responsibility of the RegisterMasterEventHandlers method is to register event handlers
for those events of the master server control that require the detail server control to update The
GridView server control exposes the following three important events that meet that description, as
shown in Listing 20-1 :
❑ SelectedIndexChanged : The GridView server control raises this event when the end user
selects a new record from the records that the control is displaying Since the detail server control
displays the details of the selected record, every time a new record is selected — that is, every
time the SelectedIndexChanged event is raised — the detail server control must be updated
with the details of the newly selected record Because of this, the MasterDetailControl
registers a method named Master_SelectedIndexChanged as an event handler for the
SelectedIndexChanged event of the GridView server control:
((GridView)Master).SelectedIndexChanged +=
new EventHandler(Master_SelectedIndexChanged);
❑ PageIndexChanged : The GridView server control raises this event when the end user clicks an
element in the pager user interface to display a new page of records Since the new page of
records may not include the selected record, you need to hide the detail server control until the
end user makes a new selection That is why the MasterDetailControl registers a method
named Master_ResetSelectedValue as an event handler for the PageIndexChanged event of
the GridView server control:
((GridView)Master).PageIndexChanged +=
new EventHandler(Master_ResetSelectedValue);
❑ Sorted : The GridView server control raises this event when the end user clicks the header
text of a column to sort the displayed records Again, the newly sorted records may not
include the selected record, so you need to hide the detail server control That is why the
MasterDetailControl registers the Master_ResetSelectedValue method as an event
handler for the Sorted event of the GridView server control:
((GridView)Master).Sorted += new EventHandler(Master_ResetSelectedValue);
Master_SelectedIndexChanged
As you can see from Listing 20-1 , this method hides the detail server control if the SelectedIndex
property of the master server control is set to -1 — that is, if no record is selected There is no point in
rendering the detail server control if there is no selected record to display:
Trang 5The MasterDetailControl inherits the SelectedValue property from the BaseMasterDetailControl
As Listing 20-1 shows, this property stores its value in the view state for future reference It is necessary to store the selected record in the view state because the following requests may end up rebinding the
GridView server control and consequently resetting the SelectedValue property of the control In such situations, you can retrieve the selected value from the view state and assign it to the SelectedValue property of the GridView server control after rebinding the control if the control still contains the selected record
As Listing 20-1 shows, the Master_SelectedIndexChanged method finally calls the UpdateDetail method to update the detail server control This is necessary because a new record has been selected
MasterDetailControl inherits the UpdateDetail method from its base class — that is, from the
BaseMasterDetailControl As you can see from Listing 20-1 , this method first calls the DataBind method on the detail server control to rebind the control and consequently to retrieve fresh data from the underlying data store:
for (int i = 0; i < ((GridView)Master).Rows.Count; i++) {
if (((GridView)Master).DataKeys[i].Value == this.SelectedValue) {
((GridView)Master).SelectedIndex = i;
break;
} }
The GridView server control uses an instance of a server control named GridViewRow to display each
of its data records The Rows collection property of the GridView server control contains all the
GridViewRow server controls that display the data records of the server control
The GridView server control exposes a collection property named DataKeys , which contains one
DataKey object for each displayed data record in which the names and values of the primary key datafields of the record are stored In other words, each DataKey object in the DataKeys collection corresponds to a GridViewRow server control in the Rows collection
Trang 6Next, the method invokes the Master_SelectedIndexChanged method discussed earlier:
Master_SelectedIndexChanged(null, null);
Properties
As you can see from Listing 20-1 , the MasterDetailControl , like any other composite server control,
exposes the properties of its child controls as its own top-level properties, as follows:
❑ PageSize : This string property exposes the PageSize property of the GridView server control
as top-level property Recall that the PageSize property of a GridView server control specifies
the total number of records to display
❑ DataKeyNames : This array property exposes the DataKeyNames property of the GridView
server control as top-level property Recall that the DataKeyNames property of a
GridView server control contains the list of primary key datafield names
Note that the DataKeyNames property is annotated with the TypeConverter(typeof(StringArray
Converter)) metadata attribute to instruct the page parser that it must use the
StringArrayConverter to convert the declarative value of the DataKeyNames to the array This
declarative value is the value that the page developer declaratively assigns to the DataKeyNames
attribute on the tag that represents the MasterDetailControl server control on an aspx or ascx file
This declarative value is a string of comma-separated list of substrings in which each substring contains
the name of a primary key datafield name As the name suggests, the StringArrayConverter
converts this string into an array, which the page parser then automatically assigns to the
DataKeyNames property of the MasterDetailControl server control
Note that the getters and setters of these properties of the MasterDetailControl invoke the
EnsureChildControls method before they attempt to access the associated child server controls,
as I mentioned earlier
Using MasterDetailControl in a Web Page
Add the following files to the App_Code directory of the application that contains the page that uses the
MasterDetailControl control:
❑ BaseMasterDetailControl.cs : Listing 19-12 presents the content of this file
❑ ContainerType.cs : Listing 19-13 presents the content of this file
❑ MasterDetailContainer.cs : Listing 19-14 presents the content of this file
❑ BaseMasterDetailControl2.cs : Listing 19-15 presents the content of this file
❑ MasterDetailControl.cs : Listing 20-1 presents the content of this file
Listing 20-2 presents a page that uses the MasterDeatilControl Note that this page uses a theme,
a database with two tables named Products and Categories, and a connections string named
MyConnectionString I’ll discuss this theme, database, and connection string shortly If you run this
page, you’ll get the result shown in Figure 20-1
Trang 7Listing 20-2: A Page that Uses the MasterDetailControl
<%@ Page Language=”C#” Theme=”Theme1” %>
<%@ Register Namespace=”CustomComponents” TagPrefix=”custom” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />
<custom:MasterDetailControl ID=”MasterDetailControl1” runat=”server”
DataKeyNames=”ProductID” DetailDataSourceID=”DetailDataSource”
MasterDataSourceID=”MasterDataSource” PageSize=”3”
MasterSkinID=”GridView1” DetailSkinID=”DetailsView1” CellSpacing=”20”
HorizontalAlign=”Center” GridLines=”both” BorderStyle=”Ridge”
BorderWidth=”20” BorderColor=”Yellow” BackImageUrl=”images.jpg”>
<MasterContainerStyle HorizontalAlign=”center” BorderStyle=”Ridge”
SelectCommand=”Select ProductID, ProductName, UnitPrice From Products” />
<asp:SqlDataSource ID=”DetailDataSource” runat=”server”
ConnectionString=”<%$ ConnectionStrings:MyConnectionString %>”
SelectCommand=”Select * From Products where ProductID=@ProductID”
UpdateCommand=”Update Products Set ProductName=@ProductName, CategoryID=@CategoryID, UnitPrice=@UnitPrice, DistributorName=@DistributorName where ProductID=@ProductID”
DeleteCommand=”Delete From Products where ProductID=@ProductID”
InsertCommand=”Insert Into Products (ProductName, CategoryID, UnitPrice, DistributorName)
Values (@ProductName, @CategoryID, @UnitPrice, @DistributorName)”>
Trang 8As you can see, the MasterDetailControl displays only the master portion of the control Now if you
select a record from the GridView control, you’ll get the result shown in Figure 20-2 : the DetailsView
server control displays the detail of the selected record
Note that the DetailsView server control displays the standard Edit and Delete buttons to enable end
users to edit and delete the current record from the underlying data store The DetailsView server
control also contains the New button to enable the end user to add a new record to the data store
Thanks to the ASP.NET AJAX partial page rendering infrastructure, all the user interactions with the
GridView and DetailsView server controls are handled asynchronously in the background without
interrupting the user or reloading the entire page
Note that the page shown in Listing 20-2 takes advantage of ASP.NET 2.0 themes A theme is
implemented as a subfolder under the App_Themes folder The subfolder must have the same name as
the theme A theme subfolder consists of one or more skin files and their respective image and Cascading
Style Sheet files Since ASP.NET 2.0 merges all the skin files of a theme into a single skin file, page
devel-opers can use as many skin files as necessary to organize the theme folder Themes are assigned to the
containing page, not to the the individual controls
Figure 20-1
Trang 9The @Page directive in ASP.NET 2.0 exposes a new attribute named Theme , which is set to the name of the desired theme Since all themes are subfolders of the App_Themes folder, the ASP.NET framework knows where to find the assigned theme A skin file includes one or more control skins A control skin defines the appearance properties of a class of server controls The definition of a control skin is very similar to the declaration of an instance of the control on an ASP.NET page This doesn’t mean that all properties of a server control can be set in its skin In general, only the appearance properties can be included and set in a control skin If the SkinID property of a control skin isn’t set, the control skin is treated as the default skin A default skin is automatically applied to the control instances whose SkinID properties aren’t set If the SkinID property of a control skin is set, it will be applied only to the control instances whose SkinID property is set to the same value
Figure 20-2
Trang 10The page shown in Listing 20-2 uses a theme named Theme1 that contains a skin file with the following
content:
<asp:GridView SkinID=”GridView1” runat=”server” BackColor=”LightGoldenrodYellow”
BorderColor=”Tan” BorderWidth=”1px” CellPadding=”2” ForeColor=”Black”
GridLines=”None”>
<FooterStyle BackColor=”Tan” />
<SelectedRowStyle BackColor=”DarkSlateBlue” ForeColor=”GhostWhite” />
<PagerStyle BackColor=”PaleGoldenrod” ForeColor=”DarkSlateBlue”
HorizontalAlign=”Center” />
<HeaderStyle BackColor=”Tan” Font-Bold=”True” />
<AlternatingRowStyle BackColor=”PaleGoldenrod” />
</asp:GridView>
<asp:DetailsView SkinID=”DetailsView1” runat=”server” Width=”100%”
BackColor=”LightGoldenrodYellow” BorderColor=”Tan” BorderWidth=”1px”
CellPadding=”2” ForeColor=”Black” GridLines=”None” HorizontalAlign=”Center”>
<FooterStyle BackColor=”Tan” />
<EditRowStyle BackColor=”DarkSlateBlue” ForeColor=”GhostWhite” />
<PagerStyle BackColor=”PaleGoldenrod” ForeColor=”DarkSlateBlue”
HorizontalAlign=”Center” />
<HeaderStyle BackColor=”Tan” Font-Bold=”True” />
<AlternatingRowStyle BackColor=”PaleGoldenrod” />
</asp:DetailsView>
Also note that the page shown in Listing 20-2 connects to a database named ProductsDB that consists of
two database tables named Products and Categories The following table describes the Products
database table:
The following table describes the Categories database table:
Column Name Data TypeProductID int
ProductName varchar (50)CategoryID int
UnitPrice decimal (18, 0)DistributorName varchar (50)
Column Name Data Type
CategoryName varchar (50)CategoryDescription varchar (255)DateCreated datetime
Trang 11Note that the data source controls in Listing 20-2 make use of a connection string named
MyConnectionString You need to add the following fragment to the web.config file of your application:
<configuration>
<connectionStrings>
<add connectionString=”server=YOUR_SERVER_NAME;initial catalog=ProductsDB;integrated security=SSPI” name=”MyConnectionString”/>
</connectionStrings>
</configuration>
MasterDetailControl2
In this section, you’ll implement a new server control named MasterDetailControl2 that derives from
BaseMasterDetailControl2 and extends its functionality to use a DropDownList server control as the master server control, as shown in Listing 20-3
Listing 20-3: The MasterDetailControl2 Server Control
public class MasterDetailControl2 : BaseMasterDetailControl2 {
protected override BaseDataBoundControl CreateBaseDataBoundControlMaster() {
DropDownList master = new DropDownList();
Trang 13set { ((DetailsView)Detail).DataKeyNames = value;
} } }}
Master_SelectedIndexChanged
When the ListControl control raises the SelectedIndexChanged event, the
Master_SelectedIndexChanged method shown in Listing 20-3 is automatically invoked This method first checks whether any item has been selected from the ListControl control If not, it hides the detail server control, as I mentioned earlier:
if (((ListControl)Master).SelectedIndex == -1) this.Detail.Visible = false;
else this.Detail.Visible = true;
Next, it assigns the value of the SelectedValue property of the ListControl control to the
SelectedValue property of the MasterDetailControl2 control:
Trang 14object whose value is given by the SelectedValue property of the MasterDetailControl2 Recall that
this property contains the value associated with the selected item:
ListItem selectedItem =
((ListControl)Master).Items.FindByValue((string)SelectedValue);
Next, it accesses the index of the selected item:
int selectedIndex = ((ListControl)Master).Items.IndexOf(selectedItem);
Then it assigns this index to the SelectedIndex property of the ListControl master:
MasterDetailControl2 , like any other composite control, exposes the properties of its child controls as
its own top-level properties, as shown in Listing 20-3 Note that the DataKeyNames property is
anno-tated with the [TypeConverter(typeof(StringArrayConverter))] metadata attribute to instruct
the page parser that it must use the StringArrayConverter to convert the declarative value of this
property to its imperative value The declarative value is the string containing a list of comma-separated
substrings that the page developer assigns to the DataKeyNames attribute on the tag that represents the
MasterDetailControl2 control on the aspx page The imperative value is the value that the
DataKeyNames property expects — that is, an array of strings The StringArrayConverter knows how
to convert the string containing a list of comma-separated substrings to a NET array that contains these
substrings
Using MasterDetailControl2
Listing 20-4 presents a page that uses the MasterDetailControl2 Figure 20-3 shows what you’ll see
on your browser when you access this page Note that this page uses a theme named Theme1 that
con-tains a skin file with the following content:
<asp:DetailsView SkinID=”DetailsView1” runat=”server” Width=”100%”
BackColor=”LightGoldenrodYellow” BorderColor=”Tan” BorderWidth=”1px”
CellPadding=”2” ForeColor=”Black” GridLines=”None” HorizontalAlign=”Center”>
<FooterStyle BackColor=”Tan” />
<EditRowStyle BackColor=”DarkSlateBlue” ForeColor=”GhostWhite” />
<HeaderStyle BackColor=”Tan” Font-Bold=”True” />
<AlternatingRowStyle BackColor=”PaleGoldenrod” />
</asp:DetailsView>
Trang 15<asp:DropDownList SkinID=”DropDownList1” runat=”server”
BackColor=”LightGoldenrodYellow” BorderColor=”Tan” BorderWidth=”1px”
CellPadding=”2” ForeColor=”Black” GridLines=”None” Width=”100%”/>
This page assumes that the following files are added to the App_Code directory of the application that contains the page:
❑ BaseMasterDetailControl.cs : Listing 19-12 presents the content of this file
❑ ContainerType.cs : Listing 19-13 presents the content of this file
❑ MasterDetailContainer.cs : Listing 19-14 presents the content of this file
❑ BaseMasterDetailControl2.cs : Listing 19-15 presents the content of this file
❑ MasterDetailControl.cs : Listing 20-1 presents the content of this file
❑ MasterDetailControl2.cs : Listing 20-3 presents the content of this file
Also note that this page uses the same database ( ProductsDB ) and connection string discussed in the previous section
Again, thanks to the ASP.NET AJAX partial page infrastructure, every time the end user selects a new item from the DropDownList master control, or deletes, inserts, or updates a record in the DetailsView detail control, the following things happen:
❑ The current page is posted back to the server asynchronously in the background, without interrupting the user interaction with the current page
❑ When the server response finally arrives, only the MasterDetailControl2 is updated, without causing the entire page to reload
Listing 20-4: A Page that Uses MasterDetailControl2
<%@ Page Language=”C#” Theme=”Theme1” %>
<%@ Register Namespace=”CustomComponents” TagPrefix=”custom” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />
(continued)
Trang 16Listing 20-4 (continued)
<custom:MasterDetailControl2 ID=”MasterDetailControl21” runat=”server”
DataKeyNames=”ProductID” DetailDataSourceID=”DetailDataSource”
MasterDataSourceID=”MasterDataSource” MasterSkinID=”DropDownList1”
DetailSkinID=”DetailsView1” CellSpacing=”20” HorizontalAlign=”Center”
GridLines=”both” BorderStyle=”Ridge” BorderWidth=”20” BorderColor=”Yellow”
SelectCommand=”Select ProductID, ProductName From Products” />
<asp:SqlDataSource ID=”DetailDataSource” runat=”server”
ConnectionString=”<%$ ConnectionStrings:MyConnectionString %>”
SelectCommand=”Select * From Products where ProductID=@ProductID”
UpdateCommand=”Update Products Set ProductName=@ProductName,
CategoryID=@CategoryID,
UnitPrice=@UnitPrice,
DistributorName=@DistributorName
where ProductID=@ProductID”
DeleteCommand=”Delete From Products where ProductID=@ProductID”
InsertCommand=”Insert Into Products (ProductName, CategoryID, UnitPrice,
Trang 17MasterDetailControl3
In this section, you’ll implement a new server control named MasterDetailControl3 that derives from
MasterDetailControl2 and extends its functionality to use a ListBox server control rather than the
DropDownList server control as master server control, as shown in Listing 20-5 As you can see,
MasterDetailControl3 simply overrides the CreateBaseDataBoundControlMaster method that
it inherits from MasterDetailControl2 and replaces the DropDownList server control with a
ListBox server control
Listing 20-5: The MasterDetailControl3 Control
Trang 18Listing 20-6 contains a page that uses MasterDetailControl3 Note that this page uses a theme named
Theme1 , which contains a skin file with the following content:
<asp:DetailsView SkinID=”DetailsView1” runat=”server” Width=”100%”
BackColor=”LightGoldenrodYellow” BorderColor=”Tan” BorderWidth=”1px”
CellPadding=”2” ForeColor=”Black” GridLines=”None” HorizontalAlign=”Center”>
<FooterStyle BackColor=”Tan” />
<EditRowStyle BackColor=”DarkSlateBlue” ForeColor=”GhostWhite” />
<HeaderStyle BackColor=”Tan” Font-Bold=”True” />
<AlternatingRowStyle BackColor=”PaleGoldenrod” />
</asp:DetailsView>
<asp:ListBox SkinID=”ListBox1” runat=”server” BackColor=”LightGoldenrodYellow”
BorderColor=”Tan” BorderWidth=”1px” ForeColor=”Black” Width=”200”/>
Note also that this page assumes that the following files are added to the App_Code directory of the
application that contains this page:
❑ BaseMasterDetailControl.cs : Listing 19-12 presents the content of this file
❑ ContainerType.cs : Listing 19-13 presents the content of this file
❑ MasterDetailContainer.cs : Listing 19-14 presents the content of this file
❑ BaseMasterDetailControl2.cs : Listing 19-15 presents the content of this file
Trang 19❑ MasterDetailControl.cs : Listing 20-1 presents the content of this file
❑ MasterDetailControl2.cs : Listing 20-3 presents the content of this file
❑ MasterDetailControl3.cs : Listing 20-5 presents the content of this file
Also note that this page uses the same database ( ProductsDB ) and connection string as the pages in the previous sections
Figure 20-4 shows what you’ll get when you access this page As you can see, the master server control is now a ListBox server control Again, thanks to the ASP.NET AJAX partial page infrastructure, every time the end user selects a new item from the ListBox master control or deletes, inserts, or updates a record in the DetailsView detail control, the following things happen:
❑ The current page is posted back to the server asynchronously in the background, without rupting the user interaction with the current page
inter-❑ When the server response finally arrives, only the MasterDetailControl3 is updated, without causing the entire page to reload
Listing 20-6: A Page that Uses the MasterDetailControl3 Control
<%@ Page Language=”C#” Theme=”Theme1” %>
<%@ Register Namespace=”CustomComponents” TagPrefix=”custom” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />
<custom:MasterDetailControl3 ID=”MasterDetailControl21” runat=”server”
DataKeyNames=”ProductID” DetailDataSourceID=”DetailDataSource”
MasterDataSourceID=”MasterDataSource” MasterSkinID=”ListBox1”
DetailSkinID=”DetailsView1” CellSpacing=”20” HorizontalAlign=”Center”
GridLines=”both” BorderStyle=”Ridge” BorderWidth=”20” BorderColor=”Yellow”
Trang 20Listing 20-6 (continued)
<asp:SqlDataSource runat=”server” ID=”MasterDataSource”
ConnectionString=”<%$ ConnectionStrings:MyConnectionString %>”
SelectCommand=”Select ProductID, ProductName From Products” />
<asp:SqlDataSource ID=”DetailDataSource” runat=”server”
ConnectionString=”<%$ ConnectionStrings:MyConnectionString %>”
SelectCommand=”Select * From Products where ProductID=@ProductID”
UpdateCommand=”Update Products Set ProductName=@ProductName,
CategoryID=@CategoryID,
UnitPrice=@UnitPrice,
DistributorName=@DistributorName
where ProductID=@ProductID”
DeleteCommand=”Delete From Products where ProductID=@ProductID”
InsertCommand=”Insert Into Products (ProductName, CategoryID, UnitPrice,
In this section I’ll implement a server control named MasterDetailControl4 that derives from the
MasterDetailControl2 and overrides its SelectedValue property Recall that the MasterDetailControl2
inherits this property from the BaseMasterDetailControl MasterDetailControl4 overrides this property
to use the ASP.NET Session object as the backing store Recall that the BaseMasterDetailControl’s
imple-mentation of this property uses the ViewState as the backing store In the next section we’ll implement a
cus-tom data control field that will demonstrate the significance of using the ASP.NET Session object as the
backing store Listing 20-7 presents the implementation of MasterDetailControl4
Listing 20-7: The MasterDetailControls4 Server Control
get { return this.Page.Session[“SelectedValue”]; }
set { this.Page.Session[“SelectedValue”] = value; }
}
}
Trang 21Developing Par tial-Rendering-Enabled
Data Control F ields
The foreign and primary key pairs establish relationships among database tables The value of a foreign key field in a given record is one of the existing values of its corresponding primary key field Most database tables automatically generate the primary key value of a record when the record is added to the table Therefore the actual foreign key value is an auto-generated integer that doesn’t mean anything to end users However, the table that contains the primary key field normally contains other field values that are more meaningful to them
For instance, consider a database that contains tables named Products and Categories The Products table has a foreign key field named CategoryID The Categories table contains the corresponding primary key field, CategoryID The Categories table also exposes fields such as CategoryName and
Figure 20-4
Trang 22CategoryDescription , which provide more meaningful information to end users Wouldn’t it be great
if you could provide end users the appropriate user interface with which to view more meaningful
information about the available categories, so they can make more intelligent decisions as to which
cate-gory to choose for a given record? This is exactly what you’re going to do in this section You’ll
imple-ment a custom data control field named MasterDetailField that will present the end users with a user
interface that consists of a DropDownList master server control and a DetailsView detail server control,
so users can view more detailed information about a given foreign key field The MasterDetailField
will take advantage of the ASP.NET AJAX partial rendering infrastructure to retrieve the required data
from the server asynchronously and to update only the necessary part of the page — that is, the detail
server control — without forcing a complete page reload
As you’ll see, the MasterDetailField data control field not only displays detailed information about a
selected foreign key field value but also enables the end user to update this information In other words,
the end user gets to update the records of both the table that contains the primay key field values and the
table that contains the associated foreign key field values, simultaneously
Extending BoundField
Most standard data control fields internally use server controls to display the values of their respective
database fields For example, the ImageField and CheckBoxField data control fields internally use
Image and CheckBox server controls, respectively, to display their field values The data type of the field
and the state of its containing row determine the type of server control used to display the value of the
field For instance, an ImageField data control field uses an Image server control to display its field
value when its containing row is in the normal state, and a TextBox server control when its containing
row is in the Edit or Insert state
The MasterDetailField custom data control field will use a MasterDetailControl4 server control
to display all the legal values of its field when its containing row is in the Edit or Insert state The
MasterDetailField data control field will display the current value of its field as simple text when its
containing row isn’t in the Edit or Insert state The MasterDetailField data control field derives from
the BoundField data control field because BoundField provides all the necessary base functionality
when the containing row isn’t in the Edit or Insert state, such as:
❑ Extracting the current value of the field whose name is the value of the DataField property
The MasterDetailField overrides this property and defines a new property named
DataTextField to replace it because DataTextField is a more appropriate name than
DataField
❑ Displaying the current value as simple text if the current value isn’t null
❑ Displaying the value of the NullDisplayText property if the current value is null
❑ Displaying the value of the HeaderText property as simple text if sorting is disabled and as a
hyperlink if sorting is enabled
❑ Raising the sort event when sorting is enabled and the header hyperlink is clicked
The main shortcoming of the BoundField data control field is that it displays the current value of the
field in a TextBox control when the containing row is in the Edit or Insert state The TextBox control is
not the appropriate server control for editing foreign key fields because it enables users to enter any
value instead of restricting values to the legal ones The MasterDetailField data control field
over-rides the InitializeDataCell , OnDataBindField , and ExtractValuesFromCell methods of the
Trang 23BoundField data control field to add the support needed when the containing row is in the Edit or Insert state Listing 20-8 shows all the properties and methods of the MasterDetailField data control field In the following sections I’ll walk you through the implementation of these properties and methods
Listing 20-8: The MasterDetailField Data Control Field
namespace CustomComponents{
}
set { throw new global::System.NotImplementedException();
} }
public virtual string DataTextField {
get { return base.DataField;
} set { base.DataField = value;
} }
public virtual string MasterSkinID {
get { return (ViewState[“MasterSkinID”] != null) ? (string)ViewState[“MasterSkinID”] : String.Empty; }
(continued)
Trang 25set { ViewState[“DataValueField”] = value;
} }
public virtual string MasterDataSourceID {
get { return (ViewState[“MasterDataSourceID”] != null) ? (string)ViewState[“MasterDataSourceID”] : String.Empty;
} set { ViewState[“MasterDataSourceID”] = value;
} }
public virtual string DetailDataSourceID {
get { return (ViewState[“DetailDataSourceID”] != null) ? (string)ViewState[“DetailDataSourceID”] : String.Empty; }
set { ViewState[“DetailDataSourceID”] = value;
} }
protected override void OnDataBindField(Object sender, EventArgs e) {
DropDownList ddl = sender as DropDownList;
if (ddl == null) {
base.OnDataBindField(sender, e);
return;
}
Control parent = ddl.Parent;
DataControlFieldCell cell = null;
while (parent != null) {
cell = parent as DataControlFieldCell;
if (cell != null) break;
parent = parent.Parent;
}
(continued)
Trang 26Listing 20-8 (continued)
IDataItemContainer container = (IDataItemContainer)cell.Parent;
object dataItem = container.DataItem;
if (dataItem == null || String.IsNullOrEmpty(DataValueField))
if ((rowState & DataControlRowState.Edit) != 0 ||
(rowState & DataControlRowState.Insert) != 0)
throw new InvalidOperationException(
“MasterDetailField could not extract control.”);
string dataValueField = ((DropDownList)mdc.Master).SelectedValue;
Trang 27if (dictionary.Contains(DataValueField)) dictionary[DataValueField] = int.Parse(dataValueField);
else dictionary.Add(DataValueField, int.Parse(dataValueField));
} } }}
Overriding InitializeDataCell
The BoundField data control field exposes a method named InitializeDataCell that contains the code that generates the appropriate HTML markup text for the data cell The InitializeDataCell method takes two arguments The first argument is the DataControlFieldCell cell being initialized The second argument is the state of the containing row
What HTML markup text the BoundField class’s implementation of the InitializeDataCell method emits depends on the state of its containing row If the containing row is not in the Edit or Insert state, the method simply registers the OnDataBindField method as the callback for the DataBinding event
of the respective DataControlFieldCell instance When the DataBinding event of the cell is raised, the OnDataBindField method extracts the current value of the respective field (the name of the field
is the value of the DataField property) If the current value is null , the value of the NullDisplayText property is displayed Otherwise the current value is displayed as simple text
The BoundField class’s implementation of the InitializeDataCell method in normal state is exactly what you need However, the BoundField class’s implementation of the method when the containing row
is in the Edit or Insert state is not acceptable, because the method instantiates an instance of the TextBox control You need an implementation that instantiates an instance of the MasterDetailControl4 control That is why the MasterDetailField data control field overrides the InitializeDataCell method The
MasterDetailField data control field calls the base version of the InitializeDataCell method when the containing row is in the normal state, because the behavior of the base version is exactly what you need However, the MasterDetailField data control field provides its own implementation when the containing row is in the Edit or Insert state
As Listing 20-8 shows, the MasterDetailField data control field’s implementation of the
InitializeDataCell method instantiates an instance of the MasterDetailControl4 control and sets its MasterDataSourceID and DetailDataSourceID properties to the values of the
MasterDataSourceID and DetailDataSourceID properties of the MasterDetailField data control field, respectively It is the responsibility of page developers to set the MasterDataSourceID and
DetailDataSourceID properties of the MasterDetailField data control field to the values of the ID properties of the appropriate data source controls in the containing page Page developers must also set the DataTextField and DataValueField properties of the MasterDetailField data control field to the names of the appropriate database fields This allows the MasterDetailField data control field to automatically populate its MasterDetailControl4 control with the valid values of the foreign key field Note that InitializeDataCell method also sets the DataKeyNames property of the MasterDetailControl4 control to the value of the DataKeyNames property of the