Build controls that post your data

Get your newborn data back to the server safely.

Please let other users know how useful this tip is by rating it below. Do you have a tip or code of your own you'd like to share? Submit it here.


Get your newborn data back to the server safely.

No one likes a production or development bottleneck. As developers, part of our job not only is to minimize them, but to alleviate them completely. One way to accomplish this is to improve the process of posting data back to the server -- the golden nugget of Web development, if you will. In this article we'll show you a way to simplify the process of getting data back to the server by encapsulating the posting logic in a control.

The control used in this demo is a simple login control, which abstracts the authentication process by managing the validation of the submitted credentials against those stored in a database. The control manages the user ID field, a password field, and a Sign In button -- pretty standard stuff. The control exposes four properties: ConnectionString, UserIDField, PasswordField and TableName. The purpose of each property is defined briefly in Figure 1.

Property Definition
ConnectionString Database connection information including authentication and database name
UserIDField Contains the database field to authenticate the UserID
PasswordField Contains the database field to authenticate the Password
TableName Contains the table name to authenticate users

Figure 1. LoginDialog properties let you specify the values and allow the control to manage authentication.

In the June issue of asp.netPRO (Build a Control Designer"), we talked about control designers and the difference between rendering HTML and creating child controls. Although creating child controls in your custom controls would simplify the process of handling postbacks -- because the child controls typically have their own built-in postback logic -- the performance and control you get from rendering allows much more flexibility. When a custom control is built using the rendering method, the control developer must supply the postback logic.

In order for the control to examine form data posted back by the client, you must follow two initial steps. First, the control must implement the System.Web.UI.IPostBackDataHandler interface. This interface lets the control determine whether its state should be altered as a result of the postback and (if necessary) raise the associated events. The IPostBackDataHandler has two methods: LoadPostData and RaisePostDataChangedEvent. Figure 2 defines both methods. The second -- and most important -- step is to code to the LoadPostData and RaisePostDataChangedEvent methods to process the data received when the page is posted, then raise any related events.

Method Definition
LoadPostData Processes postback data for an ASP.NET server control
RaisePostDataChangedEvent Instructs ASP.NET server controls to notify the ASP.NET application that the state of the control has changed

Figure 2. This table shows IPostBackDataHandler members and their definitions. See the object browser for the Microsoft perspective.

The page framework uses the UniqueID property to route postback data back to your control. When you use the IPostBackDataHandler interface, at least one constituent control must use the UniqueID. And when you use multiple constituent controls, you can append additional data to the UniqueID property to identify each of the controls uniquely:


protected override void Render(HtmlTextWriter output)
{
    output.Write("<INPUT type="text" name="" +
        this.UniqueID + "_UserName" value="" +
        this.Text + "">");
}

Check in with LoadPostData

The LoadPostData event checks for changes to the state of a custom control's constituent controls. A control whose state has changed causes this event to return true; if none of the constituents' controls have changed state, the LoadPostData method returns false. When a postback occurs, the page framework searches the posted data for values matching the server controls' unique IDs that implement the IPostBackDataHandler interface. Then, the LoadPostData method is invoked on each control that implements the interface:


public virtual bool LoadPostData(string postDataKey,
NameValueCollection values)
{
    String originalValue = Text;
    String postedValue = values[postDataKey];
    if (originalValue == null ||
           !originalValue.Equals(postedValue))
    {
        Text = postedValue;
        return true;
    }
    return false;
}

The LoadPostData method in this example simply checks to see if the data posted is different from the original data in the control.

To use the LoadPostData method, at least one constituent control must use the this.UniqueID property as its ID. Additional controls must also use this property, though additional information should be appended to ensure uniqueness. For example, a UserName textbox could use an ID like this:


output.Write("<input type="text" size="15"   +
"style="font-size:11px;width:150px;" ");
//Set the ID and Name values for the UserName input
output.Write("name="");
output.Write( this.UniqueID );
output.Write("_UserName" id="");
output.Write( this.UniqueID );
output.Write("_UserName" value="");
//Set the username textbox to the current value
output.Write( UserName );
output.Write("" />");

Raise your events

The RaisePostDataChangedEvent method raises any events that help process posted data. As postbacks occur, the page framework tracks all the controls that return true from the LoadPostData method and invokes RaisePostDataChangedEvent on them. Any related Change events are raised from this method. Postbacks work in a two-phase process; both phases are necessary to separate the loading of postback data from the notification of changes. In the first phase of postback data handling, all controls that implement IPostBackDataHandler are checked for changes. In the second phase, controls that have changes raise change notification events. This process isolates postback changes from changes that occur as a result of data changed in an event.

Imagine a scenario where a control has two textboxes. The first textbox contains a change event that searches a database for the UserID contained in the Text property of the textbox and converts it to the user's name. The second textbox contains an event to search for the user's phone number based on the UserID entered in the first textbox. In this scenario, you can't find the phone number without the two-phase postback processing because the first textbox would process its change and display the user's name, and the second textbox can't find the UserID because it has been replaced with the user's name:


public virtual void RaisePostDataChangedEvent()
}
    OnChange(EventArgs.Empty);
}

The RaisePostDataChangedEvent method is used in this example to raise the OnChange event. The control can use this event internally, or you can use the control to override it.

Here's a textbox example to demonstrate how the postback process works without the overhead of connection strings and database access. The control- SimpleText- shows the LoadPostData and RaisePostDataChangedEvent methods in their basic forms. Figure 3 contains the code for this textbox control.


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

namespace ControlTowerPostBack
{
  public class SimpleText: Control, IPostBackDataHandler
  {
    public String Text
    {
get
  {
      return (String) ViewState["Text"];
  }
  set
  {
      ViewState["Text"] = value;
  }         
    }
  
    public event EventHandler Changed;

    public virtual bool LoadPostData(string postDataKey,
    NameValueCollection values)
    {
   String originalValue = Text;
          String postedValue = values[postDataKey];
          if ( originalValue == null ||
!originalValue.Equals(postedValue))
  {
      Text = postedValue;
      return true;
  }
  return false;
    }
  
    public virtual void RaisePostDataChangedEvent()
    {
        OnChange(EventArgs.Empty);
    }
  
    protected virtual void OnChange(EventArgs e)
    {
        if (Changed != null)
        Changed(this,e);
    }
  
    protected override void Render(HtmlTextWriter output)
    {
        output.Write("<input type="text" name="" +
this.UniqueID + """   +
value="" + this.Text + "">");
    }
  }
}

Figure 3. Here's the complete code for the SimpleText server control, which simply is a textbox that handles postback data by comparing the posted data with the original control data.

As you can infer by its name, the SimpleText control is nothing fancy; it's exactly the same (only different!) than the ASP.NET server control textbox included in Visual Studio. Figure 4 shows the SimpleText control at run time on an ASP.NET WebForm.

Figure 4
Figure 4. Here's a SimpleText textbox control shown at run time with postback data.

Leverage your interface methods

Now that you understand the IPostBackDataHandler interface, let's look at a control that takes full advantage of the power of the interface methods.

The LoginDialog control uses the LoadPostData method to verify that credentials are entered correctly and validated against a database. The connection string, database table name and database fields to use for validation are all user definable via control properties. Figure 5 shows the login control after the user enters data but before it submits the data to the server; at this point its values collection (this.UniqueID) is null.

Figure 5
Figure 5. Here's the login control with username and password entered before the Sign In button has been pressed.

After the user presses the Sign In button, the data is submitted and the control's LoadPostData method executes. The first check the LoadPostData method performs is to verify that data has been entered into the control. If so, submitted values will be contained in the values collection. Because at least one control must have its ID set to this.UniqueID, you must check for a non-null value in that control's values collection:


public virtual bool LoadPostData
          (string postDataKey,NameValueCollection values)
{
...
 
//If the user submitted values return true
//This indicates that the state of the control changed
  if (
       ( UserName != null )
      ||
       ( Password != null )
      ||
       ( values[this.UniqueID] != null )
     )

Your next step is to verify that the data has been submitted and, if you're satisfied that the data is valid, you must build a SQL statement -- on the fly -- to check the database table (passed by the user) to verify a user's credentials, based on the username and password the user enters:


//Build the T-SQL command using named parameters
  System.Text.StringBuilder sbSql =
 new   System.Text.StringBuilder();
  sbSql.Append( "SELECT COUNT(*) FROM " );
  sbSql.Append( this.TableName );
  sbSql.Append( " WHERE " );
  sbSql.Append( this.UserIdField );
  sbSql.Append( " = @UserName AND " );
  sbSql.Append( this.PasswordField );
  sbSql.Append( " = @Password" );

//Note: Named parameters will help protect you from
//hackers injecting malicious T-SQL code

Next, you perform the routine data-access steps, such as connect to the database, create a SqlCommand object, and so on. Eventually, you'll return a value of zero or more. (As in most situations, more than zero is a good thing!) If there are no database errors and the proper credentials were entered - and you remembered to start SQL Server (oops!) -- the control will appear as shown in Figure 6.

Figure 6
Figure 6. This is what you'll see in the login control after the user presses Sign In and he or she is authenticated.

The LoadPostData method returns false if you get back a value of zero or if any controls have a null value. Figure 7 shows what the method displays in this situation.

Figure 7
Figure 7. After pressing the Sign In button, this user's credentials return as invalid.

Handle your postbacks

We should note that this article's LoginDialog control is intended for demonstration purposes only. Although it can be used in a production environment, in large-scale applications it's often frowned upon to have the presentation code (a UI element) manage connections to the database.

As you can see in this article, handling postback data is not as complicated as you might have thought. With only a few lines of code, you can enable postbacks in your controls.

Doug Seven is a senior .NET developer for Atomic Consulting Group Inc. with several .NET books to his credit and a few more on the way. Seven is a co-founder of the .NET online resource DotNetJunkies, and he has worked with a variety of clients, from start-up companies to Fortune 500 companies, developing everything from desktop- and Web-based applications to bleeding-edge proof-of-concept applications using mobile devices. E-mail Doug at dseven@dotnetjunkies.com.

Antoine Victor is a senior software developer with more than 10 years of experience writing applications in various programming languages, including VB .NET and C#. Antoine has developed document-management systems, e-commerce Web applications, inventory- and order-tracking systems, and disaster-recovery plans. Antoine currently focuses on high-level consulting projects and spreading the .NET gospel, and he is a co-founder of Professional Data Management. E-mail Antoine at Antoine@prodataman.com.


This article was provided by asp.netPRO Magazine, an online information resource for the ASP.NET developer community, which helps professional software developers build, deploy and run the next generation of dynamic, distributed Web applications quickly and easily. Click here to learn more.
This was first published in December 2003

0 comments

Oldest 

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to:

-ADS BY GOOGLE

SearchCloudComputing

SearchSoftwareQuality

SearchSOA

TheServerSide

SearchCloudApplications

Close