AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=685&pId=-1
A Customizable Login Server Control
page
by Bilal Haidar
Feedback
Average Rating: 
Views (Total / Last 10 Days): 59093/ 50

Introduction and Control Features

[ Download Source Code ]

Introduction

One of the strengths of ASP.NET is the ease with which developers can create custom web server controls. If you find yourself repeatedly adding the same code across pages or across applications, you should consider creating a custom server control.

If you create websites that require users to log in, then you are sure to have repeated the code needed to obtain a user's username and password. This is a prime candidate for a custom server control.

In this article we will cover the details of developing a customizable Login control that you can add to your Visual Studio .Net toolbox, which can then be added to any ASP.NET Web Form with a simple drag and drop action. The Login control has a set of useful features that we will cover in the different sections of this article.

Outline

In this article, we will start by outlining the various features of the Login control, and then look in depth at the code behind building that control. Finally, we will present an example of how to use the Login control on an ASP.NET Web Form.

Login Control Features

The Login control that we will create is a customizable control, in the sense that it contains many features that can be turned on or off according to the developer's preference. The appearance of the control is also customizable through the use of CSS.

To start with, the Login control has the following basic content:

  1. A UserName TextBox, to hold the username.
  2. A Password TextBox, to hold the password.
  3. An Error Label, to show any error generated upon a login failure.

The above controls are the basic contents of the Login control. Its customizable features are:

  1. A Remember Me CheckBox, to give the user the option to have a persistent cookie of his login.
  2. A Create New Account HyperLink, to link to the page where a user can create a new account.
  3. A Forgot Password HyperLink, to link to the page where a user can retrieve his password.

The above features will be presented in detail in the following sections.

Control Outline and Private Fields

[ Download Source Code ]

Control Skeleton

Since we are developing a custom web server control, the basic step is to create a class that inherits from WebControl and implements INamingContainer, as follows:

    [DefaultProperty("Text"),ToolboxData("<{0}:Login runat=server></{0}:Login>")]
    public class Login : WebControl, INamingContainer
    {
        // Login control code
    }

Why Inherit from WebControl?

WebControl is the building block for all web server controls. By inheriting from this class, we will be able to access and override all the methods in that class, instead of having to rewrite them from scratch.

Why Implement the INamingContainer Interface?

When a control implements this interface, its behavior is to ensure that the ClientID property of each child control is unique within the Web Form. (For more information about this interface, view the MSDN documentation.)

Private Fields

The following private fields are added to the control skeleton.

    /* Private Fields */
    private Button _SubmitButton;
    private TextBox _UserName;
    private TextBox _Password;
    private Label _ErrorLabel;
    private CheckBox _AutoLogin;
    private HyperLink _CreateAccount;
    private HyperLink _ForgotPassword;
    private RequiredFieldValidator _UserNameReq;
    private RequiredFieldValidator _PasswordReq;

In the code above, we can see the different server controls that are used to build our custom login control:

  1. A Button, used to submit the form.
  2. Two TextBoxes, used for obtaining the username and password.
  3. An Error Label, used to display any error messages based on the input of the user.
  4. A CheckBox, used to determine if the user wants a persistent cookie.
  5. Two Hyperlinks, used to provide links to pages where the user can create a new account or retrieve a forgotten password.
  6. Two RequiredFieldValidators, used to validate the user input.
Exposing Control Properties

[ Download Source Code ]

Public Properties

For the Login control to be customizable, some of the previously listed private fields need to be made accessible. For that, we create several properties which make it possible for the page developer to get and set those private fields.

The Login control exposes ten properties, some of which will now be explained in detail.

UserName Property

The first property to explain is the UserName property:

    /* Properties */
    [Bindable(false),
    Category("TextBoxes"),
    DefaultValue(""),
    Description("The username textbox value")]
    public string UserName
    {
        get
        {
            this.EnsureChildControls();
            return HttpContext.Current.Server.HtmlEncode(_UserName.Text);
        }
        set
        {
            this.EnsureChildControls();
            _UserName.Text = value;
        }
    }

As you can see, in the get section of the above property, we start by calling the EnsureChildControls() method. This method is called to make sure the current Login control has child controls, which it does by first checking the value of the ChildControlsCreated property. If this property is false, meaning that the child controls are not created, then the CreateChildControls method is called so that the child controls will be created. Otherwise, if the ChildControlsCreated property returns true, meaning that the child controls have been created, we can access and work with those child controls. In the get section of this property, after ensuring that the UserName TextBox has been created, we then access its Text value and return it.

