Professional ASP.NET 1.0 Special Edition- P34 pdf

40 331 0
Professional ASP.NET 1.0 Special Edition- P34 pdf

Đ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

if ( eventArgument == "inc" ) Number = Number + 1; if ( eventArgument == "dec" ) Number = Number - 1; } protected override void Render(HtmlTextWriter writer) { writer.Write("The Number is " + Number.ToString() + " (" ); writer.Write("<a href=\"javascript:" + Page.GetPostBackEventReference(this,"inc") + "\"'>Increase Number</a>"); writer.Write(" or " ); writer.Write("<a href=\"javascript:" + Page.GetPostBackEventReference(this,"dec") + "\">Decrease Number)</a>"); } } } 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; theform.__EVENTTARGET.value = eventTarget; theform.__EVENTARGUMENT.value = eventArgument; theform.submit(); } // > </script> </form> </body> </html> Notice how the following script block containing the __doPostBack function is automatically rendered into the output stream when an ASP.NET server control calls the Page.GetPostBackEventReference reference: <script language="javascript"> <! function __doPostBack(eventTarget, eventArgument) { var theform = document.ctrl0; theform.__EVENTTARGET.value = eventTarget; theform.__EVENTARGUMENT.value = eventArgument; theform.submit(); } // > </script> This function is called by the script code returned from Page.GetPostBackEventReference: <a href="javascript:__doPostBack('ctrl1','inc')"> Now we have covered handling postback and events, let's look at how a control can persist state during postback using viewstate. Using ViewState After an ASP.NET page is rendered the page object, which created the page and all of its server controls, is destroyed. When a postback occurs, a new page object and new server-control objects are created. When writing a server control you often need to store and manage state. Since a control is created and destroyed with each page request, any state held in object member variables will be lost. If a control needs to maintain state, it has to do so using another technique. As we have seen with our textbox control, one way of managing state is to use postback. When a postback occurs, any postback data associated with a control is made available to it via the IPostBackData interface. A control can therefore re-populate its class variables, making the control appear to be stateful. 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==" /> 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 { get { if ( ViewState["value"] == null ) return String.Empty; return (string) ViewState["value"]; } set { 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 postCollection) { bool raiseEvent = false; if ( Text != postCollection[postDataKey] ) raiseEvent = true; Text = postCollection[postDataKey]; return raiseEvent; } Before testing this code to prove that our TextChanged event is now only raised when the text changes, we need to make a small change to our ASP.NET page. As you'll recall from earlier, we have an event handler that sets the contents of a label to reflect our textbox value when our event is raised: <script runat="server" language="C#"> private void OnNameChanged( object sender, EventArgs e ) { status.Text = "Value changed to " + name.Text; } </script> The label control uses viewstate to remember its value. When a postback occurs, even if this event is not raised, the label will still display the text from the previous postback, making it look like an event was raised. So to know if an event really was raised, we need to reset the value of the label during each postback. We could do this within the page init or load events, but since the label uses viewstate to retain its value, we can simply disable viewstate for the control using the EnableViewState attribute as follows: <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: 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" BorderColor="Red" OnTextChanged="OnNameChanged" /> More on Events Any server control that derives from the Control base classes automatically inherits several built-in events that page developers can also handle:  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" BackColor="Green" ForeColor="Yellow" BorderColor="Red" OnTextChanged="OnNameChanged" OnPreRender="OnPreRender" /> and an event handler that restricts the size of the TextBox value if it exceeds 7 characters: private void OnPreRender( object sender, EventArgs e ) { if ( name.Text.Length > 7 ) name.Text = name.Text.Substring(0,7); } As a control author you can also catch these standard events within your controls. You can do this by either wiring up the necessary event wire-up code, or, as we've seen already, overriding one of these methods:  OnInit(EventArgs e)  OnLoad(EventArgs e)  OnDataBinding(EventArgs e)  OnPreRender(EventArgs e)  OnUnload(EventArgs e)  Disposed() The default implementation of each of these methods raises the associated events listed earlier. For example, OnInit fires the Init event, and OnPreRender fires the PreRender event. When overriding one of these methods, you should call the base-class implementation of the method so that events are still raised, assuming that is the behavior you want: protected override void OnInit(EventArgs e) { base.OnInit(e); if ( _text == null ) _text = "Here is some default text"; } [...]... 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... 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... control we can access For example, if we had a public property declared in our ASP.NET page called Name, we could bind our item template to this using the databinding syntax, which was introduced in Chapter 7: An item in the collection: When this expression is evaluated, ASP.NET will try and locate the Name property on the naming container first... Topics In this section of the chapter we'll show some techniques you may find useful, depending on the types of server controls you write Accessing the ASP.NET Intrinsics The Control class has a Context property that enables a server control to access ASP.NET intrinsic objects such as Application, Session, Request, Response, Error, and Cache The Context property is defined as the type System.Web.HttpContext,... 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... nothing new in this code that we haven't already discussed If you've skipped the chapter on collection classes, refer back to Chapter 15 for an explanation of using IEnumerator and ICollection The following ASP.NET page uses the ICollectionLister control to list the contents of a string array This array is created in the Page_Load event and associated with a server control which has been given a name/Id of... 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... 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... 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) Pair, Triplet... DataItem As our item template is now being instantiated within the CollectionItem class, we have to update the TemplateContainer attribute declared on the ItemTemplate property to reflect this Without this ASP.NET would throw a cast exception when evaluating a data-binding expression: ITemplate _itemStyle; [TemplateContainer(typeof(CollectionItem))] public ITemplate ItemStyle { get{ return _itemStyle; } . name="ctrl0" method="post" action="myfirstcontrol.aspx" id="ctrl0"> <input type="hidden" name="__VIEWSTATE" value="dDwtMTM0MTQ1NDExNjt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjs+O2w8dDxwPGw8TnVtYmVyOz. 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 16 0 bytes of memory 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

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

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan