ASP.NET 3.5 provides you with some powerful new tools for using client-side scripts in your server controls and retrieving other resources to the client along with the HTML your control
Trang 1Listing 26-18: Sample ASP.NET skin
<%@ Register Assembly="WebControlLibrary1" Namespace="WebControlLibrary1"
TagPrefix="cc1" %>
<cc1:webcustomcontrol1 BackColor="Green" runat="server" />
By default, ASP.NET allows all control properties to be defined in the skin file, but obviously this is
not always appropriate Most exposed properties are non-UI related; therefore, you do not apply a
theme to them By setting theThemeableattribute toFalseon each of these properties, you prevent the application of a theme Listing 26-19 shows how to do this in your control by disabling themes on the
Textproperty
Listing 26-19: Disabling theme support on a control property
VB
<Bindable(True), Category("Appearance"), DefaultValue(""), _
Localizable(True), Themeable(False)> _
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#
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
[Themeable(false)]
public string Text
{
get
{
String s = (String)ViewState["Text"];
return ((s == null) ? "["+ this.ID + "]": s);
}
set
{
ViewState["Text"] = value;
}
}
Now, if a developer attempts to define this property in his skin file, he receives a compiler error when
the page is executed
Trang 2Adding Client-Side Features
Although the capability to render and style HTML is quite powerful by itself, other resources can be
sent to the client, such as client-side scripts, images, and resource strings ASP.NET 3.5 provides you
with some powerful new tools for using client-side scripts in your server controls and retrieving other
resources to the client along with the HTML your control emits Additionally, ASP.NET now includes an
entire model that allows you to make asynchronous callbacks from your Web page to the server
Emitting Client-Side Script
Having your control emit client-side script such as VBScript or JavaScript enables you to add powerful
client-side functionality to your control Client-side scripting languages take advantage of the client’s
browser to create more flexible and easy-to-use controls Although ASP.NET 1.0 provided some simple
methods to emit client-side script to the browser, ASP.NET has since enhanced these capabilities and
now provides a wide variety of methods for emitting client-side script that you can use to control where
and how your script is rendered
If you have already used ASP.NET 1.0 to render client-side script to the client, you are probably
famil-iar with a few methods such as thePage.RegisterClientScriptBlockand thePage.RegisterStartup
Scriptmethods Since ASP.NET 2.0, these classes have been deprecated Instead, ASP.NET now uses the
ClientScriptManagerclass, which you can access usingPage.ClientScript This class exposes
various static client-script rendering methods that you can use to render client-side script
Listing 26-20 demonstrates how you can use theRegisterStartupScriptMethodmethod to render
JavaScript to the client This listing adds the code into theOnPreRendermethod, rather than into the
Rendermethod used in previous samples This method allows every control to inform the page about
the client-side script it needs to render After theRendermethod is called, the page is able to render
all the client-side script it collected during theOnPreRendermethod If you call the client-side script
reg-istration methods in theRendermethod, the page has already completed a portion of its rendering before
your client-side script can render itself
Listing 26-20: Rendering a client-side script to the browser
VB
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
Page.ClientScript.RegisterStartupScript(GetType(Page), _
"ControlFocus", "document.getElementById("’ & Me.ClientID & _
"_i" & "’).focus();", _ True)
End Sub
C#
protected override void OnPreRender(EventArgs e)
{
Page.ClientScript.RegisterStartupScript( typeof(Page),
"ControlFocus","document.getElementById("’ + this.ClientID +
"_i" + "’).focus();", true);
}
In this listing, the code emits client-side script to automatically move the control focus to the TextBox
control when the Web page loads When you use theRegisterStartupScriptmethod, notice that it now
1222
Trang 3includes an overload that lets you specify if the method should render surrounding script tags This can
be handy if you are rendering more than one script to the page
Also notice that the method requires a key parameter This parameter is used to uniquely identify the
script block; if you are registering more than one script block in the Web page, make sure that each block
is supplied a unique key You can use theIsStartupScriptRegisteredmethod and the key to determine
if a particular script block has been previously registered on the client using theRegisterStatupScript
method
When you execute the page in the browser, notice that the focus is automatically placed into a text box
If you look at the source code for the Web page, you should see that the JavaScript was written to the
bottom of the page, as shown in Figure 26-11
Figure 26-11
If you want the script to be rendered to the top of the page, you use theRegisterClientScriptBlock
method that emits the script block immediately after the opening<form>element
Keep in mind that the browser parses the Web page from top to bottom, so if you emit client-side script
at the top of the page that is not contained in a function, any references in that code to HTML elements further down the page will fail The browser has not parsed that portion of the page yet
Trang 4Being able to render script that automatically executes when the page loads is nice, but it is more likely
that you will want the code to execute based on an event fired from an HTML element on your page,
such as the Click, Focus, or Blur events In order to do this, you add an attribute to the HTML element
you want the event to fire from Listing 26-21 shows you how you can modify your control’sRenderand
PreRendermethods to add this attribute
Listing 26-21: Using client-side script and event attributes to validate data
VB
Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter)
output.RenderBeginTag(HtmlTextWriterTag.Div)
output.AddAttribute(HtmlTextWriterAttribute.Type, "text")
output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i")
output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i")
output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text)
output.AddAttribute("OnBlur", "ValidateText(this)")
output.RenderBeginTag(HtmlTextWriterTag.Input)
output.RenderEndTag()
output.RenderEndTag()
End Sub
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
Page.ClientScript.RegisterStartupScript(GetType(Page), _
"ControlFocus", "document.getElementById("’ & Me.ClientID & _
"_i" & "’).focus();", _ True)
Page.ClientScript.RegisterClientScriptBlock( _
GetType(Page), _
"ValidateControl", _
"function ValidateText() {" & _
"if (ctl.value==") {" & _
"alert(’Please enter a value.’);ctl.focus();} " & _
"}", _ True) End Sub
C#
protected override void RenderContents(HtmlTextWriter output)
{
output.RenderBeginTag(HtmlTextWriterTag.Div);
output.AddAttribute(HtmlTextWriterAttribute.Type, "text");
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i");
output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i");
output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
output.AddAttribute("OnBlur", "ValidateText(this)");
output.RenderBeginTag(HtmlTextWriterTag.Input);
output.RenderEndTag();
1224
Trang 5}
protected override void OnPreRender(EventArgs e)
{
Page.ClientScript.RegisterStartupScript(
typeof(Page),
"ControlFocus","document.getElementById("’ + this.ClientID +
"_i" + "’).focus();", true);
Page.ClientScript.RegisterClientScriptBlock(
typeof(Page),
"ValidateControl",
"function ValidateText(ctl) {" +
"if (ctl.value==") {" +
"alert(’Please enter a value.’);ctl.focus();} " +
"}",
true);
}
As you can see, the TextBox control is modified to check for an empty string We have also included an attribute that adds the JavaScriptOnBlurevent to the text box TheOnBlurevent fires when the control loses focus When this happens, the client-sideValidateTextmethod is executed, which we rendered to the client usingRegisterClientScriptBlock
The rendered HTML is shown in Figure 26-12
Embedding JavaScript in the page is powerful, but if you are writing large amounts of client-side code, you might want to consider storing the JavaScript in an external file You can include this file in your
HTML by using theRegisterClientScriptIncludemethod This method renders a script tag using the URL you provide to it as the value of itssrcelement
<script src="[url]" type="text/javascript"></script>
Listing 26-22 shows how you can modify the validation added to the TextBox control in Listing 26-20;
but this time, the JavaScript validation function is stored in an external file
Listing 26-22: Adding client-side script include files to a Web page
VB
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
Page.ClientScript.RegisterClientScriptInclude( _
"UtilityFunctions", "JScript.js")
Page.ClientScript.RegisterStartupScript(GetType(Page), _
"ControlFocus", "document.getElementById("’ & Me.ClientID & _
"_i" & "’).focus();", _ True)
End Sub
C#
protected override void OnPreRender(EventArgs e)
{
Continued
Trang 6"UtilityFunctions", "JScript.js");
Page.ClientScript.RegisterStartupScript(
typeof(Page),
"ControlFocus","document.getElementById("’ + this.ClientID +
"_i" + "’).focus();", true);
}
Figure 26-12
You have modified theOnPreRenderevent to register a client-side script file include, which contains
theValidateTextfunction You need to add a JScript file to the project and create theValidateText
function, as shown in Listing 26-23
1226
Trang 7Listing 26-23: The validation JavaScript contained in the Jscript file
// JScript File
function ValidateText(ctl)
{
if (ctl.value==") {
alert(’Please enter a value.’);
ctl.focus();
}
}
TheClientScriptManageralso provides methods for registering hidden HTML fields and adding
script functions to theOnSubmitevent
Accessing Embedded Resources
A great way to distribute application resources like JavaScript files, images, or resource files is to embed them directly into the compiled assembly While this was possible in ASP.NET 1.0, it was very difficult to access these resources as part of the page request process ASP.NET 2.0 solved this problem by including theRegisterClientScriptResourcemethod as part of the ClientScriptManager
This method makes it possible for your Web pages to retrieve stored resources — like JavaScript files — from the compiled assembly at runtime It works by using an HttpHandler to retrieve the requested
resource from the assembly and return it to the client TheRegisterClientScriptResourcemethod
emits a<script>block whosesrcvalue points to this HttpHandler:
<script
language="javascript"
src="WebResource.axd?a=s&r=WebUIValidation.js&t=631944362841472848"
type="text/javascript">
</script>
As you can see, theWebResource.axdhandler is used to return the resource — in this case, the JavaScript file You can use this method to retrieve any resource stored in the assembly, such as images or localized content strings from resource files
Asynchronous Callbacks
Finally, ASP.NET also includes a convenient mechanism for enabling basic AJAX behavior, or
client-side callbacks, in a server control Client-side callbacks enable you to take advantage of the
XmlHttp components found in most modern browsers to communicate with the server without actually performing a complete postback Figure 26-13 shows how client-side callbacks work in the ASP.NET Framework
In order to enable callbacks in your server control, you implement theSystem.Web.UI.ICallBackEvent Handerinterface This interface requires you to implement two methods, theRaiseCallbackEvent
method and theGetCallbackResult method These are the server-side events that fire when the client
executes the callback After you implement the interface, you want to tie your client-side events back
to the server You do this by using thePage.ClientScript.GetCallbackEventReferencemethod
Trang 8This method allows you to specify the two client-side functions: one to serve as the callback handler
and one to serve as an error handler Listing 26-24 demonstrates how you can modify the TextBox
con-trol’sRendermethods and add theRaiseCallBackEventmethod to use callbacks to perform validation
Using JavaScript, the
browser creates an
instance of the MSXML
ActiveX control
The MSXML ActiveX control makes a request to the server
JavaScript handles the Callback method
The Internet
JavaScript handles the ErrorCallback method
The request raises the ICallbackEventHandler method RaiseCallbackEvent
on the server
Method returns a string, or thrown an exception
Figure 26-13
Listing 26-24: Adding an asynchronous callback to validate data
VB
Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter)
output.RenderBeginTag(HtmlTextWriterTag.Div)
output.AddAttribute(HtmlTextWriterAttribute.Type, "text")
output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i")
output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i")
output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text)
output.AddAttribute("OnBlur", "ClientCallback();")
Me.AddAttributesToRender(output)
output.RenderBeginTag(HtmlTextWriterTag.Input)
output.RenderEndTag()
output.RenderEndTag()
End Sub
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
Page.ClientScript.RegisterStartupScript(GetType(Page), _
1228
Trang 9"ControlFocus", "document.getElementById("’ & _
Me.ClientID & "_i" & "’).focus();", _
True)
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)
End Sub
Public Sub RaiseCallbackEvent(ByVal eventArgument As String) _
Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent
Dim result As Int32
If (Not Int32.TryParse(eventArgument, result)) Then
Throw New Exception("The method or operation is not implemented.")
End If
End Sub
Public Function GetCallbackResult() As String _
Implements System.Web.UI.ICallBackEventHandler.GetCallbackResult
Return "Valid Data"
End Function
C#
protected override void RenderContents(HtmlTextWriter output)
{
output.RenderBeginTag(HtmlTextWriterTag.Div);
output.AddAttribute(HtmlTextWriterAttribute.Type, "text");
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i");
output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i");
output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
output.AddAttribute("OnBlur", "ClientCallback();");
this.AddAttributesToRender(output);
output.RenderBeginTag(HtmlTextWriterTag.Input);
output.RenderEndTag();
output.RenderEndTag();
}
protected override void OnPreRender(EventArgs e)
{
Page.ClientScript.RegisterStartupScript(
typeof(Page),
"ControlFocus","document.getElementById("’ +
this.ClientID + "_i" + "’).focus();",
true);
Trang 10typeof(Page), "ClientCallback",
"function ClientCallback() {" +
"args=document.getElementById("’ + this.ClientID + "_i" + "’).value;" + Page.ClientScript.GetCallbackEventReference(this, "args",
"CallbackHandler", null,"ErrorHandler",true) + "{", true);
}
#region ICallbackEventHandler Members
public void RaiseCallbackEvent(string eventArgument)
{
int result;
if (!Int32.TryParse(eventArgument,out result) )
throw new Exception("The method or operation is not implemented.");
}
public string GetCallbackResult()
{
return "Valid Data";
}
#endregion
As you can see, theOnBlurattribute has again been modified, this time by simply calling the
Client-Callbackmethod This method is created and rendered during thePreRenderevent The main purpose
of this event is to populate the client-sideargsvariable and call the client-side callback method
You are using theGetCallbackEventReferencemethod to generate the client-side script that actually
initiates the callback The parameters passed to the method indicate which control is initiating the
call-back, the names of the client-side callback method, and the name of the callback method parameters The
following table provides more details on theGetCallbackEventReferencearguments
Parameter Description
Argument Client-side variable used to pass arguments to the server-side event handler
ClientCallback Client-side function serving as theCallbackmethod This method fires when
the server-side processing has completed successfully
Context Client-side variable that gets passed directly to the receiving client-side
function The context does not get passed to the server
ClientErrorCallbackClient-side function serving as theCallbackerror-handler method This
method fires when the server-side processing encounters an error
In the code, two client-side methods are called:CallbackHandlerandErrorHandler, respectively The
two method parameters areargsandctx
In addition to the server control code changes, the two client-side callback methods have been added to
the JavaScript file Listing 26-25 shows these new functions
1230