Advertisement

ASP.NET for PHP Developers: Part 2

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

In part one of the "ASP.NET for PHP Developers" tutorial, we learned the basics of ASP.NET and the C# language. Part two builds on that foundation, and introduces some more advanced features and techniques to take your ASP.NET pages to the next level.


Before you Start...

Ensure you have read and completed the examples in part 1 of the tutorial. We'll be building on that application here. It's also worth stressing that you need a good grasp of object oriented programming (OOP) to continue.


And Before I Start...

I mentioned in part 1 of the tutorial that there are two flavours of ASP.NET available:

  • ASP.NET WebForms: the original framework allowing developers to create web applications using many of the same techniques used in .NET Windows desktop applications
  • ASP.NET MVC: a newer framework offering Model-View-Controller architecture and more control over client-side code

However I don't use either of those, but rather a third approach of my own devising. That's for several reasons:

  1. When I started writing serious ASP.NET applications I retained my high standards of HTML output learned when writing PHP. If the page didn't validate I felt dirty. There are a lot of developers who feel the same. ASP.NET WebForms gave, at the time, awful markup for many of the standard controls, so I had to come up with another solution. Adding runat="server" to HTML elements offered many of the advantages of true ASP.NET controls, but gave me full control over the HTML that was outputted. Things improved, and are looking even better for ASP.NET 4.0.
  2. I saw lots of examples of ASP.NET code where it was obvious the developer didn't care about the resulting HTML. Perhaps they were Windows desktop application developers making the jump to the web, maybe they had never hand-coded HTML. Whatever the reason, I determined I would not be one of them.
  3. Quite a few of the standard ASP.NET controls relied entirely on JavaScript which is, to be frank, unforgivable for public websites (in the UK web accessibility is a legal requirement). For example, the evil javascript:__doPostBack function is a perfect way to make your website impossible to use for a large proportion of the web audience - oh, and search engines as well.
  4. I wanted to use my own choice of JavaScript library (initially Prototype, but then jQuery, now officially supported by ASP.NET). If I had to use the ASP.NET framework JavaScript library it would have made that more difficult.
  5. So why not ASP.NET MVC? Well, it wasn't around when I started writing ASP.NET applications, and even if it was it would have been another hurdle to jump to get anything to work. Learning the .Net framework and C# language was challenging enough!

So you can see why I chose this "roll-your-own" approach. As ASP.NET matured and I discovered new features, I started to integrate those into my applications, and I fully expect that over time I'll be doing more of that.

So, let's take our ASP.NET application to the next level.


Master Pages

My second favourite feature of ASP.NET (after turning HTML controls into server controls) is master pages. A master page is a template file you can use to encapsulate HTML you use in multiple pages. For example, your master page could contain the header, menu and footer of your pages, while your normal .aspx pages contain the actual content on that page. let's look at an example web page:

A sample web page

You can see the parts which are used on multiple pages highlighted in green. The content which changes for each page in the site is highlighted in red. Master pages allow us to split up the code for these two sections into multiple files. If you've used templates in your PHP applications (for example Wordpress has header.php, footer.php and sidebar.php) you'll see how great master pages are.

Creating a master page

So let's create a master page. In the Solution view create a new directory in your ASP.NET application called "Master_Pages". In that directory create a new master page by right-clicking on the Master_Pages folder, selecting "Add > New file" then selecting "Master Page with Code Behind" and call it "DefaultMaster". Your new master page will be created and you'll see the "DefaultMaster.master", "DefaultMaster.master.cs" and "DefaultMaster.master.designer.cs" files in the Master_Pages folder.

A new master page in MonoDevelop

Open the "DefaultMaster.master" and "DefaultMaster.master.cs" files. The code-behind file (.cs) for the master page (.master) works exactly the same as the code-behind file for an .aspx page. The first thing to note is master pages do not inherit from System.Web.UI.Page like .aspx pages do. Instead they inherit from System.Web.UI.MasterPage. Here's the default code for the code-behind.

using System;
using System.Web;
using System.Web.UI;
namespace WebApplication1
{
	public partial class DefaultMaster : System.Web.UI.MasterPage
	{
	}
}

And for the .master file itself:

