Howto: Create a Custom Control in ASP.NET (C#)

October 30, 2007 12:34 by Dominick

Introduction

Creating custom controls can save you a lot of time and effort when building an application. The built-in sever controls provided can do just about anything you need them to, but sometimes it takes a great deal of coding (hacking) to make it do exactly what you want it to do. Consider the possibility that you need a textbox control that you would like to be able to assign a readony property to it so it either renders as a regular textbox if false or plain text if true. You may also want a textbox that will cause it's on "OnTextChanged" to bubble up in the "GridViewCommandEvent" of a Gridview control. By default the textbox does not support that.

Afterwards there will be Cake

Create a new Web Control Library project within the solution you want to add the control to. Here is the location to do so in my Visual Studio installation.



Visual Studio has created a file called "WebCustomControl1.cs" within that project. You can rename it to whatever you like. This file is where you can insert the code that I'll show below. I'm going to show you the entire code right now. I've put some comments in to explain what's going on. Just copy and paste this code inside the cs file.

using System;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CustomControls.UI.Controls
{
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:CustomTextBox runat=server></{0}:CustomTextBox>")]
    public class CustomTextBox : TextBox //with this you can override all the normal "stuff" within the textbox control
    {

       //Here are the properties that will be used for the command event

        [Bindable(true)]
        [Category("Custom")]
        [DefaultValue("")]
        [Localizable(true)]
        public string CommandName
        {
            get
            {
                string s = ViewState["CommandName"] as string;
                return s == null ? String.Empty : s;
            }
            set
            {
                ViewState["CommandName"] = value;
            }
        }

        [Bindable(true)]
        [Category("Custom")]
        [DefaultValue("")]
        [Localizable(true)]
        public string CommandArgument
        {
            get
            {
                string s = ViewState["CommandArgument"] as string;
                return s == null ? String.Empty : s;
            }
            set
            {
                ViewState["CommandArgument"] = value;
            }
        }

        [Bindable(true)]
        [Category("Custom")]
        [DefaultValue("False")]
        [Localizable(true)]
        public string IsReadOnly
        {
            get
            {
                string s = ViewState["IsReadOnly"] as string;
                return s == null ? String.Empty : s;
            }
            set
            {
                ViewState["IsReadOnly"] = value;
            }
        }
        
        protected static readonly object EventCommandObj = new object();

        public event CommandEventHandler Command
        {
            add
            {
                Events.AddHandler(EventCommandObj, value);
            }
            remove
            {
                Events.RemoveHandler(EventCommandObj, value);
            }
        }

        //this will raise the bubble event
        protected virtual void OnCommand(CommandEventArgs commandEventArgs)
        {
            CommandEventHandler eventHandler = (CommandEventHandler)Events[EventCommandObj];
            if (eventHandler != null)
            {
                eventHandler(this, commandEventArgs);
            }
            base.RaiseBubbleEvent(this, commandEventArgs);
        }

        //this overrides the OnTextChanged event on the normal textbox
        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);

            if (AutoPostBack)
            {
                //pass the event arguments to the OnCommand event to bubble up
                CommandEventArgs args = new CommandEventArgs(this.CommandName, this.CommandArgument);
                OnCommand(args);
            }
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
        }

        protected override void Render(HtmlTextWriter writer)
        {
            //if its readonly then render the text in a span tag
            if (IsReadOnly == "True")
            {
                writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
                writer.RenderBeginTag(HtmlTextWriterTag.Span);
                writer.Write(this.Text);
                writer.RenderEndTag();
            }
            else
            {
                //if not then render the textbox
                base.Render(writer);
            }
        }
    }
}

The Cake is a Lie

Now anytime you need to have the OnTextChanged event fire and bubble up within a Gridview than just use this control. It will also be very handy to do able to cause the textbox to become truely readonly.

 

kick it on DotNetKicks.com



Showing the Users Currently on your Site (Part 2)

October 24, 2007 13:14 by Dominick

An Evolution

The first part (read it here) was a simple implementation that just showed the number of active users on your site. It used the global.asax file to count the sessions and retain the total in a cached object. I thought that was all I really wanted to see, but after I realized I could take it up a notch with bit more effort I decided to do so. You can see the working product over there on the left.

I thought it would be usefull to show how many people are on your site, which page they are on and where they came from (URL referrer). Getting away from simple session tracking was a must. This implementation should be a set of controls that could be used in any ASP.NET application whether it had the membership provider enabled or not. Just drop a control on any page you want tracked or on a master page if you wanted them all tracked. Another control that shows the stats and one more to show a history of activity for a specified duration. Drag and drop DLL into bin folder. Drag and drop control onto page.  So easy even a manager could do it.

I want to release the source, but I've been debating on the best way to go about it. I've been leaning towards creating an open source project for the controls so everybody can download and improve it. I'm a huge fan of open source .net projects. I use SubSonic on just about every new project I work on. Of course, BlogEngine.net is what I'm using for this blog. Anyway, what are your thoughts? Should I create an open source project for these controls?

So easy even a manager could do it

An Overview

I created a user object which stores the information for each online user. The best way to keep a collection of like objects is to user Generics. A UsersOnlineCollection object was created for this purpose. I also created one to track the "history". The history collection keeps track of the last 50 (or whatever number you choose) users that have hit your site. With tracking control on the page everytime it loads the "ProcessOnlineUsers" method is called. This is where all the action happens. It does the following:

  • Grabs the current users online collection
  • Checks to see if the current user is in the collection (checks IP address rather than sessionId)
  • If the user is already in the collection then the stats for the user are updated (current page, last activity time, timeout time, etc)
  • If the user is not in the collection then they are added. They are also added to the history collection.
  • Then the users that are inactive are deleted from the collection

All the statistics are still stored in a cached object. No database interaction is needed. I wanted this to have no adverse effects on the performance of my site.

Cluster Issues

If your site is served by a cluster of servers you will have issues. This is due to the fact that the cached object is localized to whichever server you are currently on. You'll notice the stats may change when you look at the working demo on the left. This, of course, is because my site is on a cluster. You can see the name of the current server in the cluster right below the users online list. This is to allow you to see the effects of switching to another server in the cluster.

There are ways to fix this problem with additional software (NCache for one) and server configurations. I'm not sure if keeping the state within SQL Server would solve the issue, but I don't believe its an option in my situation. Regardless, this solution will working just fine for the majority of people who'd like to use it.

Conclusion 

I need some feedback at this point. Anybody interested in the source? Should I just create an open source project? I think this could be really usefull to a lot of people. There's a bunch of stuff I'd still like to add/fix/change to make the controls work better so I'm hoping there enough interest for me to continue to do so.  

Feel free to leave a comment to voice your opinions.

kick it on DotNetKicks.com



Repeater control alternating backgrounds

October 8, 2007 20:02 by Dominick
Here's an easy way to have an alternating background color with a Repeater control. There's no "AlternatingItemStyle" like in other data list controls, but this does this trick with a couple lines of code.


protected void OnItemDataBound(object sender, RepeaterItemEventArgs e)
{
  if (e.Item.ItemType == ListItemType.AlternatingItem)
   {
     ((HtmlTableRow)e.Item.FindControl("Row")).BgColor = "silver";
   }
}



kick it on DotNetKicks.com