This time I'll start from the top, with the using statements and namespace and class declaration to be sure you don't miss anything: using System;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Guestbook.Engine;
namespace Guestbook
{
/// <summary>
/// This WebControl displays the form that allows users to sign the guestbook.
/// </summary>
public class GuestbookForm : WebControl, INamingContainer
{
The important thing here is the derive directive and the interface implementation –
the class derives from System.Web.UI.WebControls.WebControl
and implements the interface System.Web.UI.INamingContainer
. The WebControl
class contains several methods that we need in order for our control to work, and the implementation of INamingContainer
interface is needed because we need to have unique IDs on the controls within GuestbookForm
(due to the post back nature of the control).
Next thing on the todo list is to add a declaration block for all of the controls we're going to use, so let's do that:
TextBox txtName = new TextBox();
TextBox txtEmail = new TextBox();
TextBox txtMessage = new TextBox();
Label lblName = new Label();
Label lblEmail = new Label();
Label lblMessage = new Label();
Label lblRating = new Label();
DropDownList listRating = new DropDownList();
Button btnSubmit = new Button();
RequiredFieldValidator validMessage = new RequiredFieldValidator();
RegularExpressionValidator validEmail = new RegularExpressionValidator();
Table table = new Table();
The controls needs to be declared at class-level, because there are several methods that'll use them.
Now we're going to add a bunch of properties to our custom control, that way a page developer can control the custom control without needed to change the code. Let's craify this further by taking a look at the Label control: <asp:label id=”myLabel” ... runat=”server” />
ID here is an example of what a property is and how it is used. Hope this made sense…
Well let's add the properties then: public string NameLabel
{
get { return lblName.Text; }
set { lblName.Text = value; }
}
public string EmailLabel
{
get { return lblEmail.Text; }
set { lblEmail.Text = value; }
}
public string MessageLabel
{
get { return lblMessage.Text; }
set { lblMessage.Text = value; }
}
public string RatingLabel
{
get { return lblRating.Text; }
set { lblRating.Text = value; }
}
public string ButtonLabel
{
get { return btnSubmit.Text; }
set { btnSubmit.Text = value; }
}
public string MessageValidatorErrorMessage
{
get { return validMessage.ErrorMessage; }
set { validMessage.ErrorMessage = value; }
}
public string EmailValidatorErrorMessage
{
get { return validEmail.ErrorMessage; }
set { validEmail.ErrorMessage = value; }
}
public string EmailValidatorExpression
{
get { return validEmail.ValidationExpression; }
set { validEmail.ValidationExpression = value; }
}
public IEnumerable RatingListDataSource
{
get { return (IEnumerable) listRating.DataSource; }
set { listRating.DataSource = value; }
}
Properties are defined in this manner, by using get-set blocks, although both get and set is not necessary. The type of property must match the returned type in the get block – this is very important and the returned valued must then in many cases be converted or type-casted to the appropriate type, as seen in the last property, RatingListDataSource
.
Now it's time to take a look at our control's methods, and we'll start off by the smallest one, and that is the method OnInit
which we'll override: protected override void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeControls();
}
Ok, as you can see nothing exciting here besides the call to the method InitializeControls
. Well let's take a look at that method then:
private void InitializeControls()
{
// Set up the validator controls
txtEmail.ID = "Email";
txtMessage.ID = "Message";
validEmail.ControlToValidate = txtEmail.ID;
validMessage.ControlToValidate = txtMessage.ID;
// Tweak the message textbox
txtMessage.TextMode = TextBoxMode.MultiLine;
txtMessage.Rows = 6;
// Subcribe to the submit button's Click event
btnSubmit.Click += new EventHandler(btnSubmit_Click);
// Assign default values to the controls that are blank
if (lblName.Text == string.Empty)
lblName.Text = "Your Name: ";
if (lblEmail.Text == string.Empty)
lblEmail.Text = "Your E-mail address: ";
if (lblMessage.Text == string.Empty)
lblMessage.Text = "Your Message: ";
if (lblRating.Text == string.Empty)
lblRating.Text = "Your Rating: ";
if (btnSubmit.Text == string.Empty)
btnSubmit.Text = "Submit";
if (listRating.DataSource == null)
listRating.DataSource = new string[] { "Excellent", "Good", "OK", "Bad", "Worst ever!" };
if (validMessage.ErrorMessage == string.Empty)
validMessage.ErrorMessage = "Must be filled out!";
if (validEmail.ErrorMessage == string.Empty)
validEmail.ErrorMessage = "Incorrect e-mail address format!";
if (validEmail.ValidationExpression == string.Empty)
validEmail.ValidationExpression = @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
// DataBind the rating dropdownlist
listRating.DataBind();
}
All this code is pretty trivial stuff, and detailed explanation shouldn't be needed, just make note of the properties' default values.
You may have also noticed that we declare a subscription to the button control's Click
event here. Let us not delay, so we will take a look at that method now: private void btnSubmit_Click(object sender, EventArgs e)
{
// Try to insert an entry
if (XmlEngine.InsertGuestbookEntry( txtName.Text , txtEmail.Text , txtMessage.Text , listRating.SelectedItem.Text ))
Page.Response.Redirect("Default.aspx"); // If the entry was successfully inserted, reload the page
}
Pretty much all we do here is to try and insert the entry into XML file by using the method InsertGuestbookEntry
in the XmlEngine
class, and if this is successful we reload the page. (Note: this method redirects to Default.aspx, change this to whatever page you're going to have your GuestbookRepeater control).
We're almost done with this class, just two more methods.
First one up is another overridden method called CreateChildControls
. CreateChildControls
method is the method where a control's child controls are created, as the name implies. We override it so we can add the controls the way we want to. Basically all we do is to add our controls to other controls' ControlCollection
, in a hierarchical manner. Enough talk, let's take a look at it: protected override void CreateChildControls()
{
// Create the table
TableRow tr = new TableRow();
TableCell td = new TableCell();
// "Name" row
td.Controls.Add(lblName);
tr.Controls.Add(td);
td = new TableCell();
td.Controls.Add(txtName);
tr.Controls.Add(td);
td = new TableCell();
tr.Controls.Add(td);
table.Controls.Add(tr);
// "E-mail" row
tr = new TableRow();
td = new TableCell();
td.Controls.Add(lblEmail);
tr.Controls.Add(td);
td = new TableCell();
td.Controls.Add(txtEmail);
tr.Controls.Add(td);
td = new TableCell();
td.Controls.Add(validEmail);
tr.Controls.Add(td);
table.Controls.Add(tr);
// "Message" row
tr = new TableRow();
td = new TableCell();
td.VerticalAlign = VerticalAlign.Top;
td.Controls.Add(lblMessage);
tr.Controls.Add(td);
td = new TableCell();
td.Controls.Add(txtMessage);
tr.Controls.Add(td);
td = new TableCell();
td.VerticalAlign = VerticalAlign.Top;
td.Controls.Add(validMessage);
tr.Controls.Add(td);
table.Controls.Add(tr);
// "Rating" row
tr = new TableRow();
td = new TableCell();
td.Controls.Add(lblRating);
tr.Controls.Add(td);
td = new TableCell();
td.Controls.Add(listRating);
tr.Controls.Add(td);
td = new TableCell();
tr.Controls.Add(td);
table.Controls.Add(tr);
// Button row
tr = new TableRow();
td = new TableCell();
td.ColumnSpan = 3;
td.Controls.Add(btnSubmit);
tr.Controls.Add(td);
table.Controls.Add(tr);
// Add the table to the control's ControlCollection
Controls.Add(table);
}
The output of this is a table structure with cells and rows.
There's only one method left in this class now, so let's get right to it. Once again this method is an overridden method. This time it's called Render
and handles the rendering of controls into HTML: protected override void Render(HtmlTextWriter writer)
{
// Renders the control's attributes
AddAttributesToRender(writer);
// Render the table
table.RenderControl(writer);
}
This method is strictly speaking not needed in this context, but due to the complexity of our control hierarchy the entire GuestbookForm
control will be encapsulated by tags. This isn't a problem, but I prefer to prevent it, and by overriding the Render method we do exactly that.
The method AddAttributesToRender
makes sure all of the controls are rendered with their attributes.
Ok that's the end of this class, but we have one more remaining. Although it isn't even close to as big as this one. So you'll soon be able to rest your eyes :)
Comments