Pages

Advertisement

Friday, July 13, 2007

Authenticate in ASP without NT

There is a simple alternative to NT authenication is to cheat. Here's a "no NT involved" version of security.inc; just put

<!--#INCLUDE FILE="security.inc"--> 

at the top of each ASP page you want to protect, and put this in security.inc:
<%

'does the session know the user?

UserID=Session("UserID")

Rejected=False



If IsEmpty(UserID) Or IsNull(UserID) Or UserID="" Then

Attempted=False



'Figure out who we are, for later

URL=Request.ServerVariables("QUERY_STRING")

If IsEmpty(URL) Or URL="" Then

URL="" ' just in case

Else

URL="?" & URL

End If

URL=Request.ServerVariables("SCRIPT_NAME") & URL



'check for POSTed authentication information

UserID=Request.Form("UserID")

UserPWD=Request.Form("UserPWD")



If IsEmpty(UserID) Or IsNull(UserID) Or UserID="" Then

Rejected=True

Else

' insert your own checking here -- this is deliberately lame

If UserID="Foo" AND UserPWD="Foo" Then

'wahoo!

'set the session variable

Session("UserID")=UserID

Rejected=False

Else

Attempted=True

Rejected=True

End If

End If

End If



If Rejected Then

If Attempted Then

Title="Authentication Failure"

Else

Title="Please Authenticate"

End If

%>

<!--#INCLUDE FILE="authentication_form.htm"-->

<%

Response.End 'stop processing before it gets back to your page

End If

' ... otherwise, on with your normal page.

%>

The authentication page (authentication_form.htm) could look like this:
	&lthtml>&lthead>&lttitle><%=Title%></title></head>

&lth1><%=Title%></h1>

&ltform action="<%=URL%>" method="POST">

Username: &ltinput type="text" name="UserID" size="20">&ltbr>

Password: &ltinput type="password" name="UserPWD" size="20">&ltbr>

&ltinput type="submit">

</form>

</html>



You could just as easily paste this HTML in where the INCLUDE is, but it makes it a little harder to edit using FrontPage. Note that anyone trying to hit authentication_form.htm is going to find it a little... well, useless. I'll leave it as an exercise to the reader how to get around this.
How does security.inc work?
If the user has authenticated already, security.inc notices that the UserID session variable is set and passes control back to your page. If they haven't, it sends them a form which asks for their username and password. When they submit that information, security.inc checks it and either gives them the form again or passes control back to your page.
The extra code is there to tweak the form if the user failed authentication (as opposed to simply not having authenticated yet), and to preserve any query information in the URL.
Note that if the user doesn't accept the ASP cookie (or is using a non-cookie-aware browser), the session variable won't be preserved and they'll be continuously asked to re-authenticate. You should modify the authentication page so that it warns users of this.

Rendering Data with ASP.NET Web Controls

Introduction :

ASP.NET provides several Web controls that make displaying data on a Web page easier than ever before. This chapter shows you how to take advantage of a process called data-binding to easily display data in a variety of formats using very little code. This chapter covers many of the most commonly used features of the Repeater, DataList, and DataGrid, including some fairly advanced DataGrid features. Using these data-bound Web controls, it is very easy to write data-driven Web Forms by just dragging and dropping a few controls onto a form and writing a few lines of code.

Rendering Data Directly on a Web Form :

You want to display a piece of data on a Web Form using data-binding

Technique

The Repeater control is the simplest of three templated data-bound controls provided with ASP.NET (the others being the DataList and DataGrid). It supports templates for Header, Item, AlternatingItem, Separator, and Footer, which can each contain static and dynamic (data-bound) content. The following example demonstrates how to set up a Repeater's templates and how to data-bind the Repeater to a dataset.

The ASPX page is as follows:

 

<asp:Repeater id="Repeater1" runat="server"> <HeaderTemplate>Customers:<br/><ul></HeaderTemplate> <ItemTemplate> <li><%#DataBinder.Eval(Container.DataItem, "CompanyName")%>, <%#DataBinder.Eval(Container.DataItem, "ContactName")%></li> </ItemTemplate> <AlternatingItemTemplate> <li><font color="red"> <%#DataBinder.Eval(Container.DataItem, "CompanyName")%>, <%#DataBinder.Eval(Container.DataItem, "ContactName")%></font></li> </AlternatingItemTemplate> <FooterTemplate><hr/>Data Retrieved at: <%# System.DateTime.Now.ToString() %></FooterTemplate> </asp:Repeater> <br> <br> <asp:Label id="errorMsgLabel" runat="server" Width="327px" Height="111px"></asp:Label>

In <script runat="server" /> block or codebehind:

Sub BindRepeater() 'object vars Dim sqlConnection As SqlConnection Dim sqlDataAdapter As SqlDataAdapter Dim sqlCommand As SqlCommand Dim dataSet As DataSet Dim dataTable As DataTable Try sqlConnection = New SqlConnection("Integrated Security=yes; _ Initial Catalog=Northwind; _ Data Source=(local)") 'pass the stored proc name and SqlConnection sqlCommand = New SqlCommand("Select * From Customers", _ sqlConnection) 'instantiate SqlAdapter and DataSet sqlDataAdapter = New SqlDataAdapter(sqlCommand) dataSet = New DataSet() 'populate the DataSet sqlDataAdapter.Fill(dataSet, "Customers") 'apply sort to the DefaultView to sort by CompanyName dataSet.Tables(0).DefaultView.Sort = "CompanyName" Repeater1.DataSource = dataSet.Tables("Customers").DefaultView Repeater1.DataBind() Catch exception As Exception errorMsgLabel.Text = exception.ToString() End Try End Sub

 

 

Implementing a Fixed GridView Header in ASP.NET

For all their fancy extras, ASP.NET Web controls are basically little engines that spit out HTML. Don't get me wrong; the controls are clever and helpful engines, but it is important to know that the output is HTML.

This article looks at two new ASP.NET Web controls, the GridView and the Panel. The GridView is an updated variant of the basic grid, and the Panel supports a locatable, scrollable region. The ability to scroll helps you pack more information in the available real estate on a Web page. The Panel outputs a <div>, and the GridView (like the DataGrid before it) outputs a <table>.

The text that follows demonstrates how to use styles, a Panel, and the GridView to effectively create a scrollable grid with a fixed header.

Adding a New ASP.NET Panel Control

The ASP.NET Panel control outputs a <div>. This means that you can manipulate the Panel at design time using the Properties window; you can manipulate the Panel at runtime with code behind; and you can manipulate the Panel on the client side as its rendered <div>.

The effect created in the code samples is accomplished using HTML and styles. The technique should work with older ASP applications and earlier ASP.NET applications. (If you are not using Visual Studio 2005, you can try to repeat the effect using plain HTML.)

Defining the Panel Styles

The first step is to add an ASP.NET Panel to a Web page. The Panel is on the Standard tab of the Toolbox. Change the Panel.Class property in the Properties window to fixedHeader. Next, you need to add and define the fixedHeader style.

Note: If you are using the professional version of ASP.NET 2005 or Web Express, some features may not be available. For example, you may not be able to visually set the stylesheet property, but you can still use stylesheets. If you have to link the stylesheet in manually, check Google for information on the <link> tag.

Generally, when you add styles, you add a cascading style sheet (CSS) to your project and reference the stylesheet from your page. There is a StyleSheet property for the Document (Web page), and you can either link the stylesheet with this property or manually add the <link> tag. Additionally, you can build the styles from the Style (builder) property or add the styles in the ASP/HTML using the embedded <style> tag within the <head> tag. The example in this article uses the latter approach to show you the entire listing as one page when you are finished with the sample (see Listing 1).

Listing 1: A Partial List of a Web Page Showing the Embedded <style> Tag

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional
//EN" "http://www.w3.org/TR/xhtml1/DTD
/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Untitled Page</title>
<style type="text/css">
.fixedHeader
{
overflow: auto;
height: 150px;
}
</style>

</head>
<body>
<form id="form1" runat="server">

The <style> tag is added inside the <head> (header) tag section of the page. Listing 1 defined a style named fixedHeader. Within that style, it indicated that overflow is auto and the height is 150 pixels. Overflow indicates how to manage the content of an object when its contents exceed the height and width of the object. The auto value means that content is clipped and scrollbars are added automatically. A height of 150 pixels is added to ensure that you can easily force the Panel's content to overflow without adding tons of data.

Adding a GridView to the Panel

The next step is to add a GridView to the Panel. For the purposes of this demonstration, the GridView works similarly to the DataGrid. To create the fixed header effect, add data to the grid and define an additional style for the <Table> output by the grid at runtime. Listing 2 contains some sample VB code that adds some arbitrary test data to the GridView. (Any supported code can be used to test the scrolling behavior.)

Listing 2: Sample Code for Testing the GridView

Imports System.Collections.Generic

Partial Class _Default
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load

Dim list As List(Of Data) = New List(Of Data)
Dim I As Integer
For I = 1 To 100
list.Add(New Data("String" + I.ToString(), I))
Next

GridView1.DataSource = list
GridView1.DataBind()

End Sub
End Class

Public Class Data
Private FColumn1 As String
Private FColumn2 As Integer

Public Sub New(ByVal c1 As String, ByVal c2 As String)
FColumn1 = c1
FColumn2 = c2
End Sub

Public Property Column1() As String
Get
Return FColumn1
End Get
Set(ByVal value As String)
FColumn1 = value
End Set
End Property

Public Property Column2() As Integer
Get
Return FColumn2
End Get
Set(ByVal value As Integer)
FColumn2 = value
End Set
End Property
End Class

Listing 3 shows a revised Listing 1, containing the new style for the GridView-cum-table.

Listing 3: Listing 1 Revised to Contain the Additional Style Definition

<%@ Page Language="VB" AutoEventWireup="false"
CodeFile="Default.aspx.vb" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Untitled Page</title>
<style type="text/css">
.fixedHeader
{
overflow: auto;
height: 150px;
}

table th
{
border-width: 4px;
border-color: Black;
background-color: Gray;
position: relative;
top: expression(this.parentNode.parentNode.
parentNode.scrollTop-1);
}

</style>
</head>
<body>
<form id="form2" runat="server">

 


The new style applies to any table on this page with a subordinate table header (th). The first three properties defined a border and the background color of the header. The position attribute and the top attribute complete the fixed-header effect. Position relative means that the object is positioned relatively to the normal flow and then offset by the top and left properties. The top attribute is calculated as a result of the expression. The expression refers to the table header's ancestor three levels up, which is the table itself. This code should always work, but you can try getElementByID too.

Listing 4, a partial listing of the rendered HTML page, shows that the table header is nested three levels below the table. The first parent is <tr>, the second parent is <tbody>, and the third parent is the <table> itself.

Listing 4: Partial Listing of the Rendered HTML Page

<div id="Panel1" class="fixedHeader"
style="height:150px;width:200px;overflow-y:scroll;">
<div>
<table cellspacing="0"
rules="all"
border="1"
id="GridView1"
style="border-collapse:collapse;">
<tr>
<th scope="col">Column1</th><th scope="col">Column2</th>
</tr>

When you have added the table th style and run the code, the grid should appear to scroll while the header remains in place (see Figure 1). The technique works pretty well, but the thick table header border is used to hide text that scrolls above the header. Also, you may find it difficult to tuck the information rows—everything but the header—up tight against the header without scrolling data above the header. Some experimentation may be necessary to get the header and nested grid to look natural in the panel.


Figure 1: The GridView Scrolled Halfway Past the Fourth Item

Underneath It All, Just HTML

Good old HTML knowledge is still useful for accomplishing tasks like fixed grid headers.

This article used cascading styles and the new Panel and GridView to fix a header while permitting a grid to scroll. Although it used new controls to demonstrate the technique, it is important to note that ASP.NET Web controls emit HTML underneath, so the technique should work in a vanilla editor with plain HTML tags.

Combo Control

This is a control which will help to generate Tabs , Menus, Slider bars, and Tree views. Just in time dynamically from XML.

Background

We see many web pages with lots of controls. Most of the controls available in the market are quiet expensive and they do only one work (if it is a menu control we cant use for tab). This control has all the built in features including the security. For example, the user may want some of the items in the tab should be visible to a particular set of users say administrators and some less important to the common users. In that case this plays a important role.
This control is very easy to use. The control exposes some public properties to set the data, look 'n' feel and events for the panel. By setting these properties, the collapsible panel can be used to group information very nicely.

Using the code

The user control has four public properties:

Controltype = To mention the type of control (tab, tree, panel, menu).
Display = To set alignment of the control (only to Controltype =menu).
PageTitle = To set the pagetitle (only to Controltype = tab).
MainMenuSelected = To set the Main menu (only to Controltype = tab).
SubMenuSelected = To set the Submenu selected (only to Controltype = tab).
ImageUrl = To set the Image in the tree (only to Controltype = tree).

<%@ Register TagPrefix="CONTROL" Namespace="Controls" Assembly="FOURINONE"%>
 
// for tab
<CONTROL:FourinOne runat="server" PageTitle="Congress Type" Controltype="tab"
 MainMenuSelected="2" SubMenuSelected="1" />
 
// for tree
<CONTROL:FourinOne runat="server" Controltype="tree" />
 
// for panel bar
<CONTROL:FourinOne runat="server" Controltype="panel" />
 
// for menu - horizontal
<CONTROL:FourinOne runat="server" Controltype="menu" Display="h" />
 
// for menu - vertical
<CONTROL:FourinOne runat="server" Controltype="menu" Display="v" />

For generating the menu, tree, panel, tab dynamically the xml should be of the form

<Menu>
 <MainMenu Label="Registration" BaseURL="SearchCongress.aspx" MenuID="8"
  MainSecurity="AM" MenuSequence="1" ImageUrl="xp_documents.gif">
  <SubMenu Label="Search" URL="SearchCongress.aspx" MenuID="8"
  Security="AM" SubMenuID="9" SubMenuSequence="1" ImageUrl="xp_documents.gif"/>
 <SubMenu Label="Add Company" URL="CompanyDetails.aspx" MenuID="8" 
  Security="A" SubMenuID="6" SubMenuSequence="4" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="New Order" URL="NewOrder.aspx" MenuID="8" Security="AM"
  SubMenuID="7" SubMenuSequence="5" ImageUrl="xp_documents.gif"/>
 </MainMenu>
 <MainMenu Label="Maintenance" BaseURL="Congress.aspx" MenuID="6"
  MainSecurity="A" MenuSequence="4" ImageUrl="xp_documents.gif">
   <SubMenu Label="Congress Type" URL="Congress.aspx" MenuID="6" Security="A"
  SubMenuID="1" SubMenuSequence="1" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="Event Type" URL="EventType.aspx" MenuID="6" Security="A"
  SubMenuID="2" SubMenuSequence="2" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="Sales Type" URL="SalesType.aspx" MenuID="6" Security="A"
  SubMenuID="3" SubMenuSequence="3" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="Venue" URL="CongressVenue.aspx" MenuID="6" Security="A"
  SubMenuID="4" SubMenuSequence="4" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="Office" URL="Office.aspx" MenuID="6" Security="A"
  SubMenuID="6" SubMenuSequence="6" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="Sales Person" URL="SalesPerson.aspx" MenuID="6" Security="A"
  SubMenuID="7" SubMenuSequence="7" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="Tax Rate" URL="TaxRate.aspx" MenuID="6" Security="A"
  SubMenuID="8" SubMenuSequence="8" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="User" URL="User.aspx" Security="A" MenuID="6"
  SubMenuID="9" SubMenuSequence="9" ImageUrl="xp_documents.gif"/>
   <SubMenu Label="Option Type" MenuID="6" URL="PriceOption.aspx" Security="A"
  SubMenuID="10" SubMenuSequence="10" ImageUrl="xp_documents.gif"/>
 </MainMenu>
</Menu>

The MainSecurity attribute in the MainMenu node and Security attribute in the SubMenu node are used to implement the security in displaying the menu items.

To implement security in this control keep the UserType in the session for example (A for administrator) when the user logs on to the system. By comparing the value in session the security is implemented.

private bool isAuthorised(string sSecString, string sSecType){
  return(sSecString.IndexOf(sSecType)>=0);
 }

The isAuthorized method returns true if the user is authorized to view that page else it return 0. For exmple I have taken two users Administrator (A) and Moderator (M). I have mentioned the pages that can be viewed only my administrator as (A) and pages that can be viewed by both of them as (AM).

The menu can be configured using web.config.

      <add key="MenuXMLPath" value="/helper/" />
      <add key="MainMenuFontColor" value="#ffffff" />
      <add key="MainMenuShadowColor" value="#999999" />
      <add key="MainMenubGColor" value="#000088" />
      <add key="MainMenuMouseoverColor" value="#000088" />
      <add key="SubmenuBgColor" value="#cfd3d8" />
      <add key="SubmenuMouseoverColor" value="#ffffff" />
      <add key="SubmenuBorderColor" value="#0000cc" />
      <add key="SubmenuFontColor" value="#000000" />
      <add key="SubmenuMouseOverFontColor" value="#000000" />
      <add key="SubmenuShadowColor" value="#666666" />



