1. Trang chủ
  2. » Công Nghệ Thông Tin

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

54 293 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 54
Dung lượng 1,24 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 1

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

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 4

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:

Trang 5

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:

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 6

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

Trang 7

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”

<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 8

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

Trang 9

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

Trang 10

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 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 11

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

public class MasterDetailControl2 : BaseMasterDetailControl2 {

protected override BaseDataBoundControl CreateBaseDataBoundControlMaster() {

DropDownList master = new DropDownList();

Trang 13

set { ((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 14

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:

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 16

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”

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 17

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

Trang 18

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

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 20

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,

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 21

Developing 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 22

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

Trang 23

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{

}

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 25

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)

Trang 26

Listing 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 27

if (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

Ngày đăng: 09/08/2014, 06:23

TỪ KHÓA LIÊN QUAN