Accessing Browser Capability Information Now that you have seen how ASP.NET stores browser capability information, we want to discuss how you can access this information at runtime and p
Trang 1Listing 26-25: The client-side callback JavaScript functions
// JScript File
var args;
var ctx;
function ValidateText(ctl)
{
if (ctl.value==") {
alert(’Please enter a value.’);
ctl.focus();
}
}
function CallbackHandler(args,ctx)
{
alert("The data is valid");
}
function ErrorHandler(args,ctx)
{
alert("Please enter a number");
}
Now, when you view your Web page in the browser, as soon as the text box loses focus, you perform
a client-side callback to validate the data The callback raises the RaiseCallbackEvent method on
the server, which validates the value of the text box that was passed to it in the eventArguments
If the value is valid, you return a string and the client-side CallbackHandlerfunction fires If the
value is invalid, you throw an exception, which causes the client-side ErrorHandler function to
execute
Detecting and Reacting to Browser Capabilities
So far in the chapter we have described many powerful features, such as styling and emitting
client-side scripts, that you can utilize when writing your own custom control But if you are taking advantage of these features, you must also consider how you can handle certain browsers, often
called downlevel browsers, that might not understand these advanced features or might not have
them enabled Being able to detect and react to downlevel browsers is an important consideration
when creating your control ASP.NET includes some powerful tools you can use to detect the type
and version of the browser making the page request, as well as what capabilities the browser
supports
.browser files
ASP.NET 2.0 introduced a new and highly flexible method for configuring, storing, and
discov-ering browser capabilities All browser identification and capability information is now stored in
.browserfiles ASP.NET stores these files in theC:\Windows\Microsoft.NET\Framework\v2.0.[xxxx]\ CONFIG\Browsersdirectory If you open this folder, you see that ASP.NET provides you with a variety
of.browserfiles that describe the capabilities of most of today’s common desktop browsers, as well
as information on browsers in devices such as PDAs and cellular phones Open one of the browser files, and you see that the file contains all the identification and capability information for the browser Listing 26-26 shows you the contents of the WebTV capabilities file
Trang 2Listing 26-26: A sample browser capabilities file
<browsers>
<! sample UA "Mozilla/3.0 WebTV/1.2(Compatible;MSIE 2.0)" >
<browser id="WebTV" parentID="IE2">
<identification>
<userAgent match="WebTV/(?’version’(?’major’\d+)(?’minor’\.\d+)(?’letters’\w*))" />
</identification>
<capture>
</capture>
<capabilities>
<capability name="backgroundsounds" value="true" />
<capability name="browser" value="WebTV" />
<capability name="cookies" value="true" />
<capability name="isMobileDevice" value="true" />
<capability name="letters" value="\${letters}" />
<capability name="majorversion" value="\${major}" />
<capability name="minorversion" value="\${minor}" />
<capability name="tables" value="true" />
<capability name="type" value="WebTV\${major}" />
<capability name="version" value="\${version}" />
</capabilities>
<controlAdapters markupTextWriterType="System.Web.UI.Html32TextWriter">
</controlAdapters>
</browser>
<browser id="WebTV2" parentID="WebTV">
<identification>
<capability name="minorversion" match="2" />
</identification>
<capture>
</capture>
<capabilities>
<capability name="css1" value="true" />
<capability name="ecmascriptversion" value="1.0" />
<capability name="javascript" value="true" />
</capabilities>
</browser>
<gateway id="WebTVbeta" parentID="WebTV">
<identification>
<capability name="letters" match="^b" />
</identification>
<capture>
</capture>
Trang 3<capability name="beta" value="true" />
</capabilities>
</gateway>
</browsers>
The advantage of this new method for storing browser capability information is that as new browsers are created or new versions are released, developers simply create or update a.browserfile to describe the capabilities of that browser
Accessing Browser Capability Information
Now that you have seen how ASP.NET stores browser capability information, we want to discuss
how you can access this information at runtime and program your control to change what it renders
based on the browser To access capability information about the requesting browser, you can use
thePage.Request.Browserproperty This property gives you access to theSystem.Web.HttpBrowser
Capabilitiesclass, which provides information about the capabilities of the browser making the
cur-rent request The class provides you with a myriad of attributes and properties that describe what the
browser can support and render and what it requires Lists use this information to add capabilities to the TextBox control Listing 26-27 shows how you can detect browser capabilities to make sure a browser
supports JavaScript
Listing 26-27: Detecting browser capabilities in server-side code
VB
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
If (Page.Request.Browser.EcmaScriptVersion.Major > 0) Then
Page.ClientScript.RegisterStartupScript( _ GetType(Page), "ClientCallback", _
"function ClientCallback() {" & _
"args=document.getElementById("’ & _ Me.ClientID & "_i" & "’).value;" & _ Page.ClientScript.GetCallbackEventReference(Me, "args", _
"CallbackHandler", Nothing, "ErrorHandler", True) + "}", _ True)
Page.ClientScript.RegisterStartupScript(GetType(Page), _
"ControlFocus", "document.getElementById("’ & _ Me.ClientID & "’).focus();", _
True) End If
End Sub
C#
protected override void OnPreRender(EventArgs e)
{
if (Page.Request.Browser.EcmaScriptVersion.Major > 0)
{
Page.ClientScript.RegisterClientScriptInclude(
"UtilityFunctions", "JScript.js");
Trang 4typeof(Page),
"ControlFocus","document.getElementById("’ + this.ClientID + "_i" + "’).focus();", true);
Page.ClientScript.RegisterStartupScript(
typeof(Page), "ClientCallback",
"function ClientCallback() {" +
"args=document.getElementById("’ + this.ClientID + "_i" + "’).value;" + Page.ClientScript.GetCallbackEventReference(this, "args",
"CallbackHandler", null,"ErrorHandler",true) + "}", true);
}
}
This is a very simple sample, but it gives you an idea of what is possible using theHttpBrowser
Capabilitiesclass
Using ViewState
When developing Web applications, remember that they are built on the stateless HTTP protocol ASP
.NET gives you a number of ways to give users the illusion that they are using a stateful application,
including Session State and cookies Additionally, ASP.NET 1.0 introduced a new way of creating the
state illusion called ViewState ViewState enables you to maintain the state of the objects and controls
that are part of the Web page through the page’s lifecycle by storing the state of the controls in a hidden
form field that is rendered as part of the HTML The state contained in the form field can then be used by
the application to reconstitute the page’s state when a postback occurs Figure 26-14 shows how ASP.NET
stores ViewState information in a hidden form field
Notice that the page contains a hidden form field named_ViewState The value of this form field is the
ViewState for your Web page By default, ViewState is enabled in all in-box server controls shipped with
ASP.NET If you write customer server controls, however, you are responsible for ensuring that a control
is participating in the use of ViewState by the page
The ASP.NET ViewState is basically a StateBag that enables you to save and retrieve objects as key/value
pairs As you see in Figure 26-14, these objects are then serialized by ASP.NET and persisted as an
encrypted string, which is pushed to the client as a hidden HTML form field When the page posts back
to the server, ASP.NET can use this hidden form field to reconstitute the StateBag, which you can then
access as the page is processed on the server
Because the ViewState can sometimes grow to be very large and can therefore affect the overall page size,
you might consider an alternate method of storing the ViewState information You can create your own
persistence mechanism by deriving a class from theSystem.Web.UI.PageStatePersisterclass and
overriding itsLoadandSavemethods.
As shown in Listing 26-28, by default, the Text property included with the ASP.NET Server Control
template is set up to store its value in ViewState
Trang 5Figure 26-14
Listing 26-28: The Text property’s use of ViewState
VB
Property Text() As String
Get
Dim s As String = CStr(ViewState("Text"))
If s Is Nothing Then
Return "[" + Me.ID + "]"
Else
Return s End If
End Get
Set(ByVal Value As String)
ViewState("Text") = Value
End Set
End Property
C#
public string Text
{
get
{
String s = (String)ViewState["Text"];
Trang 6return ((s == null) ? "[" + this.ID + "]": s);
}
set
{
ViewState["Text"] = value;
}
}
When creating new properties in an ASP.NET server control, you should remember to use this same
technique in order to ensure that the values set by the end user in your control will be persisted across
page postbacks
Note that the loading of ViewState happens after theOnInitevent has been raised by the page If your
control makes changes to itself or another server control before the event has been raised, the changes are
not saved to the ViewState.
Types and ViewState
As mentioned in the preceding section, the ViewState is basically a generic collection of objects, but not
all objects can be added to the ViewState Only types that can be safely persisted can be used in the
ViewState, so objects such as database connections or file handles should not be added to the ViewState
Additionally, certain data types are optimized for use in the ViewState When adding data to the
ViewState, try to package the data into these types:
❑ Primitive Types (Int32, Boolean, and so on)
❑ Arrays of Primitive Types
❑ ArrayList, HashTable
❑ Pair, Triplet
❑ Color, DataTime
❑ String, IndexedString
❑ HybridDictionary of these types
❑ Objects that have a TypeConverter available Be aware, however, that there is a reduction in
per-formance if you use these types
❑ Objects that are serializable (marked with theSerializableattribute)
ASP.NET 2.0 introduced new ViewState features that improve performance For example, the NET 1.1
ViewState used the LosFormatter to serialize objects, but starting with.NET 2.0, ViewState no longer uses
this serializer Instead, it uses the ObjectStateFormatter, which results in dramatic improvements in the
speed with which objects are serialized and deserialized It also decreases the overall byte size of the
resulting serialization Additionally, beginning with ASP.NET 2.0, ViewState was modified to be written
out as bytes rather than strings, thereby saving the cost of converting to a string
Control State
At times, your control must store critical, usually private information across postbacks In ASP.NET 1.0,
you might have considered using ViewState, but a developer using your control could disable ViewState
Trang 7ASP.NET solved this problem by introducing a new kind of ViewState called ControlState
Control-State is essentially a private ViewControl-State for your control only, and it is not affected when ViewControl-State is
turned off
Two new methods,SaveViewStateandLoadViewState, provide access to ControlState; however,
the implementation of these methods is left up to you Listing 26-29 shows how you can use the
LoadControlStateandSaveViewStatemethods
Listing 26-29: Using ControlState in a server control
VB
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
<DefaultProperty("Text")> _
<ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")> _
Public Class ServerControl1
Inherits WebControl
Dim s As String
Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
Page.RegisterRequiresControlState(Me)
MyBase.OnInit(e)
End Sub
Protected Overrides Sub LoadControlState(ByVal savedState As Object)
s = CStr(savedState)
End Sub
Protected Overrides Function SaveControlState() As Object
Return CType("FOO", Object)
End Function
Protected Overrides Sub Render(ByVal output As System.Web.UI.HtmlTextWriter)
output.Write("Control State: " & s)
End Sub
End Class
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
Trang 8namespace ServerControl1
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class ServerControl1 : WebControl
{
string s;
protected override void OnInit(EventArgs e) {
Page.RegisterRequiresControlState(this);
base.OnInit(e);
}
protected override void LoadControlState(object savedState) {
s = (string)savedState;
} protected override object SaveControlState() {
return (object)"FOO";
} protected override void Render(HtmlTextWriter output) {
output.Write("Control State: " + s);
} }
}
Controls intending to use ControlState must call thePage.RegisterRequiresControlStatemethod
before attempting to save control state data Additionally, theRegisterRequiresControlState
method must be called for each page load because the value is not retained through page postbacks
Raising PostBack Events
As you have seen in this chapter, ASP.NET provides a very powerful set of tools you can use to develop
server controls and emit them to a client browser But this is still one-way communication because
the server only pushes data to the client It would be useful if the server control could send data back
to the server The process of sending data back to the server is generally known as a page postback You
experience a page postback any time you click a form button or link that causes the page to make a new
request to the Web server
ASP.NET provides a rich framework for handling postbacks from ASP.NET Web pages Additionally,
ASP.NET attempts to give you a development model that mimics the standard Windows Forms event
model It enables you to use controls that, even though they are rendered in the client browser, can
raise events in server-side code It also provides an easy mechanism for plugging the server control into
that framework, allowing you to create controls that can cause a page postback Figure 26-15 shows the
ASP.NET postback framework
Trang 9IPostbackEventHandler
Init Load State Process Postback Data
Load Postback Events Save State PreRender Render Unload
Figure 26-15
In order to initiate a postback, ASP.NET uses client-side scripting You can add the proper script to your control by using theGetPostBackEventReferencemethod and emitting the results to the client during
the controls render method Listing 26-30 shows how you can add that to a new server control that emits
an HTML button
Listing 26-30: Adding PostBack capabilities to a server control
VB
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Trang 10<DefaultProperty("Text")> _
<ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")> _
Public Class ServerControl1
Inherits WebControl
Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter)
Dim p As New PostBackOptions(Me) output.AddAttribute(HtmlTextWriterAttribute.Onclick, _ Page.ClientScript.GetPostBackEventReference(p)) output.AddAttribute(HtmlTextWriterAttribute.Value, "My Button") output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.RenderBeginTag(HtmlTextWriterTag.Button)
output.RenderEndTag() End Sub
End Class
C#
Using System;
Using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ServerControl1
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class ServerControl1 : WebControl
{
protected override void RenderContents(HtmlTextWriter output) {
PostBackOptions p = new PostBackOptions(this);
output.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(p));
output.AddAttribute(HtmlTextWriterAttribute.Value, "My Button");
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i");
output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i");
output.RenderBeginTag(HtmlTextWriterTag.Button);
output.RenderEndTag();
} }
}
As you can see, this code adds the postback event reference to the client-side OnClick event, but you are
not limited to that You can add the postback JavaScript to any client-side event You could even add the
code to a client-side function if you want to include some logic code