In the set section, we ensure the child control creation in a similar way, and then set the Text property of the UserName TextBox.

The Password and ErrorLabel properties can be explained the same way as the UserName property. For that reason, we will not go into the details of those properties.

IsChecked Property

The IsChecked property is defined as follows:

    [Bindable(false),
    Category("Appearance"),
    DefaultValue(""),
    Description("Returns whether the autologin checkbox is checked or not")]
    public bool IsChecked
    {
        get
        {
            this.EnsureChildControls();
            if ( _AutoLogin.Checked == true )
                return true;
            /* Not Checked */
            return false;
        }
        set
        {
            this.EnsureChildControls();
            _AutoLogin.Checked = value;
        }
    }

The above property takes care of the Remember Me CheckBox, returning a Boolean value indicating whether the check box is checked or not.

CreateAccount Property

The CreateAcount property is defined as follows:

    [Bindable(false),
    Category("Appearance"),
    DefaultValue(""),
    Description("Enable Create Account or not?")]
    public bool CreateAccount
    {
        get
        {
            object enableCA = this.ViewState["EnableCreateAccount"];
            if (enableCA != null)
            {
                return (bool) enableCA;
            }
            /* Default don't allow create account */
            return false;
        }
        set
        {
            this.ViewState["EnableCreateAccount"] = value;
        }
    }

This property of the Login control enables the page developer to show or hide the link to the page where the user can create a new account.

In the get section, we get the value of the EnableCreateAccount from ViewState, as we are using ViewState to persist the value of the CreateAccount property across postbacks. If the value is different from null, we cast and return the value, otherwise we return false. This means that the CreateAccount link is hidden by default, and visible only if the developer explicitly enables it.

In the set section, we store the value of this property in ViewState.

The ForgotPassword and RememberMe properties can be explained in the same way as the above CreateAccount property. The ForgotPassword is used to configure whether we show or hide the link to the page where the user can retrieve his password. The RememberMe property is used to configure whether we show or hide the check box used to remember the login of a user.

UsernameClientID Property

The last property is usernameClientID, which returns the ClientID of the UserName TextBox. This property is used by the client-side SetFocus function, which I will discuss later.

    [Bindable(false),
    Category("Appearance"),
    DefaultValue(""),
    Description("Get username client Id")] 
    public string usernameClientID
    {
        get
        {
            this.EnsureChildControls();
            return _UserName.ClientID;
        }
    }

Rendering the Control

[ Download Source Code ]

Building the Control

In this section, we will discuss the CreateChildControls() method, which is used to create the interface of the login control. The building block of the Login control’s interface is an HTML table, which is used to hold all the server controls that make up this composite control. If you have a look at the CreateChildControls() method, we start by adding some HTML tags, representing the table and its rows and columns.

After that, I add the first control, which is the UserName TextBox:

    // Configure & Add UserName TextBox
    _UserName = new TextBox();
    _UserName.ID = "username";
    _UserName.Attributes.Add("autocomplete","off");
    _UserName.Attributes["onclick"] = disableCLick();
    _UserName.Attributes["onload"] = "setFocus()";
    _UserName.MaxLength= 64;
    _UserName.Width= 100;
    _UserName.CssClass ="Txt";
    _UserName.EnableViewState = false;
    _UserName.TabIndex = 1;
    this.Controls.Add(_UserName);

As you can see, the UserName TextBox has been configured and then added to the Controls collection. Next, we add a RequiredFieldValidator programmatically to ensure that the user will enter a value in that text box:

    // Configure & Add RequiredFieldValidator for UserName TextBox
    _UserNameReq = new RequiredFieldValidator();
    _UserNameReq.ID = "usernameReq";
    _UserNameReq.ErrorMessage = "*";
    _UserNameReq.ControlToValidate ="username";
    _UserNameReq.ToolTip = "Username Required";
    _UserNameReq.Display = System.Web.UI.WebControls.ValidatorDisplay.Dynamic;
    this.Controls.Add(_UserNameReq);

If you continue checking the code, you will notice that the same procedure is used to add the Password TextBox and its RequiredFieldValidator, the Error Label, and the Submit Button.

