Tải bản đầy đủ (.pdf) (54 trang)

ASP.NET AJAX Programmer’s Reference - Chapter 20 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.24 MB, 54 trang )

Using 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
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections;
using System.Drawing;
using System.ComponentModel;
(continued)
c20.indd 911c20.indd 911 8/20/07 8:34:10 PM8/20/07 8:34:10 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
912
Listing 20-1 (continued)


namespace CustomComponents
{
public class MasterDetailControl : BaseMasterDetailControl2
{
protected override BaseDataBoundControl CreateBaseDataBoundControlMaster()
{
GridView master = new GridView();
master.AllowPaging = true;
master.AllowSorting = true;
master.AutoGenerateColumns = true;
master.AutoGenerateSelectButton = true;
master.ID = “MasterGridView”;
return master;
}

protected override void RegisterMasterEventHandlers()
{
((GridView)Master).SelectedIndexChanged +=
new EventHandler(Master_SelectedIndexChanged);
((GridView)Master).PageIndexChanged +=
new EventHandler(Master_ResetSelectedValue);
((GridView)Master).Sorted += new EventHandler(Master_ResetSelectedValue);
}

public int PageSize
{
get
{
EnsureChildControls();
return ((GridView)Master).PageSize;

}
set
{
EnsureChildControls();
((GridView)Master).PageSize = value;
}
}

[TypeConverter(typeof(StringArrayConverter))]
public string[] DataKeyNames
{
get
{
EnsureChildControls();
return ((GridView)Master).DataKeyNames;
}
set
c20.indd 912c20.indd 912 8/20/07 8:34:11 PM8/20/07 8:34:11 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
913
{
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;
}
}

Master_SelectedIndexChanged(null, null);
}

void Master_ResetSelectedValue(object sender, EventArgs e)
{
if (((GridView)Master).SelectedIndex != -1)
{
((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.
c20.indd 913c20.indd 913 8/20/07 8:34:11 PM8/20/07 8:34:11 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
914
RegisterMasterEventHandlers
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:
if (((GridView)Master).SelectedIndex == -1)
this.Detail.Visible = false;
else
this.Detail.Visible = true
Next, the method stores the value of the SelectedValue of the GridView server control in the


SelectedValue property of the MasterDetailControl :
this.SelectedValue = ((GridView)Master).SelectedValue;
c20.indd 914c20.indd 914 8/20/07 8:34:11 PM8/20/07 8:34:11 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
915
The 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:
detail.DataBind();
Next, the method calls the Update method on the detail UpdatePanel server control to force this control
to update.
Master_ResetSelectedValue
As you can see from Listing 20-1 , this method simply sets the SelectedIndex property of

the
GridView server control to -1 to signal that no record is selected, and then invokes the
Master_SelectedIndexChanged method discussed in the previous section.
Master_DataBound
As you can see from Listing 20-1 , this method first searches through the GridViewRow server controls in
the
Rows collection of the GridView server control for a GridViewRow server control with the same
primary key field value as the one stored in the
SelectedValue property. If the search succeeds, the
method assigns the index of the
GridViewRow server control to the SelectedIndex property of
the
GridView server control to specify this GridViewRow server control as the selected row:
for (int i = 0; i < ((GridView)Master).Rows.Count; i++)
{
if (((GridView)Master).DataKeys[i].Value == this.SelectedValue)
{
((GridView)Master).SelectedIndex = i;
break;
}
}
T h e 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.
c20.indd 915c20.indd 915 8/20/07 8:34:12 PM8/20/07 8:34:12 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
916
Next, 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 .
c20.indd 916c20.indd 916 8/20/07 8:34:12 PM8/20/07 8:34:12 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
917
Listing 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”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<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”
BorderWidth=”20” BorderColor=”Yellow” />
<DetailContainerStyle BorderStyle=”Ridge” BorderWidth=”20”
BorderColor=”Yellow” />
</custom:MasterDetailControl>

<asp:SqlDataSource runat=”server” ID=”MasterDataSource”
ConnectionString=”<%$ ConnectionStrings:MyConnectionString %>”
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)”>
<SelectParameters>
<asp:ControlParameter ControlID=”MasterDetailControl1” Name=”ProductID”
PropertyName=”SelectedValue” DefaultValue=”1” />
</SelectParameters>
</asp:SqlDataSource>
</form>
</body>
</html>
c20.indd 917c20.indd 917 8/20/07 8:34:12 PM8/20/07 8:34:12 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
918
As 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
c20.indd 918c20.indd 918 8/20/07 8:34:13 PM8/20/07 8:34:13 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
919
The @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

c20.indd 919c20.indd 919 8/20/07 8:34:13 PM8/20/07 8:34:13 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
920
The 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 Type
ProductID int
ProductName varchar (50)
CategoryID int
UnitPrice
decimal (18, 0)
DistributorName
varchar (50)
Column Name Data Type
CategoryID int
CategoryName
varchar (50)
CategoryDescription varchar (255)
DateCreated
datetime
c20.indd 920c20.indd 920 8/20/07 8:34:13 PM8/20/07 8:34:13 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
921
Note 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
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections;
using System.Drawing;
using System.ComponentModel;

namespace CustomComponents
{
public class MasterDetailControl2 : BaseMasterDetailControl2
{
protected override BaseDataBoundControl CreateBaseDataBoundControlMaster()
{
DropDownList master = new DropDownList();
master.AutoPostBack = true;
master.ID = “DropDownList”;
return master;
}


protected override void RegisterMasterEventHandlers()
{
((ListControl)Master).SelectedIndexChanged +=
new EventHandler(Master_SelectedIndexChanged);
}

(continued)
c20.indd 921c20.indd 921 8/20/07 8:34:14 PM8/20/07 8:34:14 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
922
Listing 20-3 (continued)
protected override void Master_DataBound(object sender, EventArgs e)
{
ListItem selectedItem =
((ListControl)Master).Items.FindByValue((string)SelectedValue);
int selectedIndex = ((ListControl)Master).Items.IndexOf(selectedItem);
((ListControl)Master).SelectedIndex = selectedIndex;
Master_SelectedIndexChanged(null, null);
}

protected virtual void Master_SelectedIndexChanged(object sender, EventArgs e)
{
if (((ListControl)Master).SelectedIndex == -1)
this.Detail.Visible = false;
else
this.Detail.Visible = true;

this.SelectedValue = ((ListControl)Master).SelectedValue;
this.UpdateDetail(sender, e);

}

public string DataTextField
{
get
{
return ((ListControl)Master).DataTextField;
}
set
{
((ListControl)Master).DataTextField = value;
}
}

public string DataValueField
{
get
{
return ((ListControl)Master).DataValueField;
}
set
{
((ListControl)Master).DataValueField = value;
}
}

[TypeConverter(typeof(StringArrayConverter))]
public string[] DataKeyNames
{
get

{
return ((DetailsView)Detail).DataKeyNames;
}
c20.indd 922c20.indd 922 8/20/07 8:34:14 PM8/20/07 8:34:14 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
923
set
{
((DetailsView)Detail).DataKeyNames = value;
}
}
}
}
CreateBaseDataBoundControlMaster
As you can see from Listing 20-3 , the MasterDetailControl2’s implementation of this method
instantiates and initializes a
DropDownList server control as the master server control.
RegisterMasterEventHandlers
As Listing 20-3 shows, this method registers a method named Master_SelectedIndexChanged as an
event handler for the
SelectedIndexChanged event of the master server control. Note that this method
treats the master server control as a
ListControl object rather than a DropDownList . This is possible
because the ASP.NET
DropDownList server control derives from the ListControl base class. As you’ll
see in the next section, treating the master server control as a
ListControl enables you to use the same
implementation of the
RegisterMasterEventHandlers method for all types of ListControl controls,
such as

DropDownList and ListBox .
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:
this.SelectedValue = ((ListControl)Master).SelectedValue;
Finally, it invokes the UpdateDetail method to update the detail server control. As discussed
earlier, the detail server control picks up the new value of the
SelectedValue property of the
MasterDetailControl2 and displays the detail information about the selected item:
this.UpdateDetail(sender, e);
Master_DataBound
Recall that the Master_DataBound method is automatically invoked when the DataBound event of the
master server control is fired. As you can see from Listing 20-3 , this method first accesses the
ListItem
c20.indd 923c20.indd 923 8/20/07 8:34:14 PM8/20/07 8:34:14 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
924
object 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:
((ListControl)Master).SelectedIndex = selectedIndex;
Finally, it invokes the SelectedIndexChanged method discussed earlier:
Master_SelectedIndexChanged(null, null);
Properties
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>

c20.indd 924c20.indd 924 8/20/07 8:34:14 PM8/20/07 8:34:14 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
925
<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”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />

(continued)
c20.indd 925c20.indd 925 8/20/07 8:34:15 PM8/20/07 8:34:15 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
926
Listing 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”
BackImageUrl=”images.jpg” DataTextField=”ProductName”
DataValueField=”ProductID”>
<MasterContainerStyle HorizontalAlign=”center” BorderStyle=”Ridge”
BorderWidth=”20” BorderColor=”Yellow” />
<DetailContainerStyle BorderStyle=”Ridge” BorderWidth=”20”
BorderColor=”Yellow” />

</custom:MasterDetailControl2>

<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,
DistributorName)
Values (@ProductName, @CategoryID, @UnitPrice,
@DistributorName)”>
<SelectParameters>
<asp:ControlParameter ControlID=”MasterDetailControl21” Name=”ProductID”
PropertyName=”SelectedValue” DefaultValue=”1” />
</SelectParameters>
</asp:SqlDataSource>
&nbsp;
</form>
</body>
</html>
c20.indd 926c20.indd 926 8/20/07 8:34:15 PM8/20/07 8:34:15 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls

927
MasterDetailControl3
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
using System;
using System.Data;
using System.Configuration;
Figure 20-3
(continued)
c20.indd 927c20.indd 927 8/20/07 8:34:15 PM8/20/07 8:34:15 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
928
Listing 20-5 (continued)
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Collections;
using System.Drawing;
using System.ComponentModel;


namespace CustomComponents
{
public class MasterDetailControl3 : MasterDetailControl2
{
protected override BaseDataBoundControl CreateBaseDataBoundControlMaster()
{
ListBox master = new ListBox();
master.AutoPostBack = true;
master.ID = “ListBox”;
return master;
}
}
}
Using MasterDetailControl3
Listing 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.
c20.indd 928c20.indd 928 8/20/07 8:34:16 PM8/20/07 8:34:16 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
929
❑ 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 inter-
rupting the user interaction with the current page.
❑ 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”

“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<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”
BackImageUrl=”images.jpg” DataTextField=”ProductName”
DataValueField=”ProductID”>
<MasterContainerStyle HorizontalAlign=”center” BorderStyle=”Ridge”
BorderWidth=”20” BorderColor=”Yellow” />
<DetailContainerStyle BorderStyle=”Ridge” BorderWidth=”20”
BorderColor=”Yellow” />
</custom:MasterDetailControl3>

(continued)
c20.indd 929c20.indd 929 8/20/07 8:34:16 PM8/20/07 8:34:16 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
930
Listing 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,
DistributorName)
Values (@ProductName, @CategoryID, @UnitPrice,
@DistributorName)”>
<SelectParameters>
<asp:ControlParameter ControlID=”MasterDetailControl21” Name=”ProductID”
PropertyName=”SelectedValue” DefaultValue=”1” />
</SelectParameters>
</asp:SqlDataSource>
&nbsp;
</form>
</body>
</html>
MasterDetailControl4
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
namespace CustomComponents
{
public class MasterDetailControl4 : MasterDetailControl2
{
public override object SelectedValue
{
get { return this.Page.Session[“SelectedValue”]; }
set { this.Page.Session[“SelectedValue”] = value; }
}
}
}
c20.indd 930c20.indd 930 8/20/07 8:34:16 PM8/20/07 8:34:16 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
931
Developing Partial-Rendering-Enabled
Data Control Fields
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
c20.indd 931c20.indd 931 8/20/07 8:34:16 PM8/20/07 8:34:16 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
932
CategoryDescription , 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
c20.indd 932c20.indd 932 8/20/07 8:34:17 PM8/20/07 8:34:17 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
933
BoundField 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
{
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Collections;
using System.Data;

public class MasterDetailField : BoundField
{

public override string DataField
{
get
{
return base.DataField;
}

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)
c20.indd 933c20.indd 933 8/20/07 8:34:17 PM8/20/07 8:34:17 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
934
Listing 20-8 (continued)
set
{
ViewState[“MasterSkinID”] = value;
}
}

public virtual string DetailSkinID
{
get
{
return (ViewState[“DetailSkinID”] != null) ?
(string)ViewState[“DetailSkinID”] : String.Empty;
}
set
{
ViewState[“DetailSkinID”] = value;
}
}

[TypeConverter(typeof(StringArrayConverter))]
public virtual string[] DataKeyNames
{
get

{
return (ViewState[“DataKeyNames”] != null) ?
(string[])ViewState[“DataKeyNames”] : null;
}
set
{
ViewState[“DataKeyNames”] = value;
}
}

public virtual bool EnableTheming
{
get
{
return (ViewState[“EnableTheming”] != null) ?
(bool)ViewState[“EnableTheming”] : true;
}
set
{
ViewState[“EnableTheming”] = value;
}
}

public virtual string DataValueField
{
get
{
return (ViewState[“DataValueField”] != null) ?
(string)ViewState[“DataValueField”] : String.Empty;
}

c20.indd 934c20.indd 934 8/20/07 8:34:17 PM8/20/07 8:34:17 PM
Chapter 20: Using UpdatePanel in User Controls and Custom Controls
935
set
{
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)
c20.indd 935c20.indd 935 8/20/07 8:34:18 PM8/20/07 8:34:18 PM

×