Downloads

  • combo_control.zip - Combo Control
  • Dynamically Linked Comboboxes Set

    If you frequently create comboboxes range-bounded with a database and have tired to write the same code, this article for you. During enter or searching process on a Web site, you often need to use a selected element in the first combobox to fill up the second comboboxfrom the database, a selected element in the second combo to fill up the third combo, and so forth. At the same time, you might need to a page refresh without a submit as well.

    Realization

    I attempted to solve this problem by realizing a Custom control idea implemented in ASP .NET and well explained in Jeff Prosize's book Programming Microsoft .NET. To realize refreshing w/o submit, I organized my .aspx file in a frameset with one hidden frame; this one pumps all data from the database and fills up comboboxes in visible frame without submitting it. This realization must job on all browsers that support carrying over an OPTION object of the SELECT tag between frames (in particular, IE 5.5 and higher).

    Source Code

    The control's server source is in LocControl.cs, the hidden frame's code-behind in HiddenFrm.aspx.cs. In both, two functions for filling up comboboxes by a SQL or OLE DB data provider have been realized. To activate the SQL data provider, it is necessary to add the SqlServer="true" attribute in kar tag; otherwise, the control will use the OLE DB provider and the developer must apply the appropriate Connection string. The client-side code is in scripts.js.

    Let us describe the outline of the program's job.

    1. The custom control begins its job from the source provided in Loccontrol.cs and rendering the needed HTML code to the container aspx file at the time of its loading and filling up the first combobox.
    2. The user-select item in the filled combo client code (OnChange() from scripts.js) carries out needed data to fill the next combo in HiddenFrm.aspx and submits it.
    3. The server code in HiddenFrm.aspx.cs fills in the next combo.
    4. The client code in HiddenFrmOnLoad() fills up the combo in the container page where comboboxes are set. The user continues to select the program carried out in Step 2.
    Fragments of LocControl.cs
       public class LocControl: System.Web.UI.Control
    {
    ...
    ...

    public string SqlServer // if this attribute value = true then
    // custom will use SQL Data Provider;
    // otherwise, OLE DB Provider
    {
    get{ return bSqlServer;}
    set{ bSqlServer= value;}
    }
    public string ComboData
    // String separated by ";" for each combobox.Between ";" write
    // data needed for fill OPTION elements of SELECT tag
    // separated by space ' '.
    //
    // For First element: will be three parameters
    // First parameter: Table name from assigned in ConString
    // data source
    // Second paramente: Field name of Table that will be text
    // value of combobox
    // Third parameter: Field name of Table that wi be the value
    // for combobox item and will use for seek in following
    // combobox
    // For Follwing comboboxes will be four parameters:
    // First three parameters have same meaning that was for
    // First combobox.
    // Fourth parameter point out the key field in the Table where
    // you select data for this combobox.

    {
    get { return CDates;}
    set {CDates = value;}
    }
    public string ConString // set connection string for
    // the appropriate provider
    {
    get{ return scon;}
    set{ scon = value;}
    }
    public string Captions //comboboxes captions
    {
    get{ return sCaptions;}
    set{ sCaptions = value;}
    }
    protected override void Render(HtmlTextWriter writer)
    // fill custom control HTML code on page
    //
    {
    string htextname = "";
    //check ComboData attribute for empty
    if(CDates == null)
    return;

    ArrayComboDates = CDates.Split(';'); // separate ComboData
    // attribute's data
    int comboquantity = ArrayComboDates.Length;

    // hidden textbox to save ComboData attribute of control
    writer.WriteBeginTag("input");

    ...

    // hidden textbox to save connection string
    writer.WriteBeginTag("input");
    writer.WriteAttribute("type","hidden");
    string hconnection = UniqueID+"_sconn";
    writer.WriteAttribute("name",hconnection);
    if(bSqlServer == "true")
    writer.WriteAttribute("value", scon
    + "??" + "true");
    else
    writer.WriteAttribute("value",scon );
    writer.Write(HtmlTextWriter.TagRightChar);

    // begin <table tag to arrange control's elements
    writer.WriteBeginTag("table");
    ...

    // print captions for each combobox
    writer.WriteFullBeginTag("tr");
    writer.WriteLine();
    if(sCaptions !=null)
    capsarray = sCaptions.Split(';');
    ...

    // print SELECT tags for each combobox
    writer.WriteFullBeginTag("tr");

    string[] ctrlname = new string[comboquantity];
    for(int i=0; i < comboquantity; i++)
    {
    //begin td
    writer.WriteBeginTag("td");
    writer.WriteAttribute("align","center");
    writer.Write(HtmlTextWriter.TagRightChar);
    writer.WriteLine();
    writer.WriteBeginTag("select");

    ctrlname[i] = string.Format("{0}_{1}",this.UniqueID,i+1);

    writer.WriteAttribute("name", ctrlname[i]);
    if(i != (comboquantity - 1))
    writer.Write("OnChange=\"OnChange(this.options
    [this.selectedIndex].value,
    this.name,this.form.name," +
    comboquantity.ToString() + "," + htextname
    + "," + hconnection + ")\"");
    if(ID!=null)
    writer.WriteAttribute("id",this.ClientID);
    writer.Write(HtmlTextWriter.TagRightChar);
    // fill up first combobox
    if(i == 0)
    if(bSqlServer == "true")
    FillClCombo(i,writer); // fill up by SQL
    // provider
    else
    FillClCombo_uni(i,writer); // fill up by OLE DB
    // provider
    writer.Write("</select>");
    writer.WriteEndTag("td");
    writer.WriteLine();

    }
    writer.WriteEndTag("tr");
    writer.Write("</Table>");

    }

    When the user selects an item in the combo, appropriate data are transferred from your .aspx file to hidden frame(HiddenFrm.aspx) by the OnChange() function from the subscript.js file.

    JavaScript Source Code

    void function OnChange(combovalue,name,frmname,comboquantity,htext,
    hconnection)
    {
    // get general base of combobox names using active combobox name
    var baseofname = name.substr(0,name.lastIndexOf("_") + 1);
    var number = name.substr(name.lastIndexOf("_")+1,name.length);
    var intnumber = parseInt(number) + 1;
    var nextcomboname;
    //clean up subsequent comboboxes
    for( i=intnumber; i < comboquantity; i++)
    {
    var n = i+1;
    nextcomboname = baseofname + n
    eval("document." + frmname + "." + nextcomboname +
    ".length = 0;");
    }
    //passing data from active page to Hidden form(HiddenFrm.aspx)
    eval("var cfrm = document." + frmname);
    eval("parent.bottomFrame.document.HiddenFrm.combotext.value=" +
    combovalue);
    parent.bottomFrame.document.HiddenFrm.comboname.value=frmname +
    "." + name;
    parent.bottomFrame.document.HiddenFrm.TextCDatas.value =
    htext.value;
    parent.bottomFrame.document.HiddenFrm.TextConnection.value =
    hconnection.value;
    parent.bottomFrame.document.HiddenFrm.submit();
    }

    Come back to the server side and look at HiddenFrm.aspx.cs. The job continues here.

    Fragments from HiddenFrm.aspx.cs

    namespace HiddenFrm_ns
    {
    public class HiddenFrm : System.Web.UI.Page
    {
    ...
    ...

    private void Page_Load(object sender, System.EventArgs e)
    {
    string apppath =Context.Request.ApplicationPath;
    //register script block
    string scrsrc = "<script src=" + apppath +
    "/KarControl/script.js></script>";
    if(!IsClientScriptBlockRegistered("cSrcript"))
    RegisterClientScriptBlock("cScript", scrsrc);
    Response.Write("<br>");
    // Get filled from active page data and fill up the
    // intermediate combo
    NameValueCollection fnv = Request.Form;
    if(fnv.Count !=0)
    {
    string[] condata = (Request.Form.GetValues(
    "TextConnection")[0]).Split('?');
    con = condata[0];
    string combotext = fnv.GetValues("combotext")[0];
    //key value for filter data from next table in
    //database
    string comboname = (fnv.GetValues("comboname"))[0];
    //name of combo that initiate submit in active page
    //get name of next combo
    char[] d = {'_'};
    int ind = comboname.LastIndexOfAny(d);
    string cnum = comboname.Substring(ind+1,
    comboname.Length -(ind + 1));
    int icombonum =(int)Convert.ChangeType(cnum,
    ind.GetType());
    icombonum = icombonum + 1;
    string ctrlname = comboname.Substring(0,ind+1);
    string nextcombo = ctrlname + icombonum.ToString();
    // if after connection string is ; and "true" word this
    // is SqlServer mode
    if(condata.Length == 3 && condata[1] == "" &&
    condata[2]=="true")
    FillComboSql(combotext,icombonum); // fill combo
    // by SQL
    // Provider
    else
    FillComboUni(combotext,icombonum); // fill combo
    // by OLE DB
    // provider

    this.comboname.Text = nextcombo;
    }

    }
    ...
    ...
    }
    }

    The back stream of data to your active page provides the HiddenFrmOnLoad() function from the subscript.js file.

    JavaScript Source Code from scropt.js

    //send filled up combobox.s data from HiddenFrm.aspx to your
    //active page
    void function HiddenFrmOnLoad()
    {
    var op;
    var text;
    if(document.HiddenFrm.comboname.value != "")
    {
    text = "var mcombo = parent.mainFrame.document." +
    HiddenFrm.comboname.value;
    eval(text);
    mcombo.options[0]= new Option("","");
    for(i=0; i < document.HiddenFrm.hcombo.length; i++)
    {

    op = document.HiddenFrm.hcombo.options[i];
    mcombo.options[i+1] = new Option(op.text,op.value);
    }
    mcombo.length = document.HiddenFrm.hcombo.length + 1;
    }
    }

    Using the Source

    You don't need save the LocControl.cs and HiddenFrm.aspx.cs files in your project; their compiled code is in LocControl.dll. If you want to change this source, apply this command line:

    csc /t:library /out:LocControl.dll LocControl.cs HiddenFrm.aspx.cs.

    The manual for applying for this custom control is in Readme.txt in the KarControl folder.

    About the Author
    I am a programmer for database application and data aware web sites.

    Downloads

  • DynLinkedCombo_demo.zip -
  • DynLinkedCombo_src.zip - contain two Source code cs files needed for develop custom control server sisde code.
  • DynLinkedCombo_tools.zip - contain files that is necessary to implement this custom control in ASP .NET project
  • How to Snap the Cursor to a Button!

    If you're attempting to create that foolproof Windows application, one great technique to use is that of snapping the cursor to a particular control, anticipating the next click.

    The following neat little function does exactly that. Simply pass in a control to get it started: It'll calculate the exact bottom middle location of the control, then snap the cursor to that position. Here's the code:

    Public Sub SnapToControl(ByVal Control As Control)
    ' Snaps the cursor to the bottom middle of the passed control
    Dim objPoint As Point = Control.PointToScreen(New Point(0, 0))
    objPoint.X += (Control.Width / 2)
    objPoint.Y += ((Control.Height / 4) * 3)
    Cursor.Position = objPoint
    End Sub

    And here's how you might use this to snap to, say, a Button control:

    SnapToControl(Button1)


    Figure: A simple little application, this time snapping to a LinkLabel contro

    Using Client-side Script to Focus Controls in ASP.NET

    Many users want Web applications to be as responsive as Windows applications. With Windows, you get some really nice default behaviors and with the Web you get some really nice deployment features. Generally, the ease of deployment—update a Web page and every user gets the update—doesn't come cheaply with Windows applications, and default, user-friendly features such as "focusing" control behavior—call SetFocus in Windows—doesn't come cheaply with Web applications. Usability is a nice benefit of developing for Windows Forms; deployment is a nice feature of Web Forms.

    In this article, I will demonstrate how you can use injected JavaScript to set the focus control in ASP.NET. You will learn how to write a bit of JavaScript and use VB.NET to determine which control should have the focus and how to inject dynamic JavaScript to affect this client-side behavior.

    Writing the JavaScript to Focus a Control

     

    A user performs a task that causes your Web application to post back to the server. When the Web Form is recreated on the server, the user has to manually reposition the focus to their last point of interest. On a simple page with a few controls, this is easy enough; however, if you are building a complex Web application, repositioning the focus may become a nuisance issue.

    Recently, I was responsible for building some very complex Web Forms that had collapsible regions and various edit points of interest. Manually re-expanding and positioning focus made the application seem clumsy. To resolve the problem, we elected to dynamically figure out which region and control had the user's interest and re-focus that control. Refocusing the same control requires about two lines of JavaScript (see Listing 1); dynamically re-focusing a specific control required that dynamic JavaScript be emitted to the rendered client page. Here is some pseudo-code to focus the same control. In the next section, I will show you how to convert the static JavaScript into a dynamic script block.

    Listing 1: Pseudo-code to focus the same control on the client.

    <script language="javascript">
    var control = document.getElementById(<control name>);
    if( control != null ){ control.focus(); }
    </script>

    In Listing 1, we obtain the control by passing the control's name to the document.getElementById. If a valid control object is returned, we call the control's focus method, as demonstrated in the listing. Working forward, we need to figure out the control and dynamically inject a similar block of JavaScript to the rendered client page in such a manner that the script block will be run when the page is loaded in the browser.

    Registering the Startup Script

    ASP.NET pages contain a method named RegisterStartupScript. By invoking Page.RegisterStartupScript, we can inject a block of script that will run just before the page is rendered in the browser. The arguments we need to pass to RegisterStartupScript are a unique name for the script block and a properly formatted block of script.

    To solve this problem I defined a utility routine named SetFocusControl that accepts the string name of the control to focus. SetFocusControl inserts the control to focus in the formatted script string and Page.RegisterStartupScript inserts the formatted script into the page. Listing 2 contains the implementation of SetFocusControl and Listing 3 shows the HTML of a very simple Web page illustrating the placement of the injected code block. All that remains for you to do is to call SetFocusControl in your Web Page's code-behind prior to posting the page back. Suitable places for calling SetFocusControl include postback points, such as button clicks.

    Listing 2: The implementation of SetFocusControl.

    Public Sub SetFocusControl(ByVal ControlName As String)
    ' character 34 = "

    Dim script As String = _
    "<script language=" + Chr(34) + "javascript" + Chr(34) _
    + ">" + _
    " var control = document.getElementById(" + Chr(34) + _
    ControlName + Chr(34) + ");" + _
    " if( control != null ){control.focus();}" + _
    "</script>"

    Page.RegisterStartupScript("Focus", script)
    End Sub

    Listing 2 uses the Chr(34) function to insert the double-quote into the preformatted script block to embed double-quotes into the script block. Calling SetFocusControl(control.ClientID)—for example, SetFocusControl(TextBox2.ClientID)—results in the script block shown in Listing 3 to be added to the rendered HTML.

    Listing 3: The dynamically rendered startup script block.

    <script language="javascript">
    var control = document.getElementById("TextBox2");
    if( control != null ){control.focus();}
    </script>

    Listing 3 shows the dynamically rendered script. As the page is read by the browser, the preceding block of script will run and the control passed to getElementById will have the focus.

    Summary

    Customers usually want specific usability features and seldom care about the details. Often, things that seem easy to do are harder than a customer would expect. By sharing techniques, we can provide features without a lot of re-innovation.

    In this article, I demonstrated how to set a control's focus dynamically by using a preformatted script block and the Page.RegisterStartupScript method. Client-side JavaScript will often yield some of the snappy usability customers have come to expect; one simply needs to know what services are available for creating this script.

    As an aside, one would expect setting the focus to exist in ASP.NET controls, but if you check the help documentation for Web controls, you will find this capability does not exist. Perhaps we will get it in future versions of ASP.NET.

    ASP.NET Custom Controls -- Client Script Generation

    The new state management and postback features of ASP.NET are indeed very exciting. They provide developers with a whole new range of mechanisms for producing dynamic Web pages. The ability to write your own custom controls takes that ability to a whole new level, allowing you to write a control with custom functionality that can easily be reused in multiple pages by simply defining custom tags, similar to any other HTML element. The person implementing the layout for a page no longer needs to know all the details of how to write client-side code to get the dynamic behavior that has become so popular. However, there are some pitfalls that developers need to be aware of. ASP.NET promotes server-heavy designs. Network traffic can be dramatically increased as each client-side event can potentially cause a round trip to the server. Many of the effects that result from these frequent trips to the server can easily be accomlished with a few simple JavaScript functions. Calls to the server should be kept to a minimum, with as much being done on the client as possible. By using custom controls to generate client-side script, we can take advantage of Dynamic HTML on the client while still providing a measure of separation between the layout and the logic.

    Client-Side Script Generation

     

    One of the goals of generating script from a custom control is to allow a developer to create the control and specify its behavior, and then publish it for others to use without having to know how the code works. We want to encapsulate the implementation of the control and tightly couple the HTML rendering to the script that works with it to reduce the possible points of failure associated with more traditional methods of Web component reuse (such as Cut & Paste, and include files). The most straightforward approach to script generation is to write the script along with the control in the Render method of your control (see Listing 1).

    namespace Spotu
    {
    public class HelloWorld : Control
    {
    protected override void Render (
    HtmlTextWriter writer
    )
    {
    writer.Write(@"
    <script>
    function HelloWorld()
    {
    document.all('_msg').innerText = 'Hello World';
    }
    </script>");

    writer.Write("<button onclick='javascript:HelloWorld()'>"
    + "Click Me</button>");

    writer.Write("<div id=_msg></div>");
    }
    }
    }

    Listing 1: Example of script generation using Render

    Listing 2 shows an example of a page using the HelloWorld class with client script generation.

    <%@ Page language="c#" %>
    <%@ Register Namespace='Spotu'
    TagPrefix='spotu'
    Assembly ='helloworld' %>

    <html>
    <body>
    <form runat='server'>
    <spotu:HelloWorld runat='server'/>
    </form>
    </body>
    </html>

    Listing 2: Page using the HelloWorld class.

    This approach works, and does solve the initial problem of allowing a developer to write a custom control that someone else can use in their page to provide dynamic capabilities without having to post back to the server. However, it is not very elegant, and it does have some shortcomings. Most notably, we cannot include this control in a page multiple times; doing so would cause mutiple divisions to be created with the same ID. Even if we do uniquely name the <div> elements in this example, it is still inneficient because the JavaScript gets written out with every reference to this control. This can produce a lot of overhead, transmitting the same script down to the client for each instance of the control.

    We need some way to have a control generate script, but generate it only once, even if multiple instances of the control are used on the same page. Fortunately for us, the developers at Microsoft thought of this and provided a way to register a script block to ensure we only write out a section of script once by using the Page.RegisterClientScriptBlock method. This method takes two parameters, an ID that identifies the script block so the Page class will know to ignore any other requests to register the same clock of code, and a string containing the script to be registered. The best place to register the script block is in the Init Event Handler for the control. To take advantage of this event, override the OnInit method of the Control class. With this in mind, the HelloWorld example could be rewritten as shown in Listing 3.

    using System;
    using System.Web;
    using System.Web.UI;

    namespace Spotu
    {
    public class HelloWorld : Control
    {
    protected override void OnInit(EventArgs e)
    {
    string strCode = @"
    <script>
    function HelloWorld(id)
    {
    document.all(id).innerText = 'Hello World';
    }
    </script>";

    Page.RegisterClientScriptBlock("Spotu_HelloWorld",
    strCode);
    }

    protected override void Render(HtmlTextWriter writer)
    {
    writer.Write("<button onclick='javascript:HelloWorld(\""
    + this.UniqueID + "\")'>"
    + "Click Me</button>");

    writer.Write("<div id='" + this.UniqueID
    + "'></div>");
    }
    }
    }

    Listing 3: HelloWorld rewritten to use the register script block.

    This approach is much better but there is still a problem. If the script that is being register is lengthy, or if there are a lot of calculations, data accesses, and so forth in generating our script, we will still take a performance hit when the page loads because the control creates this huge block of script that ends up being tossed out because it is already registered. Once a block of script is registered, we can test for it using the Page.IsClientScriptBlockRegistered method. To improve the performance of the HelloWorld control, we would include the call in out Init handler as shown in Listing 4.

      protected override void OnInit(EventArgs e)
    {
    if (!Page.IsClientScriptBlockRegistered("Spotu_HelloWorld"))
    {
    string strCode = @"
    <script>
    function HelloWorld(id)
    {
    document.all(id).innerText = 'Hello World';
    }
    </script>";

    Page.RegisterClientScriptBlock("Spotu_HelloWorld",
    strCode);
    }
    }

    Figure 4: OnInit using the IsClientScriptBlockRegistered test

    Using client script generation from custom controls provides a clean encapsulated method of enabling dynamic behavior in Web pages while still shielding the page designer from having to know the details of how to produce the desired effect. Developers are now free to concentrate on how to get a control to do what you want it to do without being bogged down with were to put it on the page, or being pestered by the marketing guy to move a control around, add a new one, or take one away. By combining this approach with designer integration controls, dynamic behaviors can easily be customized and reused across multiple pages with little or no developer interaction and without the pitfalls of server-side includes or cut & paste code reuse.

    Caching

    Some of you might be inclined to ask: How do I cache this script so it doesn't get downloaded every time? After all, client-side script tends to be fairly static, not needing to be downloaded every time a Web page is loaded.

    There are a couple of options for caching the output of your control. The ASP.NET approach would be to take advantage of output caching. There are a myriad of output caching options, but most of them place the responsibility of setting up that caching on the person doing the presentation by using directives and flags in the .aspx page. Also, caching the entire page may not be the desired effect. Some pages are extremely dynamic. In such cases, the ideal would be to just cache the control, or some portion of the control. ASP.NET does have some support for this, but that support is reserved primarily for user controls (.ascx files), which doesn't provide the reuse we are looking for.

    For custom controls providing generated script, we may want to consider using an external script file. As we have already noted, most script does not change often, if at all and can readily be cached on the client. Instead of writing out the script directly from our custom control, we can instead place the script in an external script file and simply write out a <script..> tag with a src="." attribute that references our script file. This allows the control and the page to fluctuate as often as necessary without incurring the network traffic of always downloading the script to the client. The primary drawback to the approach is the deployment. There are now two files that need to be deployed to use the control in a page and the .js file must be reachable from the page that is using it. Relative paths don't always work because each page that uses the control may be at a different level. One deployment solution is to create a directory at the top level of your application (example: includes) and reference it in your control using Request.ApplicationPath + "/includes/<your script file here>". Another approach might be to provide custom properties on you control so the location of the external source file can be specified in the .aspx page. Listing 5 is an example of a calculator implemented using this approach.

    using System;
    using System.Web;
    using System.Web.UI;
    using System.Collections.Specialized;

    namespace Spotu
    {
    public class Calculator : Control, IPostBackDataHandler
    {
    const string sc_strStyleClass = "calcButton";

    private string _strNumButton;
    private string _strOpButton;
    private string _strScriptSrc;
    private string _strStyleHref;
    private string _strSavedValue;
    private int _intCalcValue = 0;


    // Custom property for explicitly setting the location
    // of the script file
    public string ScriptSrc
    {
    get { return _strScriptSrc; }
    set { _strScriptSrc = value; }
    } // End ScriptSrc

    // Custom property for explicitly setting the location
    // of the stylesheet file
    public string StyleSrc
    {
    get { return _strScriptSrc; }
    set { _strScriptSrc = value; }
    } // End StyleSrc

    #region IPostBackDataHandler

    // LoadPostData gets call when the 'save' button
    // rendered by this control is clicked
    public virtual bool LoadPostData (
    string postDataKey,
    NameValueCollection values
    )
    {
    _strSavedValue = "Saved Value: "
    + values[UniqueID + "_display"];
    return false;
    } // end LoadPostData

    // Needed to implement IPostBackDataHandler
    public virtual void RaisePostDataChangedEvent()
    {
    } // End RaisePostDataChangedEvent

    #endregion

    // Loads the state of the control from the
    // viewstate managed by .NET
    protected override void LoadViewState (
    object savedState
    )
    {
    _strSavedValue = savedState as string;
    } // End LoadViewState

    // Saves the state of the control
    protected override object SaveViewState()
    {
    return _strSavedValue;
    } // End SaveViewState

    // Init event handler, called to initialize any state
    // in the object before the viewstate is restored.
    protected override void OnInit (
    EventArgs e
    )
    {
    _strNumButton = string.Format("<button "
    + "onclick='javascript:g_{0}.EnterNumber(this.innerText);'"
    + " class='{1}'>", this.UniqueID, sc_strStyleClass);

    _strOpButton = string.Format("<button "
    + "onclick='javascript:g_{0}.OnOperator(this.innerText);' "
    + "class='{1}'>", this.UniqueID, sc_strStyleClass);

    if (_strScriptSrc == null)
    {
    _strScriptSrc = Context.Request.ApplicationPath
    + "/includes/calc.js";
    }

    if (_strStyleHref == null)
    {
    _strStyleHref = Context.Request.ApplicationPath
    + "/includes/calcStyle.css";
    }

    string strScriptBlock = "<script src='"
    + _strScriptSrc
    + "'></script>";

    Page.RegisterClientScriptBlock("Spotu_Calculator",
    strScriptBlock);

    string strStyle = "<link rel='stylesheet' "
    + "type='text/css' href='"
    + _strStyleHref
    + "'></link>";

    Page.RegisterClientScriptBlock("Spotu_Calculator_Style",
    strStyle);
    } // End OnInit

    // Load Event Handler. Retrieve the value posted in the
    // display field of the calculator so we can keep the
    // state of the display regardless of how the form is
    // submitted.
    protected override void OnLoad (
    EventArgs e
    )
    {
    if (Page.IsPostBack)
    {
    _intCalcValue =
    Int32.Parse(Context.Request.Form[UniqueID
    + "_display"]);
    }
    } // End OnLoad

    // Render out the control
    protected override void Render (
    HtmlTextWriter writer
    )
    {
    string strHtml = string.Format(@"
    <script> var g_{0} = new Calc('{0}_display'); </script>
    <table>
    <tr colspan='*'>
    <input type='text'
    name='{0}_display'
    readonly=true
    value={4}>
    </input>
    </tr>
    <tr><td>{1}7</button></td>
    <td>{1}8</button></td>
    <td>{1}9</button></td>
    <td>{2}/</button></td>
    <td>
    <button
    class='{3}'
    onclick='javascript:g_{0}.OnClear();'>
    C
    </button>
    </td>
    </tr>
    <tr><td>{1}4</button></td>
    <td>{1}5</button></td>
    <td>{1}6</button></td>
    <td>{2}*</button></td>
    </tr>
    <tr><td>{1}1</button></td>
    <td>{1}2</button></td>
    <td>{1}3</button></td>
    <td>{2}-</button></td>
    </tr>
    <tr><td>{1}0</button></td>
    <td></td>
    <td>{1}.</button></td>
    <td>{2}+</button></td>
    <td>
    <button
    class='{3}'
    onclick='javascript:g_{0}.OnEqual();'>
    =
    </button>
    </td>
    </tr>
    </table>", UniqueID,
    _strNumButton,
    _strOpButton,
    sc_strStyleClass,
    _intCalcValue);

    writer.Write(strHtml);

    writer.Write("<INPUT type='submit' name='"
    + this.UniqueID + "' value='Save'></INPUT>");

    writer.Write("<H3 id='" + UniqueID + "_savedVal'>"
    + _strSavedValue + "</H3>");
    } // End Render
    }
    }

    Listing 5: calculator.cs

    <%@ Page %>
    <%@ Register Namespace='Spotu'
    TagPrefix='spotu'
    Assembly ='calc' %>

    <html>
    <body>
    <form runat='server'>
    <spotu:Calculator runat='server'/>
    <hr>
    <spotu:Calculator runat='server'/>
    </form>
    </body>
    </html>

    Listing 6: calculator.aspx

    function Calc(dispId)
    {
    this.intCurrentVal = 0;
    this.intLastNum = 0;
    this._op = "";
    this.bEqual = false;
    this.displayId = dispId;

    this.EnterNumber = function(num)
    {
    if (this.bEqual)
    this.OnClear()

    if (this.intLastNum != 0)
    this.intLastNum += num;
    else
    this.intLastNum = num;

    document.all(this.displayId).value = this.intLastNum;
    }

    this.ComputeValue = function()
    {
    switch (this._op)
    {
    case '+':
    this.intCurrentVal = Number(this.intCurrentVal)
    + Number(this.intLastNum);
    break;
    case '-':
    this.intCurrentVal -= this.intLastNum;
    break;
    case '*':
    this.intCurrentVal *= this.intLastNum;
    break;
    case '/':
    this.intCurrentVal /= this.intLastNum;
    break;
    default:
    this.intCurrentVal = this.intLastNum;
    }
    document.all(this.displayId).value = this.intCurrentVal;
    }

    this.OnOperator = function(op)
    {
    if (!this.bEqual)
    this.ComputeValue();

    this.bEqual = false;
    this.intLastNum = 0;
    this._op = op;
    }

    this.OnEqual = function()
    {
    this.ComputeValue();
    this.bEqual = true;
    }

    this.OnClear = function()
    {
    this._op = "";
    this.intCurrentVal = 0;
    this.intLastNum = 0;
    this.bEqual = false;
    document.all(this.displayId).value = this.intCurrentVal;
    }
    }

    Listing 7: JavaScript source file for calculator

    .calcButton
    {
    width=25;
    }

    Figure 8: Stylesheet for calculator buttons

    Examining the Code

    One item to note is that the reference to the stylesheet that defines the style for the calculator buttons is located in the OnInit method along with the script block registration. Registering blocks of client-side code is not limited to "script" alone. The stylesheet here is external, allowing the designer the ability to modify the look and feel of the buttons by modifying the .css file. Another approach to allowing the page designer to change the look and feel of the calculator would be to implement custom properties, or better yet, custom properties with sub-properties to group them together (example: Font-Style, Font-Size, and so forth). This approach seems somewhat limiting in that the designer then can only change the properties you have exposed. With stylesheets, the designer has all the options available to him/her that would be there if a standard HTML element was being used, options that would otherwise be unavailable because he/she does not have direct access to the HTML elements your custom control produces and would not be able to apply a class or style to them.

    There is one block of script that is written out when the control is rendered instead of beign included in the .js file. This allows multiple instances of the calculator control to be used in the same page. The UniqueID property inherited from the Control class is used to differentiate the controls from each other. The UniqueID property is a unique identifier that identifies an instance of a control within a page.

    The locations of the stylesheet and the external script file default to an /includes directory located at the virtual application root. However, there are two custom properties provided that allow the designer to override where those file are located.

    By using the UniqueID for the control as the name for the submit button, we make sure that the LoadPostData method for our control only gets called when the 'Save' button for that control is clicked. If we had named the text box with the UniqueID for the control, we would end up saving the calculated number for all the controls on the page, regardless of how the submit to the server was done. This example is a little contrived and if you are really serious about reducing server load, you could alter the 'Save' button so that, instead of posting the form back to the server, it does a Web Services call.

    Conclusion

    Using custom controls to generate client-side script can have tremendous benefits. The custom control will look and behave similarly to any other control written with ASP.NET, making it easy to reuse and shielding the page designer from needing to know the details of how the code works. By using client-side scripting to create the dynamic behaviors, you can greatly increase the responsiveness of the individual pages and the overall performance of your web site by significantly decreasing the number of calls that are made to the server. Using external files for your script has both positives and negatives. The pros include taking advantage of browser caching and easy access for customizability. The cons include a more complex deployment both in the production environment as well as the design time environment.

    Downloads
    Download demo project - 6 Kb
    Download source - 3 Kb

    Performing Validations with ASP.NET

    RegularExpressionValidator

    This validation control is used to parse strings using special characters. This is similar to the concept of Pattern Matching in PERL. This control can be used in situations such as:

    • To check for "@" character on an e-mail address (See Listing 1)
    • To verify whether the user entered valid Zip code, Telephone number and web address

     

    Listing - 1

    [Visual Basic .NET]

    <%@ Page Language="VB" %>
    <html>
    <head>
    <title>Validation Controls in Action</title>
    </head>
    <body>
    <form runat="server">
    <p>
    Enter your e-mail address:  
    </p>
    <p>
    <asp:TextBox id="TextBox1" runat="server">
    </asp:TextBox>

    <asp:Button id="Button1" runat="server"
    Text="Click here">
    </asp:Button>
    </p>
    <p>
    <asp:RegularExpressionValidator
    id="RegularExpressionValidator1"
    runat="server"
    ValidationExpression="\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"
    ErrorMessage="Please enter a valid E-mail ID"
    ControlToValidate="TextBox1">
    </asp:RegularExpressionValidator>
    </p>
    </form>
    </body>
    </html>

    [C#]

    <%@ Page Language="C#" %>
    <html>
    <head>
    <title>Validation Controls in Action
    </head>
    <body>
    <form runat="server">
    <p>
    Enter your e-mail address:  
    </p>
    <p>
    <asp:TextBox id="TextBox1" runat="server">
    </asp:TextBox>

    <asp:Button id="Button1" runat="server"
    Text="Click here">
    </asp:Button>
    </p>
    <p>
    <asp:RegularExpressionValidator
    id="RegularExpressionValidator1"
    runat="server"
    ValidationExpression="\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"
    ErrorMessage="Please enter a valid E-mail ID"
    ControlToValidate="TextBox1">
    </asp:RegularExpressionValidator>
    </p>
    </form>
    </body>
    </html>

    It is the ValidationExpression property shown in the above listings that makes the application work. Hit the click here button without entering the @ symbol and observe the output (see Figure 1). The interesting point to note is that even if you enter @ symbol, it would still show errors, till you fill out the full domain part (like yahoo.com) of the email address. It is these powerful capabilities that made this control the most popular among .NET developers and communities.

    Figure 1


    If you want to validate an Internet URL, simply update the ValidationExpression property in the above code with the listing as shown below:

    Listing 2

    http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

    Moreover, you can also write custom validation statements depending upon your project requirements.

    TIP: You can easily generate the code for the ValidationExpression property with the help of Visual Studio .NET or ASP.NET WebMatrix. Simply place the control from the Toolbox and press F4 to get the properties window. From the window, move down until you see the ValidationExpression property and click the small ellipsis button on the extreme right to open the Regular Expression Editor window. From this window, you can select your choice to get predefined expression statements for a number of items.

    RequiredFieldValidator

    As the name denotes, this control is used in cases where a user should compulsorily enter a value, preferably in a textbox. Prior to .NET, this kind of functionality was achieved through complex JavaScript and VBScript functions. Enter the code given in the listing given below, execute and observe the result:

    Listing - 3

    [Visual Basic .NET]

    <%@ Page Language="VB" %>
    <html>
    <head>
    </head>
    <body>
    <form runat="server">
    <p>
    Your Name:
    </p>
    <p>
    <asp:TextBox id="TextBox1" runat="server">
    </asp:TextBox>

    <asp:Button id="Button1" runat="server" Text="Click here">
    </asp:Button>
    </p>
    <p>
    <asp:RequiredFieldValidator
    id="RequiredFieldValidator1"
    runat="server"
    ErrorMessage="The above field should not be left blank"
    ControlToValidate="TextBox1"
    display="static">
    </asp:RequiredFieldValidator>
    </p>
    </form>
    </body>
    </html>

    [C#]

    <%@ Page Language="C#" %>
    <html>
    <head>
    </head>
    <body>
    <form runat="server">
    <p>
    Your Name:
    </p>
    <p>
    <asp:TextBox id="TextBox1" runat="server">
    </asp:TextBox>

    <asp:Button id="Button1" runat="server" Text="Click here">
    </asp:Button>
    </p>
    <p>
    <asp:RequiredFieldValidator
    id="RequiredFieldValidator1"
    runat="server"
    ErrorMessage="The above field should not be left blank"
    ControlToValidate="TextBox1"
    display="static">
    </asp:RequiredFieldValidator>
    </p>
    </form>
    </body>
    </html>

    Validation Summary

    With the help of this control, you can display the error messages on a page. It collects all the error messages from the enabled controls and shows them in various ways like list, bulleted list, and single paragraph (See Figure 2). It also displays the errors in a message box. The code shown in listing 4 examines the application of this control:

    Listing - 4

    [Visual Basic .NET]

    <%@ Page Language="vb" %>
    <html>
    <head>
    </head>
    <body>
    <form runat="server">
    <p>
    Enter a first value:

    <asp:TextBox id="txtValue1" runat="server">
    </asp:TextBox>

    <asp:RequiredFieldValidator id="Reqfield1"
    runat="server"
    ErrorMessage="The above field should not be left blank"
    ControlToValidate="txtValue1">
    </asp:RequiredFieldValidator>
    </p>
    <p>
    Enter a second value:

    <asp:TextBox id="txtValue2" runat="server">
    </asp:TextBox>

    <asp:RequiredFieldValidator id="reqField2"
    runat="server"
    ErrorMessage="The above field should not be left blank"
    ControlToValidate="txtValue2">
    </asp:RequiredFieldValidator>
    </p>
    <p>
    <asp:Button id="btnClick" runat="server" Text="Click Here">
    </asp:Button>
    </p>
    <p>
    <asp:ValidationSummary id="Validsummary"
    runat="server" HeaderText="Errors are as follows:"
    ShowMessageBox="True">
    </asp:ValidationSummary>
    </p>
    </form>
    </body>
    </html>

    As mentioned above, the above listing also displays a message box upon execution since the ShowMessageBox property is set to true.



    Figure 2 - Click here for larger image

    [C#]

    <%@ Page Language="C#" %>
    <html>
    <head>
    </head>
    <body>
    <form runat="server">
    <p>
    Enter a first value:

    <asp:TextBox id="txtValue1" runat="server">
    </asp:TextBox>

    <asp:RequiredFieldValidator id="Reqfield1"
    runat="server"
    ErrorMessage="The above field should not be left blank"
    ControlToValidate="txtValue1">
    </asp:RequiredFieldValidator>
    </p>
    <p>
    Enter a second value:

    <asp:TextBox id="txtValue2" runat="server">
    </asp:TextBox>

    <asp:RequiredFieldValidator id="reqField2"
    runat="server"
    ErrorMessage="The above field should not be left blank"
    ControlToValidate="txtValue2">
    </asp:RequiredFieldValidator>
    </p>
    <p>
    <asp:Button id="btnClick" runat="server" Text="Click Here">
    </asp:Button>
    </p>
    <p>
    <asp:ValidationSummary id="Validsummary"
    runat="server" HeaderText="Errors are as follows:"
    ShowMessageBox="True">
    </asp:ValidationSummary>
    </p>
    </form>
    </body>
    </html>

    From the above discussion, it is clear that all the required stuffs for validating Graphical User Interface (GUI) controls is now available under a single umbrella of Microsoft .NET. Once you learned the usage of all the controls, it is very easy to implement them, be it a WinForm or an ASP.NET WebForm.

    A Google Maps .NET Control

    This article examines a custom ASP.NET server control I developed to make using the Google Maps API easier for .NET developers. This article assumes you are somewhat familiar with Google Maps (or are at least somewhat curious). If you're not familiar with Google Maps or its sweet API, check out http://maps.google.com/ and http://www.google.com/apis/maps/documentation/ for the lowdown. You may see references throughout the article to "my control, my GoogleMaps control, my GMap control, and so forth." I did not create the Google Maps API; I merely used C#, XML, and XSL to create a wrapper for ASP.NET.

    As I stated above, my main goal for this control was to make it easy to use a Google Map using .NET languages. I also tried very hard not to use any fancy or proprietary "hacks" or workarounds when creating this control.

    Setup & Configuration

    I need to point out a few quick things before you get to the examples. The Google Maps Documentaion makes some specific recommendations for the DOCTYPE, HTML Namespaces, and STYLES for your Google Map pages. Each of the example pages starts like so:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "//www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="//www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml"
    xmlns:v="urn:schemas-microsoft-com:vml">

    <head>
    <meta http-equiv="content-type"
    content="text/html;
    charset=UTF-8"/>

    <title>. . .</title>
    <style type="text/css">
    v\:*
    {
    behavior:url(#default#VML);
    }
    </style>
    . . .

    </head>

    Note the "strict" DOCTYPE, the custom xmlns attributes in the HTML tag, and the cryptic-looking stylesheet entry for v\:*. If you don't include these values in your pages, strange things may happen. I noticed them especially when using polylines in Internet Explorer.

    Google API Key

    You'll need a valid Google API key (get one here) to run the examples. The key included in the downloadable code (see the web.config file) will work as long as your application is located at http://localhost/TestWeb/.

    Web.Config Setup

    The GMap control is based on the "div" tag. Unfortunately, ASP.NET renders div tag controls as tables in non-IE browsers by default. For the control to render properly in Firefox, you need to add the following entries to your web.config or machine.config file:

    <browserCaps>
    <case match="^Mozilla/5\.0\s*([^)]*\))\s*(Gecko\/\d+)\s*Firefox\/
    (?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*)).*">


    type=Firefox/${version}
    version=${version}
    majorversion=${major}
    minorversion=${minor}
    frames=true
    tables=true
    cookies=true
    javascript=true
    javaapplets=true

    ecmascriptversion=1.5
    w3cdomversion=1.0
    css1=true
    css2=true
    xml=true
    tagwriter=System.Web.UI.HtmlTextWriter
    <filter match="^b" with="${letters}">
    beta=true

    </filter>
    </case>
    </browserCaps>

    It's a good idea in general to add these values to the machine.config file on your Web server. Why? So things like asp:panel and other "div"-based controls will render properly in Firefox.

    The Good Stuff

    Included in the downloadable code (see the Downloads at the bottom of this article) is a Web project called TestWeb with nine (9) example pages on how to use my GMap Control in your applications (see all examples here). The download includes the complete source for the control.

    Basics.aspx

    __Show Me__ This is the simplest use of a GMap. Here, you just create a GMap with a width of 500px and height of 300px. What could be simpler?

    <form id="Form1" method="post" runat="server">
    <wcp:GMap runat="server" id="gMap" Width="500px" Height="300px" />
    </form>

    Basics.aspx.cs

    To initialize the map, you'll always want to make a call to CenterAndZoom. Otherwise, you may get just a big grey box.

    protected GMap gMap;

    private void Page_Load(object sender, System.EventArgs e)
    {
    gMap.CenterAndZoom(new GPoint(-122.141944F, 37.441944F), 4);
    }

    Houston, we have a GMap!

    Controls.aspx.cs

    __Show Me__ The .aspx code for the Controls example is identical to the Basics example. Here, you add some basic controls to your map.

    protected GMap gMap;

    private void Page_Load(object sender, System.EventArgs e)
    {
    gMap.AddControl( new GSmallMapControl());
    gMap.AddControl( new GMapTypeControl());
    gMap.CenterAndZoom(new GPoint(-122.141944F, 37.441944F), 4);
    }

    GMap & GMarker Events

    Say you want to tap into some of the events provided by the Google Maps API. You can access any of the GMap events by implementing the proper JavaScript functions in your page. The events are outlined below and demonstrated in the following examples:

    Event Handler
    Notes

    GMap_Click(overlay, point)
    "this" will reference the GMap being clicked

    GMap_Move()
    "this" will reference the GMap being moved


    GMap_MoveStart()
    "this" will reference the GMap being moved


    GMap_MoveEnd()
    "this" will reference the GMap that was moved

    GMap_Zoom(oldZoomLevel, newZoomLevel)
    "this" will reference the GMap being zoomed

    GMap_WindowOpen()

    GMap_WindowClose()

    GMap_AddOverlay(overlay)
    "this" will reference the GMap to which the overlay was added

    GMap_RemoveOverlay(overlay)
    "this" will reference the GMap that lost the overlay

    GMap_ClearOverlays()
    "this" will reference the GMap being cleared

    GMarker_Click()
    "this" will reference the GMarker that was clicked

    GMarker_InfoWindowOpen()

    GMarker_InfoWindowClose()

    Downloads

  • WCPierce.zip - Complete source files
  • WCPierceWebChm.zip - Compiled HTML Help file for control

  •  


    Listeners.aspx

    __Show Me__

       <script type="text/javascript">

    function GMap_MoveEnd()
    {
    var center = this.getCenterLatLng();
    var latLngStr = '(' + center.y + ', ' + center.x + ')';
    document.getElementById('message').innerHTML = latLngStr;
    }
    </script>
    </head>

    <body>
    <form id="Form1" method="post" runat="server">
    <wcp:GMap runat="server"
    id="gMap"
    Width="500px"
    Height="300px" />
    <asp:Label Runat="server" ID="message" />
    </form>
    </body>

     


    Here, you implement one of the GMap JavaScript event functions, GMap_MoveEnd. This function will run any time the user stops moving the GMap with their mouse. The JavaScript code gets the current Center coordinate of the map (this), formats the values, and displays them in the "message" label.

    InfoWindow.aspx.cs

    __Show Me__ This example opens the GMap Info Window and displays "Hello World". Note that this example uses JavaScript to create a new DOM text node. GMap also supports OpenInfoWindowHtml, which accepts a string of HTML to display in the info window.

    protected GMap gMap;

    private void Page_Load(object sender, System.EventArgs e)
    {
    GPoint gp = new GPoint(-122.141944F, 37.441944F);
    gMap.CenterAndZoom(gp, 4);
    gMap.OpenInfoWindow(gp, "document.createTextNode('Hello World')");
    }

    Overlays.aspx.cs

    __Show Me__ What fun is a map without points of interest or line annotations? You can add "overlays" to your GMap programmatically. Currently, the only two overlays provided by the Google Maps API are the GMarker and GPolyline. This example shows the usage of both.

    protected GMap gMap;

    private void Page_Load(object sender, System.EventArgs e)
    {
    gMap.AddControl(new GSmallMapControl());
    gMap.AddControl(new GMapTypeControl());
    GPoint gp = new GPoint(-122.141944F, 37.441944F);
    gMap.CenterAndZoom(gp, 5);

    GMarker gm = new GMarker(gp, "FirstMarker");
    gMap.Overlays.Add(gm);

    GPolyline gPoly = new GPolyline();
    gPoly.Weight = 5;
    gPoly.Opacity = 0.25F;
    gPoly.Color = Color.Red;
    for( int i=1; i<6; i++ )
    {
    float x = gp.X + (0.005F*-i);
    float y = gp.Y + (0.005F*-i);

    gPoly.Points.Add(new GPoint(x, y));
    }
    gMap.Overlays.Add(gPoly);
    }

    ClickHandling.aspx

    __Show Me__ You also can capture the GMap click event and take any desired actions based on the click, such as adding or removing an overlay:

       <script type="text/javascript">
    function GMap_Click(overlay, point)
    {
    if( overlay )
    this.removeOverlay(overlay);
    else if( point )
    this.addOverlay(new GMarker(point));

    }
    </script>
    </head>
    <body>
    <form id="Form1" method="post" runat="server">
    <wcp:GMap runat="server"
    id="gMap"
    Width="500px"
    Height="300px" />
    </form>

    </body>

    MarkerInfoWindow.aspx

    __Show Me__ You also can capture the GMarker click event and show an info window over the marker with any HTML you want:

       <script type="text/javascript">
    function GMarker_Click()
    {
    var html = "<b>" + this.id + "</b>";

    this.openInfoWindowHtml(html);
    }
    </script>
    </head>
    <body>
    <form id="Form1" method="post" runat="server">
    <wcp:GMap runat="server"
    id="gMap"
    Width="500px"
    Height="300px" />

    </form>
    </body>

    MarkerInfoWindow.aspx.cs

    Create the GMarkers programmatically with any .NET language. Set each GMarker's ID so it will be displayed client-side when clicked.

    protected GMap gMap;

    private void Page_Load(object sender, System.EventArgs e)
    {
    GPoint gp = new GPoint(-122.141944F, 37.441944F);

    GMarker gm = new GMarker(gp, "FirstMarker");
    gMap.Overlays.Add(gm);

    gm = new GMarker(new GPoint(gp.X + 0.005F, gp.Y + 0.005F),
    "SecondMarker");
    gMap.Overlays.Add(gm);

    gMap.CenterAndZoom(gp, 4);
    }

    Icons.aspx

    __Show Me__ The GMap control also supports custom icons. Creating icons requires a little more work unless you have access to the proper images. I'll borrow some of Google's for demonstration purposes.

       <script type="text/javascript">

    function GMarker_Click()
    {
    var html = "You clicked me!";
    this.openInfoWindowHtml(html);
    }
    </script>
    </head>
    <body>

    <form id="Form1" method="post" runat="server">
    <wcp:GMap runat="server"
    id="gMap"
    Width="500px"
    Height="300px" />
    </form>
    </body>

    Icons.aspx.cs

    Again, the theme here is programmatically creating Google API objects and interacting with them using C#/VB .NET.

    protected GMap gMap;

    private void Page_Load(object sender, System.EventArgs e)
    {
    GPoint gp = new GPoint(-122.141944F, 37.441944F);

    GIcon tiny = new GIcon();
    tiny.Id = "tiny";
    tiny.Image = new Uri("http://labs.google.com/ridefinder/images/
    mm_20_red.png");
    tiny.Shadow = new Uri("http://labs.google.com/ridefinder/images/
    mm_20_shadow.png");
    tiny.IconSize = new GSize(12, 20);
    tiny.ShadowSize = new GSize(22, 20);
    tiny.IconAnchor = new GPoint(6, 20);
    tiny.InfoWindowAnchor = new GPoint(5, 1);
    gMap.Icons.Add(tiny);

    gMap.AddControl(new GSmallMapControl());
    gMap.AddControl(new GMapTypeControl());
    gMap.CenterAndZoom(gp, 4);

    GMarker gm = new GMarker(gp, "TinyMarker", tiny.Id);
    gMap.Overlays.Add(gm);
    }

    DataListExample.aspx

    __Show Me__ This is the final example that I wanted to throw into this article. I wanted to demonstrate just how easily you could create multiple GMap controls. This example creates six (6) maps centered at the same point each with a different zoom level.

    <body>
    <form id="Form1" method="post" runat="server">
    <asp:DataList Runat="server" ID="dlMaps"
    RepeatDirection="Horizontal"
    RepeatColumns="2"
    RepeatLayout="Table">
    <ItemTemplate>
    <wcp:GMap runat="server"
    ID="gMap"
    width="400px"
    height="240px" />

    <asp:Label Runat="server">
    Location: (-122.141944, 37.441944) Zoom Level:
    <%# Container.DataItem %>
    </asp:Label>
    </ItemTemplate>
    </asp:DataList>

    </form>
    </body>

    DataListExample.aspx.cs

    Here, I get a little fancy by creating an ArrayList of zoom values. This ArrayList serves as the DataSource for your DataList. During the DataBinding, you get a reference to each GMap and center it at the given point and zoom out by one step each time.

    protected DataList dlMaps;
    private const int MAX_ZOOM = 6;

    private void Page_Load(object sender, System.EventArgs e)
    {
    ArrayList al = new ArrayList();
    for( int i=1; i<=MAX_ZOOM; i++ )
    al.Add(i);

    dlMaps.DataSource = al;
    dlMaps.DataBind();
    }

    private void dlMaps_ItemDataBound(object s,
    DataListItemEventArgs dliea)
    {
    GMap map = (GMap)dliea.Item.Controls[0];
    int zoom = Convert.ToInt32(dliea.Item.DataItem);
    map.CenterAndZoom(new GPoint(-122.141944F, 37.441944F), zoom);
    }

    Conclusion

    I hope you are able to get some good mileage out of the GMap control.

    Downloads

  • WCPierce.zip - Complete source files
  • WCPierceWebChm.zip - Compiled HTML Help file for control