<%@ Master Language="C#" Inherits="WebApplication1.DefaultMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
	<title>DefaultMaster</title>
</head>
<body>
<div id="container">
<form runat="server">
    <asp:contentplaceholder id="contentPlaceHolder" runat="server" /> 
</form>
</div>
</body>
</html>

Because we're not using the WebForms model, let's quickly remove the tags for the <form runat="server"> element.

You should be getting used to page declarations (the <%@ Page ... %> bit in .aspx pages) by now, so the <%@ Master ... %> declaration will come as no surprise. What is different in this code is a new control: <asp:contentplaceholder>.

<asp:contentplaceholder id="contentPlaceHolder" runat="server" />

This content placeholder is where the content from your .aspx pages will be inserted. You can have as many of these in a .master page as you like.

Referencing your master page

Let's go back to our normal .aspx page and make some edits. The first thing to do is remove the <html>, <head> and <body> tags, as they will now be in the master page. That leaves:

<%@ Page Language="C#" Inherits="WebApplication1.Default" %>

<h1 id="headertext" runat="server">This is the text</h1>

Now we need to specify what content to place in the content placeholder. We do that by specifying where the master page is, and wrapping our content in an asp:Content control, like this:

<%@ Page Language="C#" MasterPageFile="~/Master_Pages/DefaultMaster.master" Inherits="WebApplication1.Default" %>
<asp:Content id="Content1" ContentPlaceHolderID="contentPlaceHolder" runat="server">
<h1 id="headertext" runat="server">This is the text</h1>
</asp:Content>

There's a couple of things to note here. Firstly the Page declaration has an additional attribute of "MasterPageFile" with a value of "~/Master_Pages/DefaultMaster.master". In ASP.NET "~" means the root of the application, the rest of that path just points to our master page.

Secondly you see the new asp:Content control has an attribute of "ContentPlaceHolderID" with a value of "contentPlaceHolder", which is the "id" attribute of our <asp:contentplaceholder>. Running the application will give you:

An ASP.NET master page and content page working together

Checking the source code of the page proves that the master page (.master) and content page (.aspx) have been seamlessly integrated together. Now you see why I love master pages so much.

A more complex master page

We can push master pages a lot further than this simple example. Let's have a go at building something that looks more like a real web application, starting with the master page. Firstly we'll add some more content placeholders and a few server-side controls:

<%@ Master Language="C#" Inherits="WebApplication1.DefaultMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
	<title><asp:contentplaceholder id="PageTitle" runat="server" /></title>
	
	<script src="scripts/jquery.min.js"></script>
	<asp:contentplaceholder id="PageJS" runat="server" />
	
	<link rel="stylesheet" href="styles/default.css"></link>
	<asp:contentplaceholder id="PageCSS" runat="server" />
</head>
<body>
<div id="container">

	<h1 id="sitename" runat="server"></h1>

	<ul id="menu">
		<li><a href="about.aspx">About me</a></li>
		<li><a href="services.aspx">My services</a></li>
		<li><a href="contact.aspx">Contact me</a></li>
	</ul>

	<div id="content">
		<asp:contentplaceholder id="PageContent" runat="server" /> 
	</div>

	<div id="footer">
		<p id="copyright" runat="server"></p>
	</div>

</div>
</body>
</html>

And in the code-behind file for our master page we'll put:

using System;
using System.Web;
using System.Web.UI;
using System.Configuration;

namespace WebApplication1
{
	public partial class DefaultMaster : System.Web.UI.MasterPage
	{
		protected void Page_Load(object sender, EventArgs e)
		{
			sitename.InnerHtml = ConfigurationSettings.AppSettings["SiteName"];
			copyright.InnerHtml = ConfigurationSettings.AppSettings["CopyrightNotice"] + DateTime.Now.Year.ToString();
		}
	}
}

(I'll leave it as an exercise for you to add the SiteName and CopyrightNotice applications settings to web.config.)

Now for our content page. We have four content placeholders we can use: PageTitle, PageJS, PageCSS and PageContent. Here's the code for the .aspx content page:

<%@ Page Language="C#" MasterPageFile="~/Master_Pages/DefaultMaster.master" Inherits="WebApplication1.Default" %>

<asp:Content id="PageTitle" ContentPlaceHolderID="PageTitle" runat="server">
	<asp:Literal id="Title" runat="server"></asp:Literal>
</asp:Content>

<asp:Content id="PageCSS" ContentPlaceHolderID="PageCSS" runat="server">
<style type="text/css">
h1 {
	font-family: sans-serif;
	color: #090;
}
</style>
</asp:Content>

<asp:Content id="PageContent" ContentPlaceHolderID="PageContent" runat="server">

	<h2>Welcome, one and all</h2>
	<p>This is my very first ASP.NET website, working with a master page!</p>

</asp:Content>

And the code-behind for our .aspx content page:

using System;
using System.Web;
using System.Web.UI;
using System.Configuration;

namespace WebApplication1
{
	public partial class Default : System.Web.UI.Page
	{
		protected void Page_Load(object sender, EventArgs e)
		{
			Title.Text = "Welcome to my first ASP.NET Website";
		}
	}
}

A couple of new things to notice here. Firstly I haven't used the PageJS content placeholder at all - it's quite OK to leave it out entirely (of course nothing will be rendered to the page for that area). Secondly I've introduced another ASP.NET control, namely <asp:Literal>, which we'll take a quick look at now.

The Literal control

The Literal control is very useful when you want to render something to the page without any extra markup. For example, a lot of the time it's fine to use:

<span id="message" runat="server"></span>

message.InnerHtml = "This is the message"

Gives:

<span id="message">This is the message</span>

But if you don't want the span tags at all, for example for the page <title>, you need the Literal control. Setting the "Text" property of the Literal control renders just that text to the page:

<asp:Literal id="message" runat="server"></asp:Literal>

message.Text = "This is the message";

Gives:

This is the message

The completed master and content page

So running our application should give us this:

An ASP.NET master page and content page with multiple content placeholders

This is really just scratching the surface, as it's possible to have multiple master pages (even nested master pages!). You can also set the master page programatically (but this needs to be done in the Page_Init event, as Page_Load is too late in the page lifecycle). There's lots more detail about MasterPages on the MSDN site.


Custom Classes

It's possible to create custom classes in your application, just like you would in PHP. Let's create a security class by right-clicking the root of your application and selecting "Add > New file" then choosing "Empty class" from the "General" section and calling it "Security".

An new empty class

The code for your new class looks like this:

using System;
namespace WebApplication1
{
	public class Security
	{
		public Security()
		{
		}
	}
}

I'll throw a bit more code into this file:

using System;
using System.Web;
namespace WebApplication1
{
	public class Security
	{
		public bool IsLoggedIn;
		public Security()
		{
			CheckSession();
		}
		private void CheckSession()
		{
			if (HttpContext.Current.Session["loggedin"] != null && HttpContext.Current.Session["loggedin"] == "true")
			{
				IsLoggedIn = true;
			}
			else
			{
				IsLoggedIn = false;
			}
		}
	}
}

Pretty simple so far. The only new thing is the use of HttpContext.Current.Session rather than just Session, that's because HttpContext.Current is implicit in an .aspx web page, but not in a standalone class.

In our Default.aspx.cs code-behind file we write:

protected void Page_Load(object sender, EventArgs e)
{		
	Security security = new Security();
	if (security.IsLoggedIn)
	{
		Title.Text = "Welcome back, you are logged in";
	}
	else
	{
		Title.Text = "You are not logged in";
	}
}

Which instantiates a new instance of the Security class names "security". Running the application shows this:

Not logged in

As you're familiar with OOP you can see how this can be used to build large-scale web applications. The only other thing to say about custom classes is how to make them static. Here's the code for a static class:

using System;
using System.Web;
namespace WebApplication1
{
	public static class Security
	{
		public static bool IsLoggedIn;
		public static void CheckSession()
		{
			if (HttpContext.Current.Session["loggedin"] != null && HttpContext.Current.Session["loggedin"] == "true")
			{
				IsLoggedIn = true;
			}
			else
			{
				IsLoggedIn = false;
			}
		}
	}
}

You can see there's no default method, as this class is never instantiated. I've also added the "static" keyword to the property and method, and I've made the CheckSession() method public. To use this static class we would write:

protected void Page_Load(object sender, EventArgs e)
{		
	Security.CheckSession();
	if (Security.IsLoggedIn)
	{
		Title.Text = "Welcome back, you are logged in";
	}
	else
	{
		Title.Text = "You are not logged in";
	}
}

Pretty simple, really. As you're fully aware of the advantages that OOP can give you for abstraction, encapsulation and inheritance you'll see how powerful this is. But if we're going to use objects, we really need some serious data to model in our objects. We need a database.


Databases, Data Sources and Data Binding

ASP.NET works really well with databases, but works the best with Microsoft SQL Server (not surprisingly). Even if your ASP.NET application is running on a Linux box, you can still connect to SQL Server on a Windows server to use as a datastore. I'll demonstrate that below, but as I'm writing this tutorial in Linux I will also demonstrate the use of MySQL as an ASP.NET database. To use MySQL you'll need the ADO.NET driver for MySQL - this excellent article helped me a lot.

Database configuration

The first thing to do is configure how to connect to our database server. You can do this in web.config, add this code inside the "configuration" section (the MySQL and SQL Server code should be pretty obvious). Note that these are standard connection strings.

<connectionStrings>
    <add name="MySQL" connectionString="Server=mysqlserver;Database=aspnetdb1;User ID=root;Password=mypassword;Pooling=false"/>
    <add name="SQLServer" connectionString="Server=sqlserver;Database=aspnetdb1;User ID=sa;Password=myPassword;"/>
</connectionStrings>

I've also created a table called "users" with this code (this is for MySQL, minor edits will make it work in most other database systems):

CREATE TABLE users (
  id int  NOT NULL AUTO_INCREMENT,
  username varchar(50)  NOT NULL,
  password varchar(32)  NOT NULL,
  email varchar(255)  NOT NULL,
  PRIMARY KEY (id)
);

To access your connection string you can use the ConfigurationManager class which we used in part 1 of the tutorial to access global configuration settings. Here's the code:

string conn = ConfigurationManager.ConnectionStrings["MySQL"].ConnectionString;

Connecting and running a simple query

So we're now ready to connect to our database and run a query. First, insert a couple of rows into the " users" table so we have something to query:

insert into users
(username, password, email)
values
('User 1', 'user1password', 'user1@asp.net')

We then need to ensure we reference the right assemblies. For MySQL make sure you have this at the top of your code-behind file:

using System.Data;
using MySql.Data.MySqlClient;

Amd for SQL Server use this:

using System.Data;
using System.Data.SqlClient;

A quick note about connecting to MySQL in Linux. I had a bit of trouble making my application compile when I first tried this. I did various searches but found no answer that worked for me. The error I got was "The type or namespace name `MySqlConnection' could not be found." which looked like the MySQL Connector wasn't installed properly. The fix (for me) was to manually add the reference by right-clicking the References folder in my application and going to "Edit references". I then found the MySQL.Data.dll file in the .Net Assembly tab and referenced it. I also had to then manually reference the System.Data and System.Configuration assemblies from the Packages tab.

MonoDevelop references

Hopefully you won't need to jump through these hoops.

We now open a connection to our database like this for MySQL:

protected void Page_Load(object sender, EventArgs e)
{
	// get the connection string
	string conn = ConfigurationManager.ConnectionStrings["MySQL"].ConnectionString;
	// create a new MySQL connection
	MySqlConnection dbcon;
	using (dbcon = new MySqlConnection(conn))
	{
		// open the connection
		dbcon.Open();
		// create the query
		string query = "SELECT username, email FROM users";
		// create a new adapter between the connection and query
		MySqlDataAdapter adapter = new MySqlDataAdapter(query, dbcon);
		// create a new dataset to store the query results
		DataSet ds = new DataSet();
		// fill the dataset with the results from the adapter,
		// the name of the dataset is "result"
		adapter.Fill(ds, "result");
	
	}
}

And this for SQL Server:

protected void Page_Load(object sender, EventArgs e)
{
	// get the connection string
	string conn = ConfigurationManager.ConnectionStrings["SQLServer"].ConnectionString;
	// create a new SQL Server connection
	SqlConnection dbcon;
	using (dbcon = new SqlConnection(conn))
	{
		// open the connection
		dbcon.Open();
		// create the query
		string query = "SELECT username, email FROM users";
		// create a new adapter between the connection and query
		SqlDataAdapter adapter = new SqlDataAdapter(query, dbcon);
		// create a new dataset to store the query results
		DataSet ds = new DataSet();
		// fill the dataset with the results from the adapter,
		// the name of the dataset is "result"
		adapter.Fill(ds, "result");
	
	}
}

See, pretty easy, and not a million miles away from the equivalent PHP code. There are a couple of bits in here I'll explain in some more depth. Firstly the using statement:

using (something here) { ... }

The object you set up in the brackets is automatically destroyed when your code leaves the end curly brace "}". This is a really useful structure to know about, so read all about it here.

Secondly the DataSet. In the code above the results from the database query are fed into a DataSet, which is an object containing one or more tables, each table containing rows and columns. That means you can do useful things like:

DataSet ds = new DataSet();
// we put some data from the database in the DataSet here...
// get the number of tables
int tables = ds.Tables.Count;
// get the first table
DataTable dt = ds.Tables[0];
// get the number of rows in the first table
int rows = ds.Tables[0].Rows.Count;

And there are many other goodies in the DataSet class. You can also loop rows, just like you do in PHP, like this:

for (int x = 0; x < ds.Tables[0].Rows.Count; x++)
{
	Response.Write(ds.Tables[0].Rows[x]["fieldname"].ToString() + <br />);
}

But there's a much better way to display simple loops, and that's using the Repeater control.

Using repeaters and databinding

First a confession. There are large ASP.NET applications I've written that use no ASP.NET controls except the Literal (which we looked at above) and the Repeater. The Repeater control allows you to "bind" data, for example from a DataSet, and display it in a looped manner. Firstly we need to add something to our database code above:

protected void Page_Load(object sender, EventArgs e)
{
	// get the connection string
	string conn = ConfigurationManager.ConnectionStrings["MySQL"].ConnectionString;
	// create a new MySQL connection
	MySqlConnection dbcon;
	using (dbcon = new MySqlConnection(conn))
	{
		// open the connection
		dbcon.Open();
		// create the query
		string query = "SELECT username, email FROM users";
		// create a new adapter between the connection and query
		MySqlDataAdapter adapter = new MySqlDataAdapter(query, dbcon);
		// create a new dataset to store the query results
		DataSet ds = new DataSet();
		// fill the dataset with the results from the adapter,
		// the name of the dataset is "result"
		adapter.Fill(ds, "result");

		// below is the new code...

		// set the DataSource of the repeater
		myRepeater.DataSource = ds;
		// bind the data
		myRepeater.DataBind();
	
	}
}

And in the .aspx page we put:

<asp:Repeater id="myRepeater" runat="server">
<HeaderTemplate>
<table>
	<tr>
		<th>Username</th>
		<th>Email</th>
	</tr>
</HeaderTemplate>
<ItemTemplate>
	<tr>
		<td><%# Eval("username") %></td>
		<td><%# Eval("email") %></td>
	</tr>
</ItemTemplate>
<AlternatingItemTemplate>
	<tr class="alt">
		<td><%# Eval("username") %></td>
		<td><%# Eval("email") %></td>
	</tr>
</AlternatingItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>

You can see what happens here. When the data is bound to the Repeater control the HeaderTemplate section is displayed. Then each row is displayed in the ItemTemplate and AlternatingItemTemplate sections (the names should give you a clue how they are displayed). Then finally the FooterTemplate section is displayed. Using this simple control gives you an easy way to display repeating data, with complete control over the resulting HTML - just like you would do in PHP. Here's the results (with some CSS for styling):

A simple example of a Repeater control

As a Repeater will throw an Exception if an empty DataSet is bound to it, you need to check there is data to be bound first. A simple if statement will work, checking if there are tables in the DataSet and if the first table has rows:

if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
{
	myRepeater.DataSource = ds;
	myRepeater.DataBind();
}

I think you'll agree that having a control which sets templating for repeating data as easily as that is a massive help to the developer. One thing to note with the Repeater control - if you bind a DataSet to it by default the first table is used. If you're using stored procedures instead of inline SQL to run commands against your database you can return multiple tables, meaning you can load several sets of data for use in a page at once. In that case you'd use code like this (to bind the second table in the DataSet to the Repeater):

myRepeater.DataSource = ds.Tables[1];
myRepeater.DataBind();

Creating a data access class

Let's pull the last couple of sections together and create a data access class that will simplify connecting to and running commands on your database. This code is for MySQL, but as you've seen the code for SQL Server is very similar. Create a new empty class called "DB" and paste this into the new file:

using System;
using System.Configuration;
using System.Data;
using MySql.Data.MySqlClient;

namespace WebApplication1
{
	public class DB
	{
		private string ConnectionString;
		public DB()
		{
			// get the connection string
			this.ConnectionString = ConfigurationManager.ConnectionStrings["MySQL"].ConnectionString;
		}
		public DataSet Select(string query)
		{
			MySqlConnection dbcon;
			using (dbcon = new MySqlConnection(this.ConnectionString))
			{
				// open the connection
				dbcon.Open();
				// create a new adapter between the connection and query
				MySqlDataAdapter adapter = new MySqlDataAdapter(query, dbcon);
				// create a new dataset to store the query results
				DataSet ds = new DataSet();
				// fill the dataset with the results from the adapter,
				adapter.Fill(ds, "result");
				// return the dataset
				return ds;
			}	
		}
		public bool Execute(string query)
		{
			MySqlConnection dbcon;
			using (dbcon = new MySqlConnection(this.ConnectionString))
			{
				// create a new SQL command on thie connection
				MySqlCommand command = new MySqlCommand(query, dbcon);
				// open the connection
				dbcon.Open();
				// execute the query and return the number of affected rows
				int affectedrows = command.ExecuteNonQuery();
				// there were no rows affected - the command failed
				if (affectedrows == 0)
				{
					return false;
				// the command affected at least one row
				} else {
					return true;	
				}
			}	
		}
	}
}

To use this in your code-behind file you'd put:

DB db = new DB();
DataSet ds = db.Select("SELECT username, email FROM users");
if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
{
	myRepeater.DataSource = ds;
	myRepeater.DataBind();
}

This data access class introduces you to a new style of database connection syntax using the MySqlCommand class (SqlCommand for SQL Server) and the ExecuteNonQuery method. As the code says, the ExecuteNonQuery method executes a query and returns the number of affected rows. Very useful for INSERT, UPDATE and DELETE commands.

Those of you with a good knowledge of Wordpress will see how this class is similar to the $wpdb global class in WP which offers methods like $wpdb->get_results("select * from table"); and $wpdb->query("delete * from table");. It would be easy for you to extend this data access class to have more useful properties and methods for your applications.


User Controls

So far we've used just two ASP.NET controls - Literal and Repeater - in honour of our aim to keep full control of the output HTML. But sometimes it's useful to encapsulate functionality for your own controls. ASP.NET allows you to create user controls with properties and methods all your own. These user controls can be thought of as discrete blocks of HTML that can be used inside a .aspx page, just like you'd include a separate file in a .php file.

We're going to create a very simple control that displays a truncated link. Firstly add a new file of type "User control with code-behind file" and call it "ShortLink".

Adding a new user control

You may notice the new file has an extension of .ascx, this is the extension for user controls. Open the .ascx file and you'll see this:

<%@ Control Language="C#" Inherits="WebApplication1.ShortLink" %>

Open the code-behind file (MyControl.ascx.cs) and you'll see this:

using System;
using System.Web;
using System.Web.UI;

namespace WebApplication1
{
	public partial class MyControl : System.Web.UI.UserControl
	{
	}
}

Now we're ready to create our user control. Paste this code into the .ascx.cs (code-behind) file (I won't explain this code, it's simple enough):

using System;
using System.Web;
using System.Web.UI;
namespace WebApplication1
{
	public partial class ShortLink : System.Web.UI.UserControl
	{
		public string Link;
		protected void Page_Load(object sender, EventArgs e)
		{
			// set the href attribute
			theLink.Attributes["href"] = Link;
			
			// declare the short link, replacing protocols
			string shortlink = Link.Replace("http://", "").Replace("https://", "");
			
			// if the link is longer than 15 characters
			if (shortlink.Length > 15)
			{
				// show the first 6 and last 6 characters
				theLink.InnerHtml = shortlink.Substring(0, 6) + "..." + shortlink.Substring(shortlink.Length-6);
			}
			else
			{
				// show the full link
				theLink.InnerHtml = shortlink;
			}
			
		}
	}
}

Yes, user controls use the same Page_Load event handler that normal .aspx pages use. Now open the .ascx file and paste this into it:

<%@ Control Language="C#" Inherits="WebApplication1.ShortLink" %>
<a href="" id="theLink" runat="server"></a>

Here you can see instead of a Page declaration we have a Control declaration, but the same Inherits property to bind it to the code-behind file. We also have a standard <a> element with the runat="server" property to make it a server-side control.

To use this control in your page simply register a tag prefix (this can be anything) at the top of the page like this:

<%@ Page Language="C#" MasterPageFile="~/Master_Pages/DefaultMaster.master" Inherits="WebApplication1.Default" %>
<%@ Register TagPrefix="My" TagName="ShortLink" Src="ShortLink.ascx" %>

Then use the control wherever you want to. To demonstrate this control I'm using two instances of it:

<p><My:ShortLink id="Link1" Link="http://www.google.com" Runat="server"></My:ShortLink></p>
<p><My:ShortLink id="Link2" Link="http://www.google.com/search?q=asp.net+for+php+developers" Runat="server"></My:ShortLink></p>

The TagPrefix property is the first part of the control tag and the TagName the second part, separated by ":" - My:ShortLink. And this is the result:

A simple user control

Here you can see that the public string property I declared in my ShortCode user control class (public string Link;) can be set in a Link property of the control. You can have any number of properties and they can be of any type. You can only set string types in the control tag itself (i.e Link="http://www.google.com"), as you can set the properties programatically from your code-behind file (like Link1.DatasetProperty = new DataSet();).

There's one bit of code here which needs a little more explanation.

Using a custom tag prefix

Your user controls need to have their own tag prefix. In our example above this is "My", but of course it can be any simple string. In the example above the tag prefix was registered, so ASP.NET knew what to do when it encountered it, using a declaration at the top of the page:

<%@ Register TagPrefix="My" TagName="ShortLink" Src="ShortLink.ascx" %>

However it's possible to register your tag prefixes in your web.config file, so you don't have to do it on every page (as explained by Scott Guthrie - that's one blog you'll want to follow). Here's how, but before you rush in watch out for the error I got:

Parser Error Message: The page '/Default.aspx' cannot use the user control '/ShortLink.ascx', because it is registered in web.config and lives in the same directory as the page.

So put your user controls in a subfolder, for example "Controls".

<?xml version="1.0"?>
<configuration>
  <system.web>
    <pages>
      <controls>
        <add tagPrefix="My" src="~/Controls/ShortLink.ascx" tagName="ShortLink"/>
      </controls>
    </pages>
  </system.web>
</configuration>

You'll now want to put user controls everywhere. And the best thing about user controls is, because they are just like pages (without <html>, <head> and <body> tags) you can put anything you like in them. In fact it would be possible to write an entire application in user controls, including the relevent controls in your page depending on some parameters passed to it. Amazing.


Compiling and Deploying

As mentioned in part 1 of the tutorial, C# is a compiled language. Rather than PHP, which is compiled into language the computer can understand at runtime, C# is pre-compiled (or sometimes compiled on first run) and saved in assemblies for the computer to process.

This means that C# is faster (yes, it's true, sorry), and that you can catch a lot of errors in your code *before* you try to run it. You've already seen that happening when we discussed errors above. However, it means you can't just drop your ASP.NET application on a server and expect it to run. It also means you can't do live hacking of your code-behind files in a running application. Deployment needs to be approached a little more methodically than in PHP.

There are several other articles which do a much better job at explaining this than I would so I'll just link to them.


Next Steps

Hopefully between part 1 and this tutorial, you now have a much better idea of what ASP.NET is, and the advantages it can provide for developers. For further reading, you can check out some of my favourite places:

Finally, good luck! It's been a hard climb for me, as a PHP guy for many years, to get to grips with ASP.NET. However I've found many good things in the framework, and have come to appreciate the power of the C# language - without losing my love for PHP. I hope that you can do the same.

Advertisement