Creating a Custom .NET Web Control With Events

By Seth Juarez

As I was attempting to create an extranet application for our clients, I found little discussion regarding the proper use of .NET Web controls. A few of the questions that seemed to pervade the exercise were:

  1. How do these controls intercommunicate?
  2. How do these controls maintain state?
  3. How do I glue multiple controls together efficiently in one page?

I was an ASP developer and found moving to ASP.NET was not at all intuitive. My initial attempts revolved around maintaining state either through the Session object or using the query string. I found both methods sloppy and noticed problems when attempting to synchronize all Web controls on the page. I stumbled across an article regarding creating events within Web controls and still had to stumble through the exercise. I felt it was important to provide a systematic tutorial regarding the proper creation of Web controls as well as creating custom events for them.

This discussion will proceed as follows:

  1. Creating a Web control
  2. Creating the controls custom events and event arguments.
  3. Using the Web control properly within a page.

During the discussion I will also attempt to expose some practices that will enable you to develop applications more accurately and rapidly.

The Web control that will be created today will be a custom drop down box based on the stores table in the pubs database located in any standard install of SQL Server or MSDE. Visual Studio .NET 2003 will be the tool to create the project with, and the language used will be C#.

After creating the Pubs Web Project, the first task (at least for me) is renaming the WebForm1.aspx file to Default.aspx and changing the code-behind class to match the name. The next step is creating a folder structure within the IDE that will be conducive to finding objects using Intellisense more "intellisensably."

I created the Controls directory to house all of the controls that will be built in order to access them more readily. Further subdivisions within the Controls directory is also desirable depending on the level of granularity you would like to have when creating these controls.

I will name the control StoreSelector.ascx. The first step will be to add the DropDownList control on to the form.

Now for the actual wiring of the control:

Create a Class wide Dataset object


#code
private DataSet data;
#end code

Create a BindData function that pulls all of the data into the drop down list:

#code
private void BindData()
{
  data = new DataSet(); 
  SqlConnection cnn = new SqlConnection("Data Source=(local);Initial 
Catalog=pubs;Integrated Security=SSPI");
  SqlDataAdapter adapter = new SqlDataAdapter();
  adapter.SelectCommand = new SqlCommand("SELECT stor_id, stor_name, 
stor_address, city, state, zip FROM stores", cnn);
  adapter.Fill(data, "stores");
  storeList.DataSource = data;
  storeList.DataMember = "stores";
  storeList.DataTextField = "stor_name";
  storeList.DataBind();
  Session.Add("Data", data);
}
#end code

I added the DataSet object to the Session variable in order to have the data available throughout the duration of the session and when passing the data during the firing of our Control Event.

Make sure the Page_OnLoad event is properly filled out:


#code
private void Page_Load(object sender, System.EventArgs e)
{
	if(!Page.IsPostBack)
	{
		BindData();
	}
}
#end code

Let's test the code by dragging the new control onto the Default.aspx page and running the project:

Simple right? Now for the trickier part. Say we want a couple of labels on the Default.aspx page to respond to changing Stores. We want each label to display each of the columns of the currently selected store. This is where we need to create a custom event for the StoreSelector control as well as its own event argument class. Let's begin by creating the Event Argument Class (StoreSelectorCommandEventArgs.cs):


#code
public class StoreSelectorCommandEventArgs
{
	private string _stor_id;
	private string _stor_name;
	private string _stor_address;
	private string _city;
	private string _state;
	private string _zip;

	public StoreSelectorCommandEventArgs(string stor_id, string stor_name, 
		string stor_address, string city, string state, string zip)
	{
		_stor_id = stor_id;
		_stor_name = stor_name;
		_stor_address = stor_address;
		_city = city;
		_state = state;
		_zip = zip;
	}

	public string stor_id{ get{ return _stor_id; } }
	public string stor_name{ get{ return _stor_name; } }
	public string stor_address{ get{ return _stor_address; } }
	public string city{ get{ return _city; } }
	public string state{ get{ return _state; } }
	public string zip{ get{ return _zip; } }
}
#end code

The purpose of this class is for the usual "e" variable that defines the event arguments. What we have done is simply create one of our own. Now for the delegate class that defines how the event is to be handled (StoreSelectorCommandEventHandler.cs):

#code
public delegate void StoreSelectorCommandEventHandler(object sender, 
StoreSelectorCommandEventArgs e);
#end code

Here are the resulting files:

Now we need to tweak the StoreSelector control to throw the event.

The following lines should be added to the StoreSelector control to expose this new event we have created:


#code
public event StoreSelectorCommandEventHandler StoreSelectorChanged;
protected virtual void OnStoreSelectorChanged(StoreSelectorCommandEventArgs e)
{
	if(StoreSelectorChanged != null) StoreSelectorChanged(this, e);
}
#end code

Now that the event has been defined for the control, we need to fire the event. We planned on firing the event on the DropDownList OnChange event. Make sure the DropDownList control has AutPostBack set to true.

Here is what the code to the event looks like:



#code
private void storeList_SelectedIndexChanged(object sender, System.EventArgs e)
{
	data = (DataSet)Session["Data"];
	OnStoreSelectorChanged(
		new StoreSelectorCommandEventArgs
		(data.Tables["stores"].Rows[storeList.SelectedIndex].ItemArray[0].ToString
(),
		data.Tables["stores"].Rows[storeList.SelectedIndex].ItemArray[1].ToString(
),	
		data.Tables["stores"].Rows[storeList.SelectedIndex].ItemArray[2].ToString(),
		data.Tables["stores"].Rows[storeList.SelectedIndex].ItemArray[3].ToString(),
		data.Tables["stores"].Rows[storeList.SelectedIndex].ItemArray[4].ToString(),
		data.Tables["stores"].Rows[storeList.SelectedIndex].ItemArray[5].ToString(
)));
}
#end code

Let me break down what I am doing here. When the SelectedIndexChanged event is fired, I pass it along to the new event that we created for the control. The data I am passing relates directly to the dataset that we filled. I am passing each of the items into a new StoreSelectorCommandEventArgs object and then firing the event.

We are done!

To access the new capabilities in the Default.aspx page, we simply add the event handling in the OnInit section of the class:

As you can see, the StoreSelectorChanged event is now visible in the Default.aspx page. Let's assign it a function. I will also add six label controls on the Default.aspx page to display the values as the DropDownList is changed:

Now let's fill in the event.

The beauty of the whole thing is the way Intellisense recognizes the properties of your custom EventArg class:

The final event function will look like this:


#code
private void StoreSelector1_StoreSelectorChanged(object 
sender, Pubs.Controls.StoreSelectorCommandEventArgs e)
{
	Label1.Text = e.stor_id;
	Label2.Text = e.stor_name;
	Label3.Text = e.stor_address;
	Label4.Text = e.city;
	Label5.Text = e.state;
	Label6.Text = e.zip;
}
#end code

Now for the test! As soon as the page loads you will probably think: "IT DIDN'T WORK!" Let me assure you that it does. If you would like to have the event fired when it first starts up, you will have to set that up in the Control that we have created by exposing the selected index property of the DropDownList Control (we will point this out later).

As soon as you select another Store from the DropDownList, you will notice that the Labels do in fact change:

Now let's make the form load up (by default) to the first record.

Let's add the following property to the StoreSelector Control:


#Code
public int SelectedIndex
{
	get{ return storeList.SelectedIndex; }
	set
	{
		if(!Page.IsPostBack)
		{
			BindData();
		}
		if(value < storeList.Items.Count)
		{
			storeList.SelectedIndex = value;
			OnStoreSelectorChanged(
				new StoreSelectorCommandEventArgs
				(data.Tables["stores"].Rows[value].ItemArray[0].ToString(),
				data.Tables["stores"].Rows[value].ItemArray[1].ToString(),	
				data.Tables["stores"].Rows[value].ItemArray[2].ToString(),
				data.Tables["stores"].Rows[value].ItemArray[3].ToString(),
				data.Tables["stores"].Rows[value].ItemArray[4].ToString(),
				data.Tables["stores"].Rows[value].ItemArray[5].ToString()));
		}
	}
}
#End Code

And then in the Default.aspx Page_Load event:

#code
private void Page_Load(object sender, System.EventArgs e)
{
	// Put user code to initialize the page here
	if(!Page.IsPostBack)
	{
		StoreSelector1.SelectedIndex = 0;
	}
}
#end code

Now, when you fire up the project, it will automatically populate everything to the first record!

Hopefully this has been somewhat informative. The sky is the limit with this type of Web development. Each Web control we create, if designed properly, can easily be reused throughout the entire Web application.

About the Author

Seth Juarez has been working with Holman's of Nevada as a consultant for a little over two years. Seth's expertise lies primarily with business process modeling for database driven applications. He enjoys using existing technology to solve specific business problems and has been involved in several projects that have saved his clients time and money. He currently uses C# as his primary language but understands JAVA, Visual Basic, C, and C++. He also has understands SQL Server, mySQL, and has written software that interacts with Oracle. Seth has also developed several applications for the Web by using ASP and has now begun using ASP.NET. Seth can be reached at sjuarez@holmansnv.com



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds