Creating an Extensive Client Side Web Control

In the previous examples in this chapter, the client-side script wasn't central to the operation of the control. In this section, we'll look at a control that uses client-side script not as a minor part of its existence but as its primary reason for being.

One common problem that developers encounter is the need to duplicate the behavior of an existing system. For example, I recently needed to create a downtime patient registration system to be used while the main system was disabled for scheduled maintenance or due to a system failure. The users of this system were willing to be a little flexible, but certain things were non-negotiable. The main patient registration system allowed users to enter dates in mmddyy or mmddyyyy format. As they left the date field, the date would be changed to mm/dd/yyyy format, or a message would appear saying that the date was invalid.

It's certainly possible to format the date on the server side, with every change in the text box for the date causing a round-trip to the server, but processing all of this information on the server side could require a great deal of interaction between the client and the server. JavaScript on the client side is capable of handling this kind of problem.

With ASP, using client-side code requires a great deal of coordination between the designer of the page and the JavaScript developer. A better solution in the ASP.NET world is a component that encapsulates all of the required logic in a convenient package that can be dragged from the Toolbox onto the Web Form design surface.

Figure 7-4 shows an ASP.NET control named ReformatDate that properly formats a date when the control is exited.

ITTW

m, [.U W. F^rhx, ^ I * L j^j

J J 3 Juiji ijrnHM f in

^VJi^jwilcriir^'hHihi ."J .■-■''

il

hint |

- J ! J IP*"*** fltf.:' =,«.1" i

■Hl^*

1 J

1

J

£■; Ml» pE larfiimw

Figure 7-4 : The ReformatDate control, before and after tabbing out of it

Listing 7-7 shows the code for the completed ReformatDate control. Listing 7-7 WebCustomControl1.cs, the source for the ReformatDate control

Figure 7-4 : The ReformatDate control, before and after tabbing out of it

Listing 7-7 shows the code for the completed ReformatDate control. Listing 7-7 WebCustomControl1.cs, the source for the ReformatDate control using System;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

using System.Collections.Specialized;

[assembly: TagPrefix("Chapter07_ReformatDate","Chapter07")]

namespace FormatDateControl {

/// Summary description for WebCustomControl1.

[DefaultProperty("Text"),

ToolboxData("<{0}:ReformatDate runat=server></{0}:ReformatDate>")] public class ReformatDate : System.Web.UI.WebControls.BaseValidator, IPostBackDataHandler,IPostBackEventHandler

private bool bIsValid;

protected override bool EvaluateIsValid()

this.ServerFormatDate(); return bIsValid;

public ReformatDate() {

bIsValid=true; this.ErrorMessage="*";

[Bindable(true),

Category("Appearance"),

DefaultValue("")]

override public String Text {

return (String) ViewState["Text"];

ViewState["Text"] = value;

protected override void OnLoad(EventArgs e) {

base.OnLoad(e); if ( Page.IsPostBack )

ServerFormatDate();

IsValid=bIsValid;

if ( Page.ClientTarget.ToLower()!="downlevel" ) {

Page.RegisterClientScriptBlock("FormatDateClientScript", "<" + "SCRIPT Language=JavaScript " + "SRC=\"FormatDate.js\"></" + "SCRIPT>");

protected override void OnInit(EventArgs e)

/// Render this control to the output parameter specified.

/// <param name="output"> The HTML writer to write out to </param>

protected override void Render(HtmlTextWriter output)

if ( Page.ClientTarget.ToLower()!="downlevel" ) {

output.Write("<INPUT TYPE=\"TEXT\" ID=" + this.UniqueID + " Name= " + this.UniqueID + " Value=\"" + Text + "\" OnChange=\"FormatDate('" + this.UniqueID + "');\" Size=10 maxlen=10>");

output.Write("<INPUT TYPE=\"TEXT\" ID=" + this.UniqueID + " Name= " + this.UniqueID + " Value=\"" + Text + "\" Size=10 maxlen=10>");

// this.ControlToValidate=this.UniqueID;

output.Write("<span id=val" + this.UniqueID + ">");

output.Write("<font color=" + this.ForeColor + ">" + this.ErrorMessage + "</font>");

protected void ServerFormatDate() {

string tstr;

System.DateTime dt;

bIsValid=false;

tstr=Text;

dt=System.DateTime.Parse(tstr);

Text=dt.ToShortDateString();

bIsValid=true;

catch(FormatException fe) {

int mo,da,yr; string dtPart;

dtPart=tstr.Substring(0,2); mo=System.Int32.Parse(dtPart);

dtPart=tstr.Substring(2,2); da=System.Int32.Parse(dtPart);

dtPart=tstr.Substring(4,tstr.Length-4); yr=System.Int32.Parse(dtPart);

Text=mo.ToString() + "/" + da.ToString() + "/" + yr.ToString();

bIsValid=true;

catch (Exception e) {

bIsValid=false;

// IPostBackDataHandler related items follow. public event EventHandler TextChanged;

public virtual bool LoadPostData(string postDataKey, NameValueCollection values)

String presentValue = Text;

String postedValue = values[postDataKey];

if (IpresentValue.Equals(postedValue)) {

Text = postedValue; return true;

Text=postedValue;

return false;

public virtual void RaisePostDataChangedEvent() {

ServerFormatDate(); IsValid=bIsValid;

OnTextChanged(EventArgs.Empty);

public void RaisePostBackEvent(string EventArgument) {

return;

protected virtual void OnTextChanged(EventArgs e) {

if (TextChanged != null) TextChanged(this,e);

For this example, I won't need to add designer support for Visual Studio .NET because the default rendering of the control is reasonable, showing a text box that displays the Text property in the designer. (Chapter 6 explained how to add designer support to display a custom control in design mode.) The vast majority of the server-side work in the ReformatDate control is done in the Render method. Within Render, I simply output the HTML required to render an HTML text input control. I set the ID on the text control using this.UniqueID. (In Visual Basic .NET, this would be Me.UniqueID.) I also set the Size, Maxlen, and Value attributes in the HTML input control. In addition, I set the OnChange event to the FormatDate JavaScript function, passing in the ID of the control as the single parameter to the function. The OnChange event handler is called whenever the control is exited and the value is different from the value when the control was entered.

Caution One thing that caused me no end of confusion while creating the ReformatDate control was how it should be rendered. I've become used to setting the ID of the ASP.NET server controls and really forgot about the Name attribute. For the data to post back properly, a rendered control must have both the ID and Name attributes specified. In this example (and in virtually any case I can think of), I use the same value for ID and Name: the UniqueID of the control.

Tip A point of confusion is the difference between server-side and client-side events. In the ReformatDate control, it's clear that the OnChange event is a client-fired event, but in some cases, this distinction can be confusing. If you're in doubt as to whether you're properly setting the client-side events from a server-side control, you can always view the control in a Web browser and view the source. This technique is a powerful tool in resolving problems with how the control is rendered. In the OnLoadserver-side method of ReformatDate, I frst call the ancestor OnLoad method, by calling base.OnLoad. Remember that in Visual Basic .NET, this would be MyBase.OnLoad. Since this class is derived from BaseValidator, the ancestor class OnLoad must be called because it allows the component to emit the required JavaScript that any validator needs.

Caution Using BaseValidator as the class that ReformatDate derives from is not an obvious choice. You might want to create a composite control that contains two server-side controls: a TextBox control and a CustomValidator control. This option would be reasonable, but in the end, not as good a solution as deriving from BaseValidator (especially in light of what the ReformatDate control example is trying to show—client-side code integrated into a control). The BaseValidator class allows the ReformatDate control to participate in deciding whether or not the page is valid. Next, I call Page.RegisterClientScriptBlock to send the client script to the browser. Why use this method rather than just sending out the script directly? The reason is to ensure that if there is more than one ReformatDate control on a page, only a single copy of the script is written per page. Page.RegisterClientScriptBlock expects two parameters: a key, used to uniquely identify the script block, and the actual script. Notice that I use the Src attribute of the <SCRIPT> tag to include a file, rather than trying to embed the entire script in a string. This technique allows you to centralize scripts and also to correct the client-side script without requiring a recompile of the control using it.

Listing 7-8 shows the JavaScript file FormatDate.js.

Was this article helpful?

0 0

Post a comment