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

Professional ASP.NET 1.0 Special Edition- P34 pdf

40 334 0
Tài liệu đã được kiểm tra trùng lặp

Đ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 40
Dung lượng 1,21 MB

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

Nội dung

This is defined in the Control class as the type StateBag, and allows server controls to store and retrieve values that are automatically round-tripped and recreated during a postback..

Trang 2

The following HTML is rendered by this control:

<html>

<body>

<form name="ctrl0" method="post" action="myfirstcontrol.aspx" id="ctrl0">

<input type="hidden" name=" VIEWSTATE"

value="dDwtMTM0MTQ1NDExNjt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjs+O2w8dDxwPGw8TnVtYmVyOz

47bDxpPDQ4Pjs+Pjs7Pjs+Pjs+Pjs+" />

The Number is 48 (<a href="javascript: doPostBack('ctrl1','inc')"'>Increase Number</a> or

<a href="javascript: doPostBack('ctrl1','dec')">Decrease Number)</a>

<input type="hidden" name=" EVENTTARGET" value="" />

<input type="hidden" name=" EVENTARGUMENT" value="" />

<script language="javascript">

<!

function doPostBack(eventTarget, eventArgument) {

var theform = document.ctrl0;

Trang 3

function doPostBack(eventTarget, eventArgument) {

var theform = document.ctrl0;

Trang 4

Now we have covered handling postback and events, let's look at how a control can persist state during postback using viewstate

Using postback data to manage the state of a control is a good technique when it can be used, but there are some drawbacks The most obvious one is that only certain HTML elements like input can use postback If you had a label control that needed to remember its value, you couldn't use postback Also, postback is only really designed to contain a single item of data For example, our textbox control needs to remember its last value so it can raise a TextChanged event when the value changes To maintain this additional state, one option would be to use hidden fields When a control renders its output, it could also output hidden fields with other values that need to be remembered When a postback occurs, these values would be retrieved into the LoadPostData method This approach would work for a single control, but could be problematic when there are potentially many instances of the same control on a page For example, what would you call the hidden fields? How could you ensure the names do not clash with names a page developer may have used?

To resolve the problems of managing state ASP.NET has a feature called viewstate In a nutshell, viewstate is a hidden input field that can contain state for any number of server controls This hidden field is automatically managed for you, and as a control author you never need to access it directly

Introducing the StateBag

All server controls have a property called ViewState This is defined in the Control class as the type StateBag, and allows server controls to store and retrieve values that are automatically round-tripped and recreated during a postback

During the save state stage of a page, the ASP.NET framework enumerates all server controls within a page and persists their combined state into a hidden field called VIEWSTATE If you view any rendered ASP.NET containing a form element you will see this field:

<input type="hidden" name=" VIEWSTATE" value="dDwtMTcxOTc0MTI5NDs7Pg==" />

Trang 5

When a postback occurs, ASP.NET decodes the VIEWSTATE hidden field and automatically repopulates the viewstate for each server control as they are created This reloading of state occurs during the load state stage of a page for controls that are declared on an ASP.NET page If a control is dynamically created, either on a page or within another composite control, the state will be loaded at the point of creation ASP.NET keeps track of what viewstate hasn't been processed, and when a new control is added to the Controls property of a Control (remember a page is a control), it checks to see if it has any viewstate for the control If it has, it is loaded into the control at that point

To see viewstate in action, we will change our textbox control to store its current value in viewstate, rather than the _value field By doing this, when LoadPostData is called to enable our textbox control to retrieve its new value, we can compare it to the old value held in viewstate If the values are different we will return true, causing a TextChanged event to be raised in RaisePostDataChangedEvent If the values are the same, we will return false so

RaisePostDataChangedEvent is not called, and no event is raised

The StateBag class implements the IDictionary interface, and for the most part is used just like the Hashtable class with a string key All items stored are of the type System.Object, so any type can be held in the viewstate, and casting

is required to retrieving an item

In our earlier textbox control we used a string member variable _value to hold the current value of our textbox We'll delete that variable and rewrite the property to use viewstate:

public string Text

Trang 6

ViewState["value"] = value;

}

}

Since we have deleted the _value member variable and replaced it with this property, we need to change all references

to it, with the Text property We could directly reference the ViewState where we previously used _value, but it's good practice to use properties to encapsulate our usage of viewstate, making our code cleaner and more maintainable (For example, if we changed the viewstate key name used for the text value, we'd only have to do it in one place.)

With this new property in place, we can revise the LoadPostData to perform the check against the existing value as discussed:

bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection

of a label to reflect our textbox value when our event is raised:

<script runat="server" language="C#">

Trang 7

private void OnNameChanged( object sender, EventArgs e )

<ASP:Label runat="server" EnableViewState="false" id="status" />