Now we check the above-created properties to know which additional features are to be displayed in the control.

First, we check the RememberMe property discussed earlier. If its value is true, we add the check box, otherwise we do not.

    // Check if rememberme checkbox is on
    int flag = 0; // flag used for setting tabindex
    if ( RememberMe )
    {
        _AutoLogin = new CheckBox();
        _AutoLogin.ID = "autoLogin";
        _AutoLogin.Checked = true;
        _AutoLogin.Text = "Remember me";
        _AutoLogin.CssClass = "LoginCheck";
        _AutoLogin.TabIndex = 3;
        this.Controls.Add(new LiteralControl("<tr><td colspan=\"2\" align=\"right\">"));
        this.Controls.Add(_AutoLogin);
        this.Controls.Add(new LiteralControl("</td><td></td></tr>\r\n"));
        flag = 1;
    }

Notice the presence of the variable named flag. This flag is used to determine the tab index of the Submit button. Suppose that the RememberMe property has a value of false, which means that the check box will not be added. In this case, the tab index of the Submit button would be 3. Suppose now that the RememberMe property has a value of true, which means that we must add the check box and give it a tab index of 3. In this case, the Submit button would need a tab index of 4. The flag variable is to help calculate the right tab index.

If you continue to check the code, you will notice that we check the value of the CreateAccount property. If its value is true, we configure and add a link to the page that allows the user to create a new account. Later on, we check the value of the ForgotPassword property. If its value is true, we configure and add a link to the page that allows the user to retrieve his password. One thing to mention here is that the URL of both pages can be configured by the page developer.

If you check the configuration section for the UserName and Password Textbox controls, you would notice the use of a private method named disableClick(), which provides the client-side script needed to prevent the user from submitting the web form by pressing the Enter key when the focus is inside either of those text boxes. This way, the form will be submitted only if the user clicks the Submit button.

Another client-side function is SetFocus, which is used to set the focus to the UserName TextBox upon loading of the page. Notice that the SetFocus() function has been added in the OnPreRender() method, which is the best place to add client-side scripts, as stated by Scott Mitchell in his article, "Injecting Client-Side Script from an ASP.NET Server Control."

Raising the Click Event

[ Download Source Code ]

Handling the Click Event

In order to be able to process the data entered by the user, we need to have a method that handles the Click event of the Submit button. The following code is used to register an event handler for the button’s Click event.

    _SubmitButton.Click += new EventHandler(OnBtnLink_Click);

The Click event is defined as follows:

    /* The sole event hook that the button exposes. */
    public event EventHandler Click;

The button’s Click event handler is defined as follows:

    /* Event handling for the Login Button */
    protected virtual void OnBtnLink_Click(object sender, EventArgs e)
    {
        if (Click != null)
        {
            Click(this, e);
            UserName = string.Empty;
            Password = string.Empty;
        }
    }

This event handler means that when the user clicks the Submit button, the Click event of the Login control is bubbled up to the page. This way, the page developer can define a method to handle its Click event:

    private void Login1_Click(object sender, System.EventArgs e)
    {
        // Put user code to initialize the page here
        Response.Write("UserName&nbsp;:&nbsp;s" + Login1.UserName);
        Response.Write("<br />Password&nbsp;&nbsp;:&nbsp;" + Login1.Password);
    }

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.Load += new System.EventHandler(this.Page_Load);
    }

In the InitializeComponent() method, you need to attach an event handler for the Login control’s Click event. In the above example, we are just writing out to the page whatever the user enters in the UserName and Password text boxes. In a real application, these values would be validated against a data store of valid credentials.

Final Points

[ Download Source Code ]

CSS for the Login Control

If you check the main.css file in the downloadable sample application, you will notice a section dedicated to the Login control. Feel free to customize the CSS section so that the appearance of the Login control will work with your own page design.

Code

The code download contains two projects: the custom web server control (LoginControl), and an ASP.NET web application (LoginWebControl) to test the control.

Conclusion

In this article, we have provided a detailed explanation of how to create a customizable Login server control. In future articles, we will introduce other controls, and discuss others topics which are of interest to any ASP.NET developer.

If you would like to have a look at the other controls that I developed, please visit my website at www.bhaidar.com and check the ASP.NET controls.

Hope you enjoyed my article,

Happy Dot Netting!


Product Spotlight
Product Spotlight 

©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-25 6:44:52 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search