Saturday, October 1, 2011
JavaScript with ASP .NET server controls
Introduction
Before ASP .NET, there was ASP; before ASP, there was JavaScript. While ASP has been gradually marginalized by the rapid succession of ASP .NET 1.x, 2.x and now 3.x, JavaScript remains the sole source and foundation of snappy client-side user experience.If the time of existence is of any indication, the already gigantic and still rapidly growing repertoire of JavaScript is a force we cannot afford to ignore. The successes of various Google applications offer the best testimony (a testimony for Ajax too).
On the other hand, ASP .NET, especially the vastly improved ASP .NET 2.0 is undeniably a powerful and flexible development environment. The suite of systems (such as
membership
, role
and profile
, health monitoring
), server controls (GridView
, DataList
, etc.) and master page, skin and theme relieve programmers much of the burden of doing the same plumbing over and over again, and allow them to concentrate on individual site-based business logic instead.However the flip side of the coin is the server-centric-ness of ASP .NET. It relies almost exclusively on the server to deal with user interactions. With a slow network connection, ASP .NET applications would seem to be slow responding or not responding at all; if you have a deep-colored background, you get very unpleasant fits of white-flashes. Yes, ASP .NET has taken great effort to address the issues, introduced methods and properties to handle common tasks such as popping up alert or confirmation boxes, setting focus on server controls; created
ClientScript
class to allow JavaScript to be included with a page or server controls. You may refer to JavaScript with ASP .NET Pages for more details. However, its highly encapsulated declarative-based syntax still poses great challenges for programmers to go beyond the pre-cut patterns and practices and inject into server controls individualized client side responses. So how do we leverage the power of ASP .NET and the nimbleness of JavaScript to create a powerful website with rich UI experience?
You may say: ASP .NET AJAX.
Yes. And No.
ASP .NET AJAX, or AJAX, is the hotly pursued technology. I, like everyone else, has jumped on this train of Mr. popular. However, like any other technology, ASP .NET technology can be misused and abused.
In a blog article entitled "Past the AJAX Hype - Some things to think about" in 2005 (while outdated it still has a grain of truth), Rick Stahl writes about the reasons why AJAX should not be the answer to every prayer.
- "Grafting AJAX onto existing applications adds another layer of complexity to your application”
- "With AJAX, the client code gets large very quickly".
- "You should also consider what impact AJAX has on your application’s scalability. AJAX tends to increase the number of requests on a back end application considerably."
There are scenarios where AJAX offers the only acceptable solution. That is when server data retrieval is needed and a whole-page refreshing to be avoided. However in other common-place scenarios, such as show and hide a page section based on user input, zoom in and out of an image in responding to a user's mouse movement, AJAX should not be rushed in an ASP .NET page as the rescue. Client-side only reactions should be left to client-side only scripts.
And there is a whole world of JavaScript for us to take advantage of.
So let's jam with ASP .NET and JavaScript code with examples. To demonstrate how we can take advantage of the tons of snappy JavaScript that is out there, I downloaded most of the script for my examples (JavaScript source for this article is mostly from Dynamic Drive DHTML).
Check out the live demo for this article here.
Greybox with ASP .NET server controls
One rule of thumbs of website design is to try to keep the number of pop up windows to a minimum (normal pop-up windows usingwindow.open()
call may not work anyway because of the prevalence of popup blockers), another one is try not to have (too many) links away, i.e., links to external websites channeling away traffic.There have been some work-around scripts for creating non-intrusive and within-site navigation popup. ASP .NET AJAX ModalPopup is one way to do it in ASP .NET Ajax. However, I much prefer the Greybox created by Orangoo Labs. Greybox can have wide use ranging from login, small gallery and external-web page links, etc.
The installation is very easy and business-as-usual; you can simply download and stick the JavaScript references in the head section of a page.
Turns out that installing a Greybox for use with an ASP .NET page is very simple.
The following example, slightly modified from the original example, attaches to GreyBox to a server-side button and a hyperlink.
Step 1: Reference the Greybox JavaScript source in your header section, as instructed in the original JavaScript.
Listing 1: Step 1: Reference the Greybox JavaScript
01.
<
head
id
=
"Head2"
runat
=
"server"
>
02.
<
title
>GreyBox with ASP .NET Examples</
title
>
03.
<
script
type
=
"text/javascript"
>
04.
var GB_ROOT_DIR = "./greybox/";
05.
</
script
>
06.
<
script
type
=
"text/javascript"
src
=
"greybox/AJS.js"
></
script
>
07.
<
script
type
=
"text/javascript"
src
=
"greybox/AJS_fx.js"
></
script
>
08.
<
script
type
=
"text/javascript"
src
=
"greybox/gb_scripts.js"
></
script
>
09.
<
link
href
=
"greybox/gb_styles.css"
rel
=
"stylesheet"
type
=
"text/css"
10.
media
=
"all"
/>
11.
<
script
type
=
"text/javascript"
src
=
"static_files/help.js"
></
script
>
12.
<
link
href
=
"static_files/help.css"
rel
=
"stylesheet"
type
=
"text/css"
13.
media
=
"all"
/>
14.
</
head
>
Page_load
event by using attributes.add
in the code behind page. Please note that GreyBox heavily utilizes the rel
attribute of a link tag. The following example launches a small image gallery on button click and then launches the DotNetSlackers website upon a click of the hyperlink.
Listing 2: The Web Form with a button and a hyperlink
01.
<
form
id
=
"form2"
runat
=
"server"
>
02.
<
script
type
=
"text/javascript"
>
03.
var image_set = [{'caption': 'Flower', 'url':
05.
{'caption': 'Nice waterfall', 'url':
07.
</
script
>
08.
<
asp:Button
ID
=
"button1"
runat
=
"server"
text
=
"Click me!"
/><
br
/><
br
/>
09.
<
asp:HyperLink
runat
=
server
ID
=
"link1"
Text
=
"Click me"
10.
NavigateUrl
=
"http://www.dotnetslackers.com"
></
asp:HyperLink
>
11.
</
form
>
Listing 3: Code Behind
1.
protected
void
Page_Load(
object
sender, EventArgs e)
2.
{
3.
button1.Attributes.Add(
"onClick"
,
"return GB_showImageSet(image_set, 1)"
);
4.
link1.Attributes.Add(
"rel"
,
"gb_page_center[640, 480]"
);
5.
}
Figure 1: Screenshot of Greybox with server controls
Check out the demo for the code in action.
JavaScript with List controls and how to show/hide a layer
ASP .NET list controls include RadioButtonList, CheckBoxList, DropDownList andListBox
. In the ASP .NET 1.x times, it was a known bug that the regular attributes.add (key, function
) didn’t work for list controls. For example, you cannot apply special CSS styles to a particular checkbox if it is checked; and you cannot attach a JavaScript function to a RadioButtoList’s OnClick
event. To make your list controls have individual ListItem specific reactions or behaviors, you have to write custom Web Controls inheriting from ListControl. It is not exactly a trivial task (To read more, please see List Control Items and Attributes).Luckily, we have ASP .NET 2.0 which has swept away bugs like this. However, how do we style different list items? How do we set up JavaScript functions in response to client-side actions, for example, in a common scenario where you want to display a set of questions only if a user has answered "Yes" to a screening question?
We can dress individual List Items in the code-behind page, since it is not possible to set the style declaratively. For example, the following code sets the background color of each list item of a DropdownList of colors.
1.
foreach
(ListItem li
in
2.
3.
dropdownlist1.Items)
4.
li.Attributes.Add(
"style"
,
"background-color:"
+ li.Text);
autopostback
to True
, and set the property visible
to either False
or True
. To avoid the annoying whole-page refreshing and unpleasant post back flash, you can also use an UpdatePanel.The following example is directly plucked out of a survey. It displays the Government-related question if a user indicates that she/he works for Government or Civic agency, or the "other" textbox to allow user to specify the type of work he/she works on.
Listing 4: Using UpdatePanel to show/hide a specific set of questions based on users’ response
01.
<
asp:UpdatePanel
ID
=
"UpdatePanel1"
runat
=
"server"
><
ContentTemplate
>
02.
<
p
>Which best describes your work?
03.
<
asp:RadioButtonList
ID
=
"q1"
runat
=
"server"
AutoPostBack
=
"true"
04.
repeatDirection
=
"vertical"
OnSelectedIndexChanged
=
"q1_SelectedIndexChanged"
>
05.
<
asp:ListItem
value
=
"1"
>Not-for-profit</
asp:ListItem
>
06.
<
asp:ListItem
value
=
"2"
>Foundation/corporate philanthropy</
asp:ListItem
>
07.
<
asp:ListItem
value
=
"3"
>Private sector</
asp:ListItem
>
08.
<
asp:ListItem
value
=
"4"
>Government/civic agency</
asp:ListItem
>
09.
<
asp:ListItem
value
=
"5"
>Educational institution/agency</
asp:ListItem
>
10.
<
asp:ListItem
value
=
"6"
>Faith-based organization</
asp:ListItem
>
11.
<
asp:ListItem
value
=
"7"
>Independent consultant</
asp:ListItem
>
12.
<
asp:ListItem
value
=
"8"
>Student</
asp:ListItem
>
13.
<
asp:ListItem
value
=
"9"
>Other
14.
</
asp:ListItem
>
15.
</
asp:RadioButtonList
>
16.
</
p
>
17.
<
div
id
=
"divQlgov"
runat
=
"server"
visible
=
"false"
>
18.
<
b
>Government/civic agency?
19.
</
b
>
20.
<
asp:RadioButtonList
ID
=
"q1gov"
runat
=
"server"
RepeatDirection
=
"Horizontal"
>
21.
<
asp:ListItem
Value
=
"1"
>Local</
asp:ListItem
>
22.
<
asp:ListItem
Value
=
"2"
>State</
asp:ListItem
>
23.
<
asp:ListItem
Value
=
"3"
>Federal</
asp:ListItem
>
24.
</
asp:RadioButtonList
>
25.
</
div
>
26.
<
div
id
=
"divQ1other"
visible
=
"false"
runat
=
"server"
>
27.
<
b
> Other (Specify)</
b
>
28.
<
asp:TextBox
id
=
"q1oth"
runat
=
"server"
/>
29.
</
div
>
30.
</
ContentTemplate
>
31.
</
asp:UpdatePanel
>
q1_SelectedIndexChanged
function as such:1.
protected
void
q1_SelectedIndexChanged(
object
sender, EventArgs e)
2.
{
3.
divQ1other.Visible = (q1.SelectedIndex == q1.Items.Count - 1);
4.
divQlgov.Visible = (q1.SelectedIndex == 3);
5.
}
However, we can do it in a simpler, client-centric way, instead of posting back every user mouse movement to server. First, to each ListItem we add an
OnClick
attribute; then, rather than wrap the selective set of questions in a server-side control and further wrap it up in UpdatePanel/ContentTemplate
tags, we use a client-side layer and adjust its style.display
property to either block
or none
.For example, we can always rewrite the above server code as follows:
Listing 5: Add JavaScript to server list controls to turn off/on a set of specific questions based on user’s response
01.
<
p
>Which best describes your work?
02.
<
asp:RadioButtonList
ID
=
"q1"
runat
=
"server"
repeatDirection
=
"vertical"
>
03.
<
asp:ListItem
value
=
"1"
>Not-for-profit</
asp:ListItem
>
04.
<
asp:ListItem
value
=
"2"
>Foundation/corporate philanthropy</
asp:ListItem
>
05.
<
asp:ListItem
value
=
"3"
>Private sector</
asp:ListItem
>
06.
<
asp:ListItem
value
=
"4"
>Government/civic agency</
asp:ListItem
>
07.
<
asp:ListItem
value
=
"5"
>Educational institution/agency</
asp:ListItem
>
08.
<
asp:ListItem
value
=
"6"
>Faith-based organization</
asp:ListItem
>
09.
<
asp:ListItem
value
=
"7"
>Independent consultant</
asp:ListItem
>
10.
<
asp:ListItem
value
=
"8"
>Student</
asp:ListItem
>
11.
<
asp:ListItem
value
=
"9"
>Other
12.
</
asp:ListItem
>
13.
</
asp:RadioButtonList
>
14.
</
p
>
15.
<
div
id
=
"divGov"
style
=
"display:none"
>
16.
<
b
>Government/civic agency?
17.
</
b
>
18.
<
asp:RadioButtonList
ID
=
"q1gov"
runat
=
"server"
RepeatDirection
=
"Horizontal"
>
19.
<
asp:ListItem
Value
=
"1"
>Local</
asp:ListItem
>
20.
<
asp:ListItem
Value
=
"2"
>State</
asp:ListItem
>
21.
<
asp:ListItem
Value
=
"3"
>Federal</
asp:ListItem
>
22.
</
asp:RadioButtonList
>
23.
</
div
>
24.
<
div
id
=
"divOther"
style
=
"display:none"
>
25.
<
b
> Other (Specify)</
b
>
26.
<
asp:TextBox
id
=
"q1oth"
runat
=
"server"
/>
27.
</
div
>
Listing 6: JavaScript function to show/hide a layer
01.
function
ShowHide(divName, OnOff){
02.
var
ele = document.getElementById(divName);
03.
if
(ele !=
null
){
04.
if
(OnOff ==
"on"
)
05.
ele.style.display =
"block"
;
06.
else
07.
ele.style.display =
"none"
;
08.
}
09.
}
attributes.add
(). Since there are two layers involved in the example, we call the same JavaScript function twice with different parameters. Listing 7: Add JavaScript function calls to individual ListItem
01.
foreach
(ListItem li
in
q1.Items) {
02.
//if government selected, show government questions
03.
if
(li.Value ==
"4"
)
04.
li.Attributes.Add(
"Onclick"
,
05.
"ShowHide('divGov','on');ShowHide('divOther','off')"
);
06.
//if other selected, show the other textbox
07.
else
if
(li.Value ==
"9"
)
08.
li.Attributes.Add(
"Onclick"
,
09.
"ShowHide('divGov','off');ShowHide('divOther','on')"
);
10.
else
11.
li.Attributes.Add(
"Onclick"
,
12.
"ShowHide('divGov','off');ShowHide('divOther','off')"
);
13.
}
Now let's try to change the logic a little bit. Let’s use a RadioButtonList to check or uncheck all of the checkbox items in a CheckBoxList, which is also a very common scenario.
Insert the following JavaScript function in the web form.
Listing 8: JavaScript: Check or uncheck all of the items in the checkboxList
01.
function
Check(sender, checklistName, total)
02.
{
03.
if
(sender.value ==
"1"
)
04.
for
(
var
i = 0; i < total;i++)
05.
document.getElementById(checklistName +
"_"
+ i).checked =
true
;
06.
else
07.
for
(
var
i = 0; i < total;i++)
08.
document.getElementById(checklistName +
"_"
+ i).checked =
false
;
09.
}
Listing 9: The web form with a RadioButtonList and a CheckBoxList
01.
From where did you hear about us?
02.
<
asp:RadioButtonList
ID
=
"raioList1"
runat
=
"server"
>
03.
<
asp:ListItem
Value
=
"1"
>All the Following</
asp:ListItem
>
04.
<
asp:ListItem
Value
=
"2"
>None of the Following</
asp:ListItem
>
05.
</
asp:RadioButtonList
>
06.
<
asp:CheckBoxList
ID
=
"checkList1"
runat
=
"server"
>
07.
<
asp:ListItem
Value
=
"1"
>TV/Radio</
asp:ListItem
>
08.
<
asp:ListItem
Value
=
"2"
>Newspaper</
asp:ListItem
>
09.
<
asp:ListItem
Value
=
"3"
>Friends</
asp:ListItem
>
10.
<
asp:ListItem
Value
=
"4"
>Internet</
asp:ListItem
>
11.
</
asp:CheckBoxList
>
Listing 10: Call the JavaScript from code-behind
1.
foreach
(ListItem li
in
raioList1.Items)
2.
{
3.
li.Attributes.Add(
"Onclick"
,
"Check(this,'"
+ checkList1.ClientID +
"',"
+
4.
checkList1.Items.Count+
")"
);
5.
}
sender
, the object that evokes the function; checklistName
, the collective name or the prefix of the array of checkboxes in concern; total
: the total number of checkboxes in the list. On the server side, we pass the
this
keyword to refer to the sender itself; the ClientID
of the CheckboxList as ChecklistName
; and items.count
for the total number of checkboxes.Popup in a GridView
Popup is the darling of web programmers; we always try to outsmart popup-blockers. Good popup are non-intrusive, informative, nimble and visually appealing. Popup are as popular asGridView
is powerful. How then to allow a little popup bubbling up while you mouse over each data item of a GridView
? I have seen examples using AJAX for GridView
popup. However, I doubt it is absolutely necessary.How about using one of the pop up JavaScript out there and combine it with our powerful and data-rich
GridView
?The result is the following:
Figure 2: Screenshot of information popup with a GridView
Here is how:
First, download the JavaScript file (
tooltip.js
in the sample code) and reference it in your ASP .NET page;Second, set up your Gridview. In my example, I created a dummy DataTable as the Gridview's
DataSource
(I’ve stripped out the database part to make it simpler); 1.
<
asp:GridView
ID
=
"GridView1"
runat
=
"server"
AutoGenerateColumns
=
"False"
2.
DataKeyNames
=
"id"
3.
OnRowCreated
=
"GridView1_RowCreated"
CellPadding
=
"4"
ForeColor
=
"#333333"
4.
GridLines
=
"None"
>
attribute.add()
to add a note to OnRowCreated
event the of Gridview to call the JavaScript function whenever a mouse is detected hovering over the magnifier image, to pop up a small information box.Listing 11: Code for the OnRowCreated event
01.
protected
void
GridView1_RowCreated(
object
sender, GridViewRowEventArgs e)
02.
{
03.
if
(e.Row.RowType == DataControlRowType.DataRow)
04.
{
05.
// Programmatically reference the Image control
06.
Image image1 = (Image)e.Row.Cells[1].FindControl(
"Image1"
);
07.
HyperLink link1 = (HyperLink)e.Row.Cells[1].FindControl(
"link1"
);
08.
link1.Attributes.Add(
"onmouseover"
,
"doTooltip(event,"
+
09.
e.Row.RowIndex.ToString() +
")"
);
10.
link1.Attributes.Add(
"onmouseout"
,
"hideTip()"
);
11.
}
12.
}
Custom Validation with JavaScript
All the ASP .NET built-in validators - RequiredFieldValidator, CompareValidator, RangeValidator and RegularExpressionValidator are provided with JavaScript as default. You have to set theEnableClientScript
property of the validator or ValidationSummary control to False
to disable client script validation.For validations that require special logic - for example, a control's input that needs to check against the input of multiple other controls - the CustomValidator class comes into play. CustomValidator allows us to do validation both on the server and client side, and it is a common practice to place identical validation functions on both sides (I do not know why). Here, we will only focus on the side of client script, since it involves some basics of using the mini-client-side validation API and accessing server controls locally.
Listing 12: A custom validator to validate a group of textboxes to ensure their percentage values add up to 100
01.
<
asp:CustomValidator
id
=
"CustomValidator1"
runat
=
"server"
02.
ErrorMessage
=
"Total Percentages must add up to 100!"
03.
ClientValidationFunction
=
"CheckTotal"
Display
=
"Dynamic"
/><
br
>
04.
Please indicate the percentage of your daily expenditure:<
br
/>
05.
Grocery: <
asp:TextBox
id
=
"txtGrocery"
runat
=
"server"
Text
=
"0"
/><
br
/>
06.
Entertainment: <
asp:TextBox
id
=
"txtEntertainment"
runat
=
"server"
Text
=
"0"
/><
br
/>
07.
Other:<
asp:TextBox
id
=
"txtOther"
runat
=
"server"
Text
=
"0"
/><
br
/>
08.
<
script
language
=
"javascript"
>
09.
<!--
10.
function CheckTotal(source, args) {
11.
var ele1 =document.getElementById("<%=txtGrocery.ClientID%>");
12.
var ele2 =document.getElementById("<%=txtEntertainment.ClientID%>");
13.
var ele3 =document.getElementById("<%=txtOther.ClientID%>");
14.
var val1 = ele1.value;
15.
var val2 = ele2.value;
16.
var val3 = ele3.value;
17.
if (isNaN(val1) ||isNaN(val2) || isNaN(val3) ) {
18.
args.IsValid = false;
19.
}
20.
else {
21.
args.IsValid = ((val1/1 + val2/1 + val3/1) == 100);
22.
}
23.
}
24.
// -->
25.
</
script
>
- To perform client-side custom validation, we must indicate the name of function in the
ClientValidationFunction
property. As in the above:ClientValidationFunction
="CheckTotal
". - The custom function must always have the same two parameters:
source
andarguments
(translated on the server-side:customValidate (object sender, EventArgs e
)).Source
is the client-sideCustomValidator
object, andarguments
is an object with two properties,Value
andIsValid
. TheValue
property is the value to be validated and theIsValid
property is a Boolean used to set the return result of the validation. - In the case where we need to access the value of other server controls, we use
document.getElementById ("<%=server control ID.ClientID% >")
to access the server control using itsClientID
. - The
IsValid
property is false by default. Always explicitly setIsValid
property to true if the values are validated. - You can leave out the
ControlToValidate
property. One CustomValidator can be used for a whole web form, in which case, the custom validation function will be fired only once.
Take a look at the Ajax Control Toolkit
"The ASP.NET AJAX Control Toolkit is a joint project between the community and Microsoft that provides a rich array of controls for building interactive Web experiences easily." As of now, the sample website showcases about thirty-four samples of web control extenders, such as Accordion, Animation, AutoComplete.Extenders are server controls that allow a server control to imitate client-side behaviors. For example, the Accordion allows you to provide multiple panes that can expand or collapse depending on user's mouse click; only one pane will be visible at one time.
If you are not using Visual Studio 2008, or ASP .NET 3.5; to enable extenders to work with web controls, you must first configure your web site to be AJAX enabled, and then you must install the AjaxControlToolkit assembly. Finally, you add the controls to your Toolbox. Then you can drag and drop Toolkit's extenders in your page. It is not very difficult to do, and you do not need to write any JavaScript code to have the benefit of JavaScript, which could be a plus for programmers that do not have any JavaScript skills.
However, in what planet can you develop a rich and fast- responding website with only spoon-fed canned formulas and no JavaScript at all?
To me, the problem with Ajax extenders is that, it is not at all easy to plow through the obtuse process of creating extenders of your own. Given the meager number of extenders available on the sample website and the vast and varied requirement for rich client capabilities, you will soon be out of luck. And think of the world of JavaScript (text effects, image effects, links and menus, mouse and cursors) that are available to download!
Among the extenders in the Ajax Control Toolkit, the auto-complete extender is a good and necessary one, because to provide a list of suggested words to aid users filling their content box, it involves retrieving data (sometimes a heavy amount of data) from server upon every keystroke, which client script could not do. However, others, in my view, are not quite so necessary. It is easier still to cherry-pick from the available JavaScript and directly apply to a web control.
Take the CollapsiblePanel for example. Collapse and expand a layer has been a long-standing JavaScript function. A search will quickly turn up many JavaScript for this purpose. So I downloaded one and added the reference in the web form
head
section - and with a few lines, I got a nice working collapsible panel. No Toolkit’s .dll plug in, no AJAX configuration.Listing 13: The accordion web form
01.
<
div
style
=
"background-color:navy; width:400px"
>
02.
<
asp:Label
Font-Bold
=
"true"
Text
=
"What is ASP .NET AJAX (hide details...)"
03.
ForeColor
=
"white"
ID
=
"label1"
runat
=
"server"
></
asp:Label
>
04.
<
asp:HyperLink
ImageUrl
=
"expand_blue.jpg"
runat
=
"server"
05.
ID
=
"linkCollapse"
></
asp:HyperLink
><
br
/><
br
/>
06.
</
div
>
07.
<
div
id
=
"DivContent"
style
=
"width: 300px; background-color: #99E0FB;"
>
08.
<!--Your DIV content as follows. Note to add CSS padding or margins, do it inside
09.
a DIV within the Collapsible DIV -->
10.
<
div
style
=
"padding: 0 5px"
>
11.
ASP.NET AJAX is a free framework for building a new generation of richer, .....
12.
</
div
>
13.
</
div
>
14.
<
script
type
=
"text/javascript"
>
15.
//Syntax: var uniquevar=new animatedcollapse("DIV_id", animatetime_milisec,
16.
enablepersist(true/fase), [initialstate] )
17.
var collapse2=new animatedcollapse("DivContent", 800, true)
18.
</
script
>
1.
linkCollapse.Attributes.Add(
"Onclick"
,
"javascript:collapse2.slideit()"
);