During the save state stage of a page, the ASP.NET page framework will not persist viewstate for the controls with an EnableViewState property of false This change to the page will therefore make our label forget its value during each postback

Setting EnableViewState to false does not prevent a control from remembering state using postback As such, should you need to reset the value of a textbox, you'd have to clear the Text property in a page's init/load event With all these changes made, if we enter a value of "Wrox Press" and press the postback button, we will see that during the first postback our event is fired, and our label control displays the value:

Trang 8

If we click the postback button again, the textbox control will use its viewstate to determine that the postback value has not changed, and it will not fire its TextChanged event Since the label control does not remember its state, as we disabled viewstate for it, the value-changed message will not appear during the second postback since the label will default back to its original blank value:

Our textbox control is now pretty functional for a simple control: it can remember its value during postback, can raise events when its text changes, and can have style properties applied in the same way as other web controls using the various style attributes:

<Wrox:MyTextBox id="name" runat="server"

BackColor="Green"

ForeColor="Yellow"

Trang 9

ƒ Init - called when a control has to be constructed and its properties have been set

ƒ Load - called when a control's viewstate is available

ƒ DataBinding - called when a control bound to a data source should enumerate its data source and build its control tree

ƒ PreRender - called just before the UI of a control is rendered

ƒ Unload - called when a control has been rendered

ƒ Disposed - called when a control is destroyed by its container

These events behave just like any other event For example, we could catch the PreRender event of our TextBox and restrict its length to seven characters by adding an OnPreRender attribute to our control declaration:

<P>Enter a value: <Wrox:MyTextBox id="name" runat="server"

Trang 10

private void OnPreRender( object sender, EventArgs e )

protected override void OnInit(EventArgs e)

{ base.OnInit(e);

if ( _text == null )

_text = "Here is some default text";

}

Trang 11

Event Optimization in C# Using the EventHandlerList

When an event is declared within a class definition, additional memory must be allocated for an object instance at run-time for the field containing the event As the number of events a class supports increases, the amount of memory consumed by each and every object instance increases Assuming that a control supports 10 events (the 6 built-in ones and 4 custom events), and assuming an event declaration requires roughly 16 bytes of memory, each object instance will require 160 bytes of memory If nobody is interested in any of these events, this is a lot of overhead for a single control

To only consume memory for events that are in use, ASP.NET controls can use the EventHandlerList class

The EventHandlerList is an optimized list class designed to hold delegates The list can hold any number of delegates, and each delegate is associated with a key The Control class has an Events property that returns a reference to an instance of the EventHandlerList This instantiates the class on demand, so if no event handlers are in use, there is almost no overhead:

protected EventHandlerList Events

The EventHandlerList class has two main methods:

void AddHandler( object key, Delegate handler );

Trang 12

void RemoveHandler( object key, Delegate handler );

AddHandler is used to associate a delegate (event handler) with a given key If the method is called with a key for which

a delegate already exists, the two delegates will be combined and both will be called when an event is raised

RemoveHandler simply performs the reverse of AddHandler

Using the Events property, a server control should implement support for an event using a property declared as the type event:

private static readonly object _textChanged = new object();

public event EventHandler TextChanged

{

add { Events.AddHandler(EventPreRender, value); }

remove { Events.RemoveHandler(EventPreRender, value); }

}

Since this property is declared as an event, we have to use the add and remove property accessor declarations, rather than get and set When add or remove are called, the value is equal to the delegate being added or removed, so we use this when calling AddHandler or RemoveHandler

As Visual Basic NET does not support the add/remove accessor, we can't use optimized event handlers in Visual Basic NET

To create a unique key for our events, which we know will not clash with any events defined in our base classes, we define

a static, read-only member variable called _textChanged, and instantiate it with an object reference We could use other techniques for creating the key, but this approach adds no overhead for each instance of our server control, and is also the technique used by the built-in ASP.NET server controls By making the key value static, there is no per-object overhead

Checking and raising an event using the Events property is done by determining if a delegate exists for the key associated with an event If it does, we raise it to notify one or more subscribed listeners:

void IPostBackDataHandler.RaisePostDataChangedEvent()

Trang 13

Tracking ViewState

When adding and removing items from viewstate, they are only persisted by a control if its viewstate is being tracked This tracking only occurs after the initialization phase of a page is completed This means any changes a server control makes

to itself, or to another control before this phase, and the OnInit event has been raised, will not be saved

Types and ViewState

We mentioned earlier that the StateBag class used to implement viewstate allows any type to be saved and retrieved from it While this is true, this does not mean that you can use any type with it Only types that can be safely persisted can be used As such, types that maintain resources such as database connections or file handles should not be used ViewState is optimized and designed to work with the following types:

ƒ Int32, Boolean, String, and other primitive types

ƒ Arrays of Int32, Boolean, String, and other primitive types

ƒ ArrayList, Hashtable

ƒ Types that have a type converter A type converter is a class derived from

System.ComponentModel.TypeConverter that can convert one type into another For example, the type converter for the Color class can convert the string "red" into the enumeration value for red ASP.NET requires

a type converter that can convert a type to and from a string

ƒ Types that are serializable (marked with the serializable attribute, or support the serialization interfaces)

Trang 14

ƒ Pair, Triplet (defined in System.Web.UI, and respectively hold 2 or 3 of the other types listed)

ViewState is converted from these types into a string by the Limited Object Serialization (LOS) formatter class (System.Web.UI.LosFormatter)

The LOS formatter used by ASP.NET encodes a hash code into viewstate when a page is generated This hash code is used during postback to determine if the static control declarations in an ASP.NET page have changed (for example, the number and ordering of server controls declared within an ASP.NET page) If a change is detected, all viewstate is discarded, since viewstate cannot reliably be processed if the structure of a page has changed This limitation stems from the fact that ASP.NET automatically assigns unique identifiers to controls, and uses these identifiers to associate viewstate with individual given controls If a page structure changes, so do the unique identifiers assigned to controls, so the viewstate/control relationship is meaningless In case you're wondering, yes, this is one technical reason why ASP.NET only allows a page to postback to itself

More on Object Properties and Template UI

Earlier, we discussed how the default control builder of a server control would automatically map sub-elements defined within a server-control declaration to public properties of that control For example, given the following server-control declaration:

<Wrox:ICollectionLister id="SessionList" runat="server">

The control builder of the ICollectionLister control shown here would try to initialize the object properties

HeadingStyle and ItemStyle, determining the type of the object properties by examining the meta data of the ICollectionLister class using reflection As the HeadingStyle element in this example has a Font sub-element, the control builder would determine that the HeadingStyle object property has an object property of Font

The ICollectionLister server control is a simple composite control we've created that can enumerate the contents of any collection class implementing the ICollection For each item in the collection it creates a Label control, and sets the text of the label using the ToString method of the current item in the collection This causes a linebreak because for each item in the collection, the label starts with "<BR>" The control also has a fixed heading of "ICollection Lister

Trang 15

Control" which is also created using a label control

The ICollectionLister control has three properties:

ƒ DataSource - a public property of the type ICollection When CreateChildControls is called this property is enumerated to generate the main output of the control

ƒ HeadingStyle - a public property of the type Style This allows users of the control to specify the style attributes used for the hard-coded heading text The Style.ApplyStyle method is used to copy this style object into the Label control created for the header

ƒ ItemStyle - a public property of the type Style This allows users of the control to specify the style attributes used for each of the collections that is rendered The Style.ApplyStyle method is used to copy this style object into the Label control created for each item

The code for this server control is shown here:

Trang 16

{

get { return _datasource; }

set { _datasource = value; }

}

Style _headingStyle = new Style();

public Style HeadingStyle

{

get{ return _headingStyle; }

}

Style _itemStyle = new Style();

public Style ItemStyle

Trang 17

// Create the heading, using the specified user style

Trang 18

<script runat="server" language="C#">

void Page_Load( object sender, EventArgs e )

Trang 19

<Font Size="18"/>

</HeadingStyle>

<ItemStyle ForeColor="Green" Font-Size="12"/>

</Wrox:ICollectionLister>

The output from this page (if viewed in color) is a blue header with green text for each item in the collection:

For controls that have fixed style and layout requirements, initializing them using object properties like we have in the ICollectionLister control is a good approach You will have seen the same approach used throughout the standard ASP.NET server controls, such as the data grid and data list However, for a control to provide ultimate flexibility, it's better to enable the user of the control to define what the UI of a control looks like by using templates Again, you'll have seen this in earlier chapters, with controls like the data grid

Supporting template properties in a server control is relatively straightforward, although when using them within a data-bound control things can initially seem a little complex, since the way child controls are created has to be handled

Trang 20

slightly differently

Let's introduce templates by rewriting our ICollectionLister control to support a heading and item template We need to make the following changes to our code:

ƒ Change the HeadingStyle and ItemStyle properties to the ITemplate type

ƒ Make the HeadingStyle and ItemStyle properties writeable This has to be done since the objects implementing the ITemplate interface are dynamically created by the ASP.NET page and then associated with our server control

ƒ Use the TemplateContainer attribute to give the control builder a hint about the type of object within which our templates will be instantiated This reduces the need for casting in databinding syntax

Our changed code is shown here:

ITemplate _headingStyle;

[TemplateContainer(typeof(ICollectionLister))]

public ITemplate HeadingStyle

{

get{ return _headingStyle; }

set{ _headingStyle = value; }

Ngày đăng: 03/07/2014, 07:20

TỪ KHÓA LIÊN QUAN