Understanding Web Controls: The Wrox United Application
The Wrox United application that we'll be building up over the next few chapters has a database with some sample data and a series of pages that query and display that data. We'll be using a variety of controls and events, and examine each of these in turn as we encounter them.Let's take a quick look at the database structure for the application. This is shown in Figure 10-6:
Figure 10-6
The key tables in this database are Players , Teams , Games , and Opponents . Players have a Status
(Active, Injured, or Retired), and can play in different positions in more than one Wrox team (they can be a forward in one team, and a goalkeeper in another). Each team has many players. Games are held between one of the Wrox teams and an opposing team. Each game can take place either at home (at the Wrox location) or away (at the opposing team's ground). Finally, games can be either friendly or league games.
For a more thorough investigation of the Wrox United database and its data structures, and creating it, please refer to Appendix C – The Wrox United database. The Access and MSDE versions of this database are available for download from the Wrox Web site.So, let's have a go at displaying some of this data using some server controls.
Important | To run this code, you need to download the Wrox United database. Download the version of the database that you prefer (the samples in the book assume you are using the Access version) before starting this exercise. For more information on the Wrox United database, refer to Appendix C. |
Try It Out - Wrox United Main Page – Default.aspx
The first page we'll build in our site is the Default.aspx page. This page will be the first thing that visitors will see when they visit the site. For now, don't worry too much about styling – as you work through these chapters, we'll refine this page step by step until it looks and feels like a Web site. For now, let's start by adding a heading or two and some links to other pages that we'll be building.
Open up your ASP.NET Web Matrix editor, and create a new ASP.NET page called Default.aspx in a folder that lives within your BegASPNET11 directory (C:\BegASPNET11\WroxUnited , for example), as shown in Figure 10-7. Web Matrix will then display this page in the main window, ready to be worked on:

Figure 10-7
Start off in Design view. Type in Wrox United and make this text Heading 1 style, using the block format drop-down menu on the main toolbar at the top of the environment. On a new line, change the paragraph style back to Normal and type in Welcome to the Wrox United Website! Please select one of the following:
Select the Web Controls tab on the left (if it isn't already selected), and drag four Hyperlink controls onto your form, pressing the Return key after each link to place them on separate lines. Your page screen should appear as shown in Figure 10-8:

Figure 10-8
Notice that when you select one of the Hyperlink controls, the properties available for that control appear in the Properties pane on the right hand side. Select each Hyperlink control in turn and assign the following properties using the Properties pane:
Property | Link 1 | Link 2 | Link 3 | Link 4 |
---|---|---|---|---|
Text | Teams | Players | Upcoming Games | Results |
ID | lnkTeams | lnkPlayers | lnkGames | lnkResults |
NavigateUrl | Default.aspx | Default.aspx | Default.aspx | Default.aspx |
As we continue to build pages in this site, we'll add a separate NavigateUrl property for each of these controls, but for now we keep them all pointing to the same page. If you run this page now, you will see that these controls are rendered almost exactly like an HTML<a href ...> hyperlink would be; see Figure 10-9:

Figure 10-9
How It Works
In Web Matrix, go to the HTML view and you will see that the following code has been created:
<h1>Wrox United
</h1>
<p>
Welcome to the Wrox United Website! Please select one of the following:
</p>
<p>
<asp:HyperLink id="lnkTeams" runat="server"
NavigateUrl="Default.aspx">Teams</asp:HyperLink>
</p>
<p>
<asp:HyperLink id="lnkPlayers" runat="server"
NavigateUrl="Default.aspx">Players</asp:HyperLink>
</p>
<p>
<asp:HyperLink id="lnkGames" runat="server"
NavigateUrl="Default.aspx">
Upcoming Games
</asp:HyperLink>
</p>
<p>
<asp:HyperLink id="lnkResults" runat="server"
NavigateUrl="Default.aspx">Results</asp:HyperLink>
</p>
This isn't a lot of code, but we would have had to type it all by hand, so we've saved time by using the Web Matrix editor (with the added advantage of no typos).Now, view the page in your browser, and select View | Source from the main menu. You will see the following code:
<h1>Wrox United
</h1>
<p>
Welcome to the Wrox United Website! Please select one of the following:
</p>
<p>
<a id="lnkTeams" href="/Default.aspx">Teams</a>
</p>
<p>
<a id="lnkPlayers" href="/Default.aspx">Players</a>
</p>
<p>
<a id="lnkGames" href="/Default.aspx">Upcoming Games</a>
</p>
<p>
<a id="lnkResults" href="/Default.aspx">Results</a>
</p>
The ASP.NET Hyperlink control is one of the simplest Web controls in our toolbox and acts in a very similar way to the <a href ... > HTML anchor control. As you can see, the changes between the code we've created and the rendered code are very small, but this is not always the case.When you click on each link in turn, notice that the browser window flickers as it follows the link to the page. These controls don't cause a postback to the server in the same way as a Button control does – these controls simply transfer you to a new page, without firing any server-side events. We didn't need to add an OnClick attribute like we did with the Button control.So what's so useful about the ASP.NET Hyperlink control? First, they have the same basic set of properties that every Web control has. Second, they're simple and just as easy to use as a normal anchor tag. Later on, we'll use the Visible property of the Hyperlink controls to display or hide links, depending on whether or not you're logged in as a user – a very useful trick!Let's take a quick look at several other simple controls available to you for developing ASP.NET pages.
Intrinsic Controls
These are controls that correspond directly to HTML tags and include the Button , Checkbox , DropDownList , and TextBox controls. By now you must be familiar with these controls, as they've been used throughout the book. Here is a list to remind you of the controls that fall into this group:
Control | Purpose |
---|---|
Button | General-purpose button – you typically use the Click event handler |
CheckBox | Single check box |
DropDownList | Drop-down list box, also known as a combo box, for selecting from a list of items |
Hyperlink | Similar to the HTML <a> </a> tag for displaying a hyperlink |
Image | Displays a GIF, JPG, or other image files |
Label | Provides a way to display text on a page; corresponds to the HTML <span> tag |
ListBox | Provides a scrollable list of items, single or multiple selection |
Panel | Similar to the <DIV> tag – typically serves as a container for other controls |
RadioButton | Single radio button, similar to a checkbox, except that you need to handle its de-selection programmatically |
Table | Similar to an HTML table |
TableCell | A cell within a table |
TableRow | A row within a table |
TextBox | Text box – single or multiple lines – similar to an <input ... > or a <textarea ... > control |
Let's take a look at some more interesting controls. Here we'll create the Teams.aspx page, and examine a different type of control, the DataList control. This control enables you to display repetitive elements on a page based on rows of data that are stored in a database.
Try It Out - Wrox United – Teams.aspx
Create a new ASP.NET page and call it Teams.aspx . On this page, in the Design view, add a Heading 1 that displays the text Wrox United again. Underneath that, add a Heading 2 that displays the text Teams. On a new line, switch back to Normal paragraph style. Now you can add some content.
You can add a simple table to the form to lay the form out a bit more clearly. Navigate to the HTML menu and select Insert Table. Create a 1-row, 2-column table, with no specified width or height, as shown in Figure 10-10:

Figure 10-10
Drag a DataList control into the first cell of the table from the Web Controls panel on the left, as shown in Figure 10-11:

Figure 10-11
Let's do some work on the code in the page. First, switch to HTML view and enter the following code:
<asp:DataList id="TeamList" runat="server">
<ItemTemplate>
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "TeamName") %>'
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "TeamID") %>'
id="TeamNameLink" style="color:darkred" runat="server" />
<br />
<asp:Label text='<%# DataBinder.Eval(Container.DataItem, "Notes") %>'
id="teamnotes" runat="server" />
</ItemTemplate>
<SeparatorTemplate>
<br />
<hr color="#b0c4de" width="200px"/>
</SeparatorTemplate>
</asp:DataList>
We've entered some data binding expressions in here, but not yet created a data source for this control. Next, you'll need to grab the name of each of the teams from the database, and create an ID for each team. This ID will come in handy when you need to get more information about each of the teams. In addition, you'll need to grab data from the Notes field that describes each of the teams. Both of these tasks will be carried out next.
Important | If you are using Microsoft Access, ensure that a copy of the database is placed into the BegASPNET11\WroxUnited\Database folder (you need to create this folder if it doesn't already exist). |
The first step is to add some data source information to the page. We're going to use the neat data wizard feature of Web Matrix to generate some database access code. In Code view, drag a SELECT Data Method wizard onto the page from the Code Wizards panel on the left, which shows up as shown in Figure 10-12:

Figure 10-12
Create a new database connection to the WroxUnited database by selecting your preferred database type, and clicking on the Create button. If you are using Access, you will be prompted to enter the path to the database. If you are using MSDE, you will be prompted to select the WroxUnited database from your database list; see 10-14, and 10-15:

Figure 10-13

Figure 10-14
Important | In this book, the examples use the Access version of the database, but you can obviously use the SQL Server version. In fact, in Appendix D, a SQL Server connection has been used instead. |

Figure 10-15
Now that you have a connection to your database, you can go ahead with the Code Wizard.
Click Next, and the Code Wizard is launched. All you need is data from the Teams table, so select the Teams table, then select each field from that table, as shown in Figure 10-16:

Figure 10-16
Click Next, and you can test out the query. You should see a screen similar to Figure 10-17:

Figure 10-17
In the final screen, save this method as GetTeams() , ensure that the DataReader type is selected, and then click Finish, as shown in Figure 10-18:

Figure 10-18
Back in Code view, you will now see the following:
System.Data.IDataReader GetTeams()
{
string connectionString =
"Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4;" +
"Data Source=C:\\BegASPNET11\\WroxUnited\\Database\\WroxUnited.mdb";
System.Data.IDbConnection dbConnection = new
System.Data.OleDb.OleDbConnection(connectionString);
string queryString = "SELECT [Teams].[TeamID], [Teams].[TeamName], " +
"[Teams].[Notes] FROM [Teams]";
System.Data.IDbCommand dbCommand = new System.Data.OleDb.OleDbCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
dbConnection.Open();
System.Data.IDataReader dataReader =
dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
return dataReader;
}
Phew – that's a lot of code! These code builders are very useful, and we'll be using these quite often as we build up the site. We now have a function that returns a DataReader object that we can use to populate the DataList control. However, before we continue, we need to change this code a bit. The database connection string can be stored in a central location in the web.config
file. Since we'll be doing a fair amount of database work for this application, let's change the code so that we use this technique.
Create a new web.config file for your Wrox United application and in the code that is generated, add the highlighted line of code.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appSettings>
<add key="ConnectionString"
value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4;
Data Source=C:\BegASPNET11\WroxUnited\Database\WroxUnited.mdb"/>
</appSettings>
<system.web>
Modify the GetTeams() method as follows to make use of this global connection string:
System.Data.IDataReader GetTeams()
{
string connectionString =
ConfigurationSettings.AppSettings["ConnectionString"];
System.Data.IDbConnection dbConnection = new
System.Data.OleDb.OleDbConnection(connectionString);
...
Add the following code block above the GetTeams() function:
void Page_Load()
{
TeamList.DataSource = GetTeams();
TeamList.DataBind();
}
That's it for this page. Run the page; the output is as shown to Figure 10-19:

Figure 10-19
How It Works
This exercise used many different types of ASP.NET controls to display team information on the page. Let's look at each of these controls, in turn, starting with the DataList control.The DataList control is a powerful way to display repeated values from a database. It uses templates to present the data. In this example, we added content for the ItemTemplate and SeparatorTemplate elements within the DataList .The ItemTemplate section is used to display each row of data retrieved and gives you the option of adding some layout and styling code:
<asp:DataList id="TeamList" runat="server">
<ItemTemplate>
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "TeamName") %>'
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "TeamID") %>'
id="TeamNameLink" style="color:darkred" runat="server" />
<br />
<asp:Label text='<%# DataBinder.Eval(Container.DataItem, "Notes") %>'
id="teamnotes" runat="server" />
</ItemTemplate>
The SeparatorTemplate is where we added a horizontal line to clearly separate the results:
<SeparatorTemplate>
<br />
<hr color="#b0c4de" width="200px"/>
</SeparatorTemplate>
</asp:DataList>
Inside the ItemTemplate , we used two different controls – an ASP.NET LinkButton control and a Label control. Let's first look at the LinkButton control:
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "TeamName") %>'
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "TeamID") %>'
id="TeamNameLink" style="color:darkred" runat="server" />
The Text property is set to display the value stored in the TeamName field for each row in the database. The CommandArgument property stores the TeamID that represents the currently selected team. This property will be very useful later on when we use the LinkButton control to display more information on the page. Later in this chapter, we'll use it to retrieve a list of players that play for the selected team.Now, let's look at the Label control:
<asp:Label text='<%# DataBinder.Eval(Container.DataItem, "Notes") %>'
id="teamnotes" runat="server" />
This control is a little simpler than the LinkButton control. The interesting bit is the Text property that is set to the value stored in the database for the notes for the currently selected team.Each of these controls will be rendered once for each row in the database. Six teams would result in six links and six notes. Each result will have the same separator between items.The code used to access the data stored on the database should be familiar to you from working through the exercises in Chapters 8 and 9. The Web Matrix data wizard takes a lot of hard work away and produces neat functions that you can use in your code. However, the Web Matrix wizards don't allow you to specify a centralized database connection string, so we added a line to the default web.config file created for this exercise:
<add key="ConnectionString"
value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4;
Data Source=C:\BegASPNET11\WroxUnited\Database\WroxUnited.mdb"/>
Once this was added, the connectionString created by the data access wizard was changed as follows:
string connectionString =
ConfigurationSettings.AppSettings["ConnectionString"];
This line of code looks up the value stored in the central web.config file and uses that value to connect to the database.
Linking to the Teams.aspx Page
Before you finish this example completely, flip back to Default.aspx and change the following line of code as shown:
<asp:HyperLink id="lnkTeams" runat="server" NavigateUrl="Teams.aspx">
Teams
</asp:HyperLink>
Changing the NavigateUrl property means that you can link to the newly created Teams.aspx page from the main front page.The DataList control is one of the numerous data controls available to ASP.NET developers. Here's a quick look at the other controls available to us.
Data Rendering Controls
These controls are extremely feature-rich (they have numerous properties to choose from) and greatly simplify the work of displaying a variety of data, particularly database-related data. The definition of data in the context of these controls is very broad. It could include database records, an ArrayList , an XML data source, or even custom collection of objects containing custom class instances. There are two important concepts you need to know:
Data binding: This is the term used to describe the process of associating a server control with information in a data store. Binding data to a server control is a two-step process in ASP.NET – first assign the server control's DataSource property to the data you want to bind to and then call the control's DataBind() method. Controls can be bound to a variety of data sources, ranging from tables retrieved from the database to values stored in an object, such as an Array or a Hashtable.
Templates: These are used to define the various layout elements of a particular control. Templates describe how data is displayed in the browser.
The following table lists the available data controls:
Control | Purpose |
---|---|
DataGrid | Creates a multi-column, data-bound grid. This control allows you to define various types of columns, lay out the contents of the grid, and add specific functionality (edit button columns, hyperlink columns, and so on). |
DataList | Displays items from a data source by using templates and renders them as a structured table. You can customize the appearance and contents of the control by manipulating the templates that make up its different components. |
Repeater | TheRepeater control is very similar to the DataList , except that results are not rendered in a table. Each row in the data source is rendered in a format that you specify in the ItemTemplate . Given the lack of structure in the rendered output, you may find that you use the ItemSeparatorTemplate more often. Unlike DataList , the Repeater control does not have any builtin selection or editing support. |
The DataGrid Control
The DataGrid control provides a wealth of functionality for displaying data in columns and rows and has many properties that you can use to control the layout of your grid. For example, you could alternate the colors for the rows of data being displayed. Some useful properties for the DataGrid
control include:
AllowSorting : Allows you to dynamically sort and re-display the data based on the values in a selected column. For example, if you have a table in a database containing employees' surnames and salaries, enabling sorting would allow you to sort the rows in your table according to either column.
AllowPaging : Allows you to view subsets of the data called by the DataGrid control on different pages. The number of items displayed on the page is determined by the PageSize property.
AlternatingItemStyle : Sets the style (such as background color) of every other item listed.
ItemStyle : The style of individual items. If no AlternatingItemStyle is defined, all the items will render with this style.
FooterStyle : The style of the footer (if any) at the end of the list.
HeaderStyle : The style of the header (if any) at the beginning of the list.
To use the DataGrid control, declare it like the other server controls you've seen (using the <asp:DataGrid> start and end tags), set the relevant properties, define the columns in your table, and then apply the relevant template for those columns. Within the template tags, include the information to which the template must be applied:
<asp:DataGrid id="EventData"
AllowSorting="true"
<Columns>
<asp:TemplateColumn HeaderText="Column1">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "ShortDesc") %>
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Column2">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "DetailDesc") %>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
The DataList Control
The DataList control used in the previous example is useful for displaying rows of database information (which can become columns in DataGrid tables) in a format that you can control using templates and styles. Manipulating various template controls changes the way your data is presented. The DataList control enables you to select and edit the data that is presented. Here is a list of the supported templates:
Templates | Purpose |
---|---|
ItemTemplate | This is a required template that provides the content and layout for items referenced by DataList . |
AlternatingItemTemplate | If defined, this template provides the content and layout for alternating items in the DataList . If it is not defined, ItemTemplate is used. |
EditItemTemplate | If defined, this template provides editing controls, such as text boxes, for items set to 'edit ' in the DataList . If it is not defined, ItemTemplate is used. |
FooterTemplate | If defined, the FooterTemplate provides the content and layout for the footer section of the DataList . If it is not defined, the footer section will not be displayed. HeaderTemplate If defined, this provides the content and layout for the header section of the DataList . If it is not defined, the header section will not be displayed. |
SelectedItemTemplate | If defined, this template provides the content and layout for the currently selected item in the DataList . If it is not defined, ItemTemplate is used. |
SeparatorTemplate | If defined, this provides the content and layout for the separator between items in the DataList . If it is not defined, a separator will not be displayed. |
Note | You can actually make a DataList render like a DataGrid by arranging data in columns. You can do this by changing the RepeatDirection property from Vertical to Horizontal . We recommend trying this out yourself and taking a look at the source for the rendered page – you'll soon see where you need to add or remove layout data to customize the appearance of your data. |
The Repeater Control
The Repeater control is very similar to the DataList control with one very important distinction: the data displayed is always read-only. You cannot edit the data being presented. It is particularly useful for displaying repeating rows of data. Like the DataGrid and DataList controls, it utilizes templates to render its various sections. The templates it uses are generally the same as the ones used with the DataList control, with a similar syntax. The next Try-It-Out illustrates the Repeater control in action.We've completed the first part of the Teams page, but it would be really useful to see the players on each team. For that part of the example, we'll work with some more Web controls (including a Repeater control) that bind to data.
Try It Out - Wrox United – Teams.aspx, Part 2
In this example, we'll make the TeamNames from the previous example "clickable", so that when a team is selected, the players of that team are listed on the right-hand side of the table.
Reopen Teams.aspx , and go straight to Code view. We need to get some more data from the database. Start by dragging another SELECT query onto the page and launching the wizard.
This time, select PlayerName from the Players table and PositionName from the Positions
table as the two columns to be displayed, as shown in Figure 10-20:

Figure 10-20
Before you can exit the wizard, you need to add some WHERE clause data to link the data that you're gathering. This data comes from two different tables, Players and Positions . You also need to ensure that player data is only retrieved for the specific team that you're interested in.
Click the WHERE button to start adding a WHERE clause, which selects only those rows where the PlayerID column in the PlayerTeam join table matches the PlayerID column in the Players table as shown in Figure 10-21:

Figure 10-21
Repeat this process to join the following sets of tables and columns: the Position column in the PlayerTeam table to the PositionID column in the Positions table, and the TeamID column in the PlayerTeam table to the TeamID column in the Teams table.
Finally, set the TeamID column in the PlayerTeam table to be equal to the TeamID parameter, as shown in Figure 10-22. This will be passed in when the function is called:

Figure 10-22
You should now see the screen shown in Figure 10-23:

Figure 10-23
Click Next to test the query. When prompted, enter an integer that corresponds to a valid TeamID , as shown in Figure 10-24 (1 is a safe bet!):

Figure 10-24
You should now see the screen shown in Figure 10-25:

Figure 10-25
Save the query as a DataReader called GetPlayersByTeam . You can now exit the wizard. The following code should have been inserted (we've added a few line breaks in the longer strings to make it easier to read):
System.Data.IDataReader GetPlayersByTeam(int teamID)
{
string connectionString =
"Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; " +
"Data Source=C:\\BegASPNET11\\WroxUnited\\Database\\WroxUnited.mdb";
System.Data.IDbConnection dbConnection =
new System.Data.OleDb.OleDbConnection(connectionString);
string queryString =
"SELECT [Players].[PlayerName], [Positions].[PositionName] " +
"FROM [Players], [Positions], [PlayerTeam], [Teams] "+
"WHERE (([PlayerTeam].[PlayerID] = [Players].[PlayerID]) " +
"AND ([PlayerTeam].[Position] = [Positions].[PositionID]) " +
"AND ([PlayerTeam].[TeamID] = [Teams].[TeamID]) " +
"AND ([PlayerTeam].[TeamID] = @TeamID))";
System.Data.IDbCommand dbCommand = new System.Data.OleDb.OleDbCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_teamID =
new System.Data.OleDb.OleDbParameter();
dbParam_teamID.ParameterName = "@TeamID";
dbParam_teamID.Value = teamID;
dbParam_teamID.DbType = System.Data.DbType.Int32;
dbCommand.Parameters.Add(dbParam_teamID);
dbConnection.Open();
System.Data.IDataReader dataReader =
dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
return dataReader;
}
Again, the wizard has done a great job of creating complex code with only a few mouse clicks! You need to make one slight adjustment though, so that the code uses the centralized connection string added to the web.config file earlier. Change the following highlighted line of code to use this central connection string:
System.Data.IDataReader GetPlayersByTeam(int teamID)
{
string connectionString =
ConfigurationSettings.AppSettings["ConnectionString"];
Dim dbConnection As System.Data.IDbConnection = _
New System.Data.OleDb.OleDbConnection(connectionString)
Now, add some controls that can use this code. Switch back to HTML view and add another cell to the table that contains a Repeater control:
</td>
<td style="vertical-align:top">
<asp:Repeater id="PlayersList" runat="server">
<ItemTemplate>
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "PlayerName") %>'
style="color:darkred" runat="server"
width="120"/>
<asp:Label
text='<%# DataBinder.Eval(Container.DataItem, "PositionName") %>'
id="playerposition" runat="server" /><br/>
</ItemTemplate>
<headerTemplate>
Players in: <%= SelectedTeam %>
<hr color="#b0c4de" width="200px">
</headerTemplate>
<footerTemplate>
<hr color="#b0c4de" width="200px">
</footerTemplate>
</asp:Repeater>
</td>
</tr>
</table>
With this, you've got a Repeater control and a function to fill it with data. However, a few things are missing. The Repeater control should only be filled with data when a team is selected. We do this in three steps. First, you wire up an event handler so that when the name of the team is clicked, the respective team players will be displayed. Then, you pass the TeamID parameter to the GetPlayerByTeam() function. Here, you should remember the name of the team that has been selected, so that you can display it in the Repeater's header template.
Head back to the DataList control and edit the LinkButton control as follows:
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "TeamName") %>'
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "TeamID") %>'
id="TeamNameLink" style="color:darkred"
CommandName="ShowTeam" runat="server" />
Add the following to the DataList control tag:
<asp:DataList id="TeamList" runat="server"
OnItemCommand="TeamList_ItemCommand">
Add the following line of code outside all the methods in the page (this is a public variable):
string selectedTeam;
Finally, add the following code in the Code view of the page:
void TeamList_ItemCommand(object sender, DataListCommandEventArgs e)
{
if (e.CommandName == "ShowTeam")
{
LinkButton button = (LinkButton)e.CommandSource;
selectedTeam = button.Text;
PlayersList.DataSource =
GetPlayersByTeam(int.Parse((string)e.CommandArgument));
PlayersList.DataBind();
}
}
The page is now ready, so run the page in your browser. You should see the screen shown in Figure 10-26:

Figure 10-26
How It Works
We extended the first part of the example to incorporate an event handler that runs some specific code when the name of a team is clicked. This code is used to populate the Repeater control. Let's run through it bit-by-bit:
<asp:DataList id="TeamList" runat="server"
OnItemCommand="TeamList_ItemCommand">
Adding the OnItemCommand property to the DataList control ensures that when the LinkButton within the DataList is clicked, the ItemCommand event of the Repeater is raised and code in the TeamList_ItemCommand() event handler is run. However, we need to pass the CommandArgument of the LinkButton , so we assign a CommandName property to the LinkButton :
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "TeamName") %>'
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "TeamID") %>'
id="TeamNameLink" style="color:darkred"
CommandName="ShowTeam" runat="server" />
In the event handler that runs when the ItemCommand event is fired, we can use the CommandArgument property of the LinkButton :
void TeamList_ItemCommand(object sender, DataListCommandEventArgs e)
The event handler takes two parameters. e is an object of the type DataListCommandEventArgs . This means that we can access certain properties of e in our code that relate back to the control that originally fired the event.
First, we can use the CommandName property of the LinkButton that was set to ShowTeam earlier:
if (e.CommandName == "ShowTeam")
{
We test whether the argument passed into the event handler has a CommandName set to ShowTeam . If it does, we process some more code. The next two lines extract the name of the currently selected team. This is used to set the value of the public string variable selectedTeam :
LinkButton button = (LinkButton)e.CommandSource;
selectedTeam = button.Text;
Notice that we first have to create a new LinkButton object and use it to work with the properties of the button that originally fired the method. We can identify the button that the request originated from by using the CommandSource property of the EventArgs (e ) passed into the method. Once we have the newly-created button object, we can access its Text property.The next section binds data to the PlayersList Repeater control. Here, we access the CommandArgument property of the e object, which is the CommandArgument property of the LinkButton control that raised the event and is passed into the event handler. Because this argument is passed across as a string and we require an integer, we must first specify that the argument is a string and then convert it to an integer:
PlayersList.DataSource =
GetPlayersByTeam(int.Parse((string)e.CommandArgument));
PlayersList.DataBind();
}
}
Important | This event handler is quite complicated, but is a great example of how to handle events raised by child controls (the LinkButton ) via a parent control (the DataList ). This is a process known as Event Bubbling, in which the events raised by the child control bubble up to their parent control where they can be intercepted and handled. |
We've seen how the event is handled. Now let's look at the control that is populated when the event handler runs:
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "PlayerName") %>'
style="color:darkred" runat="server"
width="120"/>
<asp:Label
text='<%# DataBinder.Eval(Container.DataItem, "PositionName") %>'
id="playerposition" runat="server" /><br/>
</ItemTemplate>
This control contains a LinkButton and a Label control in its ItemTemplate . We also define a header and footer for our player list. Notice that the string value stored in the SelectedTeam variable adds the name of the team to the header of the control:
<headerTemplate>
Players in: <%= SelectedTeam %>
<hr color="#b0c4de" width="250px">
</headerTemplate>
<footerTemplate>
<hr color="#b0c4de" width="250px">
</footerTemplate>
</asp:Repeater>
The data for the items in the Repeater control comes from the GetPlayersByTeam() method:
System.Data.IDataReader GetPlayersByTeam(int teamID)
{
string connectionString =
"Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; " +
"Data Source=C:\\BegASPNET11\\WroxUnited\\Database\\WroxUnited.mdb";
System.Data.IDbConnection dbConnection =
new System.Data.OleDb.OleDbConnection(connectionString);
This method contains the following large SQL statement to retrieve the data we're interested in:
string queryString =
"SELECT [Players].[PlayerName], [Positions].[PositionName] " +
"FROM [Players], [Positions], [PlayerTeam], [Teams] "+
"WHERE (([PlayerTeam].[PlayerID] = [Players].[PlayerID]) " +
"AND ([PlayerTeam].[Position] = [Positions].[PositionID]) " +
"AND ([PlayerTeam].[TeamID] = [Teams].[TeamID]) " +
"AND ([PlayerTeam].[TeamID] = @TeamID))";
This query is followed by some code that creates a Command object, and a parameter that passes the TeamID .
System.Data.IDbCommand dbCommand = new System.Data.OleDb.OleDbCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_teamID =
new System.Data.OleDb.OleDbParameter();
dbParam_teamID.ParameterName = "@TeamID";
dbParam_teamID.Value = teamID;
dbParam_teamID.DbType = System.Data.DbType.Int32;
dbCommand.Parameters.Add(dbParam_teamID);
Finally, the data is read from the database and the DataReader object is returned by the function:
dbConnection.Open();
System.Data.IDataReader dataReader =
dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
return dataReader;
}
Our page is a bit more interesting now. Try clicking on different teams and see how the list of players changes. You could also add more code that responds to the clicking of the link button in the PlayersList control, which transfers the user to more information about the selected player, but we recommend that you this yourselves.The Repeater control has produced results similar to the DataList control, and in many cases, you might find yourself wondering which controls to use for different purposes. The end result is subtly different. The DataList control actually renders as an HTML table with cells for each item in the template. The Repeater is much less structured and simply spits back rendered versions of exactly what was put into the templates with no extra hidden tags. This is why we had to add a line break to this control to split the content onto different lines (we could also have used a separator template). The only functionality difference between the two is that the DataList can be used to edit data, whereas the Repeater is always read-only. For more information and some great examples, refer to Professional ASP.NET 1.0, Special Edition, Wrox Press, ISBN 0-7645-4396-2.
Note | Data-oriented controls have many different properties and events that we could discuss in depth here, but it's time to move on and look at other topics. I highly recommend that you play and experiment with these controls. Check out the rendered HTML in your browser using View | Source – you'll find that it's a great way to understand how the rendering process works, and how you can optimize your code to work with, not against, the ASP.NET compiler to produce the results you want. |
You've now gained some experience of using a DataList , a Repeater , and a LinkButton while putting together one of the pages in the site. As you saw at the end of the previous example, the DataList control is a data rendering control. The LinkButton control, however, is a rich control. Let's find out more about this type of control.
Rich Controls
Rich controls are compound in nature and provide extended functionality. In other words, rich controls are typically combinations of two or more simple or intrinsic controls that compose a single functional unit, for example, an AdRotator control. Another distinguishing trait of these controls is that they don't have a direct correlation to any single HTML control, even though they render to HTML when displayed in the client browser. The following table discusses various rich controls and their functions:
Control | Purpose |
---|---|
AdRotator | Displays advertisement banners on a Web form. The displayed advertisement is randomly changed each time the form is loaded or refreshed. |
Calendar | Displays a one-month calendar for viewing/selecting a date, month, or year. |
CheckBoxList | Multiple-selection checkbox group; can be dynamically generated with data binding. |
ImageButton | Provides a clickable image with (optional) access to the clicked coordinates to support image-map functionality. |
LinkButton | Hyperlink-style button that posts back to the page of origin. We've seen a couple of these in action already. |
RadioButtonList | Mutually exclusive radio button group. Can be dynamically generated with data binding. |
The nice thing about this family of rich controls is that they are just as easy to use as the other ASP.NET server controls. They may boast of more features and properties, but the basic way of defining them and interacting with them is programmatically the same as for all the other ASP.NET server controls.
The Calendar Control
The Calendar control produces some really complex HTML when rendered, but you'll find that adding this control to a Web page is as simple as adding any other Web control! This control is designed to present date information in the form of a calendar and allows a user to select a particular date. You can configure the control via the SelectionMode property to allow the user to select a range of dates. You can also completely customize how this control is presented using the many different properties that it has access to. Let's take a quick look at some of these properties:
FirstDayOfWeek=[Default|Monday|Tuesday|Wednesday|
Thursday|Friday|Saturday|Sunday]
The FirstDayOfWeek property enables you to choose the day of the week your calendar starts from. Some calendars default to Sunday as the first day of the week. For business purposes, however, it's more practical to have the week starting from Monday.
SelectionMode=[None|Day|DayWeek|DayWeekMonth]
By default, the Calendar control's SelectionMode defaults to Day . This is useful when you want your user to be able to select only a single day. However, you can select multiple days by setting the SelectionMode property to either DayWeek , which will allow you to select a single day or an entire week, or DayWeekMonth , which will allow you to select a single day, an entire week, or the entire month.
The Calendar control's SelectMonthText and SelectWeekText properties enable you to customize the HTML that is rendered at the browser – use these properties if you're really going for a customized look:
SelectMonthText="HTML text"
SelectWeekText="HTML text"
You need not define all of the properties of the ASP.NET Calendar control to display the control. In fact, the following declaration will create an efficient ASP.NET Calendar server control that looks good and displays quite well:
<asp:Calendar id="MyCalendarControl" runat="server" />
When delivered to the client browser, the result is an HTML calendar as shown in Figure 10-27 that enables you to navigate through the various days, months, and years:

Figure 10-27
Take a look at the HTML that ASP.NET produced to create this page – over 100 lines of HTML and JavaScript, while you wrote only a single line!Let's now add a calendar that will highlight the days on which matches are scheduled and then display the details of those matches when that date is selected.
Try It Out - Wrox United – Default.aspx, Part 2, the Event Calendar
Reopen Default.aspx and add the following HTML table in the HTML view to change the layout of the new and improved front page:
<p>
Welcome to the Wrox United Website! Please select one
of the following:
</p>
<table style="WIDTH: 800px">
<tr>
<td style="WIDTH: 200px">
<p>
<asp:HyperLink id="lnkTeams" runat="server"
NavigateUrl="Default.aspx">
Teams
</asp:HyperLink>
</p>
...
<p>
<asp:HyperLink id="lnkResults" runat="server"
NavigateUrl="Default.aspx">
Results
</asp:HyperLink>
</p>
</td>
<td style="VERTICAL-ALIGN: top; width: 350px;"> </td>
<td style="VERTICAL-ALIGN: top; width: 250px;"></td>
</tr>
</table>
</form>
Switch to Design view and drag a Calendar control into the third cell of the table, as shown in Figure 10-28. Name this calendar EventCalendar :

Figure 10-28
Click the lightning bolt in the Properties pane to display the available events, and double-click on the DayRender() event's textbox to be taken back to Code view, where you need to enter more code. Before filling in the DayRender() event handler, let's take a moment to consider what we're trying to achieve. We want to highlight the days of the games and store dates of the games in the Games table in the database. If we want to display more information about a particular day, we also need to store the GameID so that we can again query the database later.
The next task is to add another DataReader to this page using the wizard, and then store that data in a Hashtable (where we can store details of dates and games in key-value pairs, as we saw in Chapter 8). At the top of the page, add the following line of code:
System.Collections.Hashtable DateList;
Drag a SELECT data wizard onto the page. Select the GameID and Date fields from the Games
table, save this function with the name Dates() , and specify that it returns a DataReader
object:
System.Data.IDataReader Dates()
{
string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0; " +
"Ole DB Services=-4; Data Source=C:\\BegASPNET11\\" +
"WroxUnited\\Database\\WroxUnited.mdb";
System.Data.IDbConnection dbConnection = new
System.Data.OleDb.OleDbConnection(connectionString);
string queryString = "SELECT [Games].[Date], [Games].[GameID] FROM [Games]";
System.Data.IDbCommand dbCommand = new System.Data.OleDb.OleDbCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
dbConnection.Open();
System.Data.IDataReader dataReader =
dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
return dataReader;
}
Change the following code to use the central connection string stored in web.config :
System.Data.IDataReader Dates()
{
string connectionString =
ConfigurationSettings.AppSettings["ConnectionString"];
System.Data.IDbConnection dbConnection = new
System.Data.OleDb.OleDbConnection(connectionString);
Add a Page_Load() event handler to the page, just below the hashtable declaration:
public void Page_Load(object sender, EventArgs e)
{
DateList = new System.Collections.Hashtable();
// we need to run this each time the page is loaded, so that
// after a date is selected and the page is posted back, the
// active dates will still be highlighted.
System.Data.IDataReader DateReader = Dates();
while (DateReader.Read())
{
DateList[DateReader["Date"]] = DateReader["Date"];
}
DateReader.Close();
}
Finally, add code to the DayRender() event handler that checks if a match has been scheduled for a specific day and highlights it in such a case:
public void EventCalendar_DayRender(object sender, DayRenderEventArgs e)
{
if (DateList[e.Day.Date] != null)
{
e.Cell.Style.Add("font-weight", "bold");
e.Cell.Style.Add("font-size", "larger");
e.Cell.Style.Add("border", "3 dotted darkred");
e.Cell.Style.Add("background", "#f0f0f0");
}
else
{
e.Cell.Style.Add("font-weight", "lighter");
e.Cell.Style.Add("color", "DimGray");
}
}
Run the page. You should see the screen shown in Figure 10-29:

Figure 10-29
How It Works
The Calendar control is one of the most complex controls in the ASP.NET toolbox, and without writing too much code, we've managed to get the calendar to highlight dates that are stored in a database! Let's take a look at how we did this, and consider how this code can be improved.First, we added a Calendar control to our page by simply dragging it onto the page and then wired it up to an event handler:
<asp:Calendar id="EventCalendar" runat="server"
OnDayRender="EventCalendar_DayRender"></asp:Calendar>
The OnDayRender property ensures that when each day is rendered in the calendar, the event handler is fired. This means that we can test each date as it is rendered, and compare it with the dates stored in the database. Let's take a look at the event handler:
public void EventCalendar_DayRender(object sender, DayRenderEventArgs e)
{
if (DateList[e.Day.Date] != null)
{
This is where we check whether the date being rendered (e.day.date ) matches a date in the DateList hashtable that is declared at the top of the page.
e.Cell.Style.Add("font-weight", "bold");
e.Cell.Style.Add("font-size", "larger");
e.Cell.Style.Add("border", "3 dotted darkred");
e.Cell.Style.Add("background", "#f0f0f0");
}
If a matching date is found, some formatting is added to the cell as shown in the preceding code. We make the date slightly larger, bold, and add a border and background shading. However, if the date being rendered doesn't match, we apply different formatting as follows:
else
{
e.Cell.Style.Add("font-weight", "lighter");
e.Cell.Style.Add("color", "DimGray");
}
}
At the top of the page, as mentioned earlier, is the hashtable declaration:
System.Collections.Hashtable DateList;
Adding this to the top of the page implies that all methods in the code have access to the data stored in the hashtable for as long as the page exists.
public void Page_Load(object sender, EventArgs e)
{
DateList = new System.Collections.Hashtable();
// we need to run this each time the page is loaded, so that
// after a date is selected and the page is posted back, the
// active dates will still be highlighted.
System.Data.IDataReader DateReader = Dates();
The first part of the Page_Load() method is where we create a new DataReader using the Dates()
function that is built using the wizard. We can then loop through this data, and store key and value pairs in the Hashtable. In this case, we can store just the date information in both the key and the value parts of the Hashtable:
while (DateReader.Read())
{
DateList[DateReader["Date"]] = DateReader["Date"];
}
DateReader.Close();
}
As the comment in the code states, we need to run this code each time the page is loaded, because the values in the Hashtable do not persist between postbacks. The problem with this method is that every time the page is loaded, we have to go back to the database to get the same data over and over again, which isn't very efficient! In the next chapter, we'll look at a better way to store this data, when we introduce the Cache object.The function that retrieves the data from the database is really quite simple and looks very similar to the code we've seen previously – let's just recap the SQL that was generated to get the data we're interested in:
string queryString = "SELECT [Games].[Date], [Games].[GameID] FROM [Games]";
In the final example of this chapter, we'll extend the calendar example so that the details of the match are displayed whenever the selected date corresponds to a match day.
Try It Out - Wrox United – Displaying Fixture Details
In this example, we need to respond to a different type of event to display the details of a match. The Calendar control has many different events that can be handled, and the one we need to handle in this example is the SelectionChanged() event, which fires whenever a date is selected. The user should be able to click on a highlighted date in the calendar and view the details of any matches scheduled for that day. Clicking on a different date will cause the SelectionChanged() event to fire, which we can handle and add code to display the data for the selected day.
Let's start by adding some more controls to the page for displaying the results. Start by adding a paragraph and an ASP.NET Panel control. Set the Panel ID to pnlFixtureDetails , and its Visible property to false :
<asp:Calendar id="EventCalendar" runat="server"
OnDayRender="EventCalendar_DayRender"></asp:Calendar>
<p>
<asp:Panel id="pnlFixtureDetails" runat="server" visible="false">
</asp:Panel>
</p>
Inside the Panel control, add the following Repeater control:
<asp:Repeater id="MatchesByDateList" runat="server">
<headertemplate>
<span style="width:110px;height:25px">Date: </span>
<span style="width:135px;height:25px">
<%# EventCalendar.SelectedDate.ToShortDateString() %>
</span> <br />
</headertemplate>
<itemtemplate>
<span style="width:110px">Wrox Team</span>
<span style="width:135px">
<%# DataBinder.Eval(Container.DataItem, "TeamName") %>
</span><br />
<span style="width:110px">Opposing Team</span>
<span style="width:135px">
<%# DataBinder.Eval(Container.DataItem, "OpponentName") %>
</span><br />
<span style="width:110px">Venue</span>
<span style="width:135px">
<%# Venue((string)DataBinder.Eval(Container.DataItem, "OpponentLocation"),
(int)DataBinder.Eval(Container.DataItem, "Location")) %>
</span><br />
</itemtemplate>
<separatortemplate>
<hr color="#b0c4de" width="200" />
</separatortemplate>
</asp:Repeater>
We will bind this control to some data to display it on the page. As there could be more than one match on a certain day, we need to use a repeater control to display all the matches on that day. One last item to note at this stage is the last span in the ItemTemplate :
<span style="width:135px">
<%# Venue((string)DataBinder.Eval(Container.DataItem, "OpponentLocation"),
(int)DataBinder.Eval(Container.DataItem, "Location")) %>
</span><br />
Another piece of code needs to be added to render an appropriate value for the match venue. We'll add a venue function in just a moment, and we'll examine why we need this in the How It Works section.
While you're in the HTML view, modify the Calendar control to add an event handler for the SelectionChanged() event:
<asp:Calendar id="EventCalendar" runat="server"
OnDayRender="EventCalendar_DayRender"
OnSelectionChanged="EventCalendar_SelectionChanged">
</asp:Calendar>
Add the following event handler to the page:
public void EventCalendar_SelectionChanged(object sender, EventArgs e)
{
if (DateList[EventCalendar.SelectedDate] != null)
{
MatchesByDateList.DataSource = GamesByDate(EventCalendar.SelectedDate);
pnlFixtureDetails.Visible = true;
MatchesByDateList.DataBind();
}
else
{
pnlFixtureDetails.Visible = false;
}
}
Add the Venue() function:
public string Venue(string OpponentLocation, int MatchVenue)
{
if (MatchVenue == 1)
{
// match is at home
return "Wroxville";
}
else
{
return OpponentLocation;
}
}
Finally, we need to add a function that gets the data that we're interested in. We need to display:
The name of the team (the TeamName field from the Teams table)
The name of the opposing side (the OpponentName field from the Opponents table)
The location of the game (the Location field from the Games table)
The home location of the opposing team (the OpponentLocation from the Opponents table)
We also need to add some WHERE clauses to link together the related tables, so select the WHERE clause and add the following relationships:
OpponentID from the Opponents table is equal to the OpposingTeam field in the Games table
TeamID from the Teams table is equal to the WroxTeam field in the Games table
Date from the Games table is equal to a parameter that we input called @Date
Save the method as GamesByDate() , and have it return another DataReader object. If it all goes according to plan, you will end up with the following code:
System.Data.IDataReader GamesByDate(System.DateTime date)
{
string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB
Services=-4; Data Source=C:\\BegASPNET11\\" +
"WroxUnited\\Database\\WroxUnited.mdb";
System.Data.IDbConnection dbConnection =
new System.Data.OleDb.OleDbConnection(connectionString);
string queryString =
@"SELECT [Teams].[TeamName], [Opponents].[OpponentName], " +
"[Games].[Location], [Opponents].[OpponentLocation] " +
"FROM [Teams], [Opponents], [Games] " +
"WHERE (([Opponents].[OpponentID] = [Games].[OpposingTeam]) " +
"AND ([Teams].[TeamID] = [Games].[WroxTeam]) " +
"AND ([Games].[Date] = @Date))";
System.Data.IDbCommand dbCommand = new System.Data.OleDb.OleDbCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_date =
new System.Data.OleDb.OleDbParameter();
dbParam_date.ParameterName = "@Date";
dbParam_date.Value = date;
dbParam_date.DbType = System.Data.DbType.DateTime;
dbCommand.Parameters.Add(dbParam_date);
dbConnection.Open();
System.Data.IDataReader dataReader =
dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
return dataReader;
}
Change this code to use the central connection string as follows:
System.Data.IDataReader GamesByDate1(System.DateTime date)
{
string connectionString =
ConfigurationSettings.AppSettings["ConnectionString"];
System.Data.IDbConnection dbConnection =
new System.Data.OleDb.OleDbConnection(connectionString);
Run the page and take a look at the results; they should be as seen in to Figure 10-30:

Figure 10-30
How It Works
We've added another panel to the page to display details of matches on a specific day. We first added the Panel control itself, and then started to build up another Repeater control:
<asp:Panel id="pnlFixtureDetails" runat="server" visible="false">
<asp:Repeater id="MatchesByDateList" runat="server">
<headertemplate>
<span style="width:110px;height:25px">Date: </span>
<span style="width:135px;height:25px">
<%# EventCalendar.SelectedDate.ToShortDateString() %>
</span> <br />
</headertemplate>
To write out the selected date in the header of the control, we use the SelectedDate property of the calendar, and write it out as a short date. After the header template comes the item template. Here, to render the results, we add plain HTML<span> tags as follows:
<itemtemplate>
<span style="width:110px">Wrox Team</span>
<span style="width:135px">
<%# DataBinder.Eval(Container.DataItem, "TeamName") %>
</span><br />
Notice how the text within the span contains a data binding expression that takes the TeamName column from the data source and places it as text within the control.We repeat the process of building up the template for the rest of the columns:
<span style="width:110px">Opposing Team</span>
<span style="width:135px">
<%# DataBinder.Eval(Container.DataItem, "OpponentName") %>
</span><br />
<span style="width:110px">Venue</span>
<span style="width:135px">
<%# Venue((string)DataBinder.Eval(Container.DataItem, "OpponentLocation"),
(int)DataBinder.Eval(Container.DataItem, "Location")) %>
</span><br />
In the final item in the list, we call the Venue() function that we declared in our code, which displays text detailing the location of the game (depending on whether it's a home or away match). We pass two parameters into the function – the location of the opponent's pitch and the location flag from the Games table that states whether the game is at home (Wrox location) or away (opponent's location).Finally, after finishing with the items in the Repeater , we add a SeparatorTemplate :
</itemtemplate>
<separatortemplate>
<hr color="#b0c4de" width="200" />
</separatortemplate>
</asp:Repeater>
</asp:Panel>
Let's head back to the Venue() function and look at how it works:
public string Venue(string OpponentLocation, int MatchVenue)
{
The function takes two input parameters – the location of the opposing team's pitch and the flag that states whether the match is at home or away. It returns a string that writes out either the name of the home pitch, or the name of the opposing teams pitch:
if (MatchVenue == 1)
{
// match is at home
return "Wroxville";
}
else
{
return OpponentLocation;
}
}
The rest of the code for this exercise deals with getting the match data from the database and displaying it when an event happens. We want to display details when the currently selected date in the calendar changes, so we added an OnSelectionChanged attribute to the Calendar control:
OnSelectionChanged="EventCalendar_SelectionChanged">
Then we added the event handler code:
public void EventCalendar_SelectionChanged(object sender, EventArgs e)
{
if (DateList[EventCalendar.SelectedDate] != null)
{
MatchesByDateList.DataSource = GamesByDate(EventCalendar.SelectedDate);
pnlFixtureDetails.Visible = true;
MatchesByDateList.DataBind();
}
else
{
pnlFixtureDetails.Visible = false;
}
}
In this event handler, we call the GamesByDate() function to retrieve match details by passing the selected date as a parameter. This function will retrieve the information and the resulting DataReader
will be used as the data source for the MatchesByDateList repeater control. The DataReader in the panel is data bound only if matches are scheduled for the selected date – if not, the panel is hidden.The actual code that retrieved the data included quite a substantial SELECT query:
string queryString =
"SELECT [Teams].[TeamName], [Opponents].[OpponentName], " +
"[Games].[Location], [Opponents].[OpponentLocation] " +
"FROM [Teams], [Opponents], [Games] " +
"WHERE (([Opponents].[OpponentID] = [Games].[OpposingTeam]) " +
"AND ([Teams].[TeamID] = [Games].[WroxTeam]) " +
"AND ([Games].[Date] = @Date))";
Because we are gathering data from multiple tables, the query used is a bit long-winded. As the end result, it retrieves the required data fields from three tables and displays them on the page. There are two more types of controls that we need to look at in this chapter. First, we're going to look at the Web- Matrix-specific controls, and produce another quick and simple ASP.NET page. In the second example, we'll take a quick look at validation controls.
Web Matrix Controls
There are some extra controls that you can use when working with Web Matrix to develop Web applications. They are designed to make life very simple when you want to create data-driven pages.
The MX DataGrid
This control is a lot like a normal DataGrid control, except that you can use it in conjunction with a Web Matrix DataSource control. There are two data source controls, one for SQL Server connections and one for Access connections. There is only one extra property on the MX DataGrid control – the DataSourceControlID property. This is how you tell the MX grid to get its data from the appropriate data source control.
The Data Source Controls
These controls have three simple properties:
ConnectionString : The string for connecting to the database
SelectCommand : The SQL used to get the data into the grid
EnableViewState : To determine whether the data stored in the control has persisted between postbacks
Let's see a quick example of this in action.
Try It Out - Wrox United – Players.aspx and the Web Matrix MX DataGrid
Create a new ASP.NET page and call it Players.aspx . In the Design view, add a Heading 1 that displays the text Wrox United. Underneath this, add a paragraph underneath a Heading 2 that displays the text Players. On a new line, switch back to the Normal paragraph style. Now we can add some content. Then in in the Workspace | Data panel at the top left of the Web Matrix environment, switch to Data view. Now drag the Players table onto the form, below the two headings as shown in Figure 10-31:

Figure 10-31
You can actually run the page at this stage – with one click and drag operation, you can display an entire table of data on the screen. If you run the page now, you will see the screen shown in Figure 10-32:

Figure 10-32
Although this is very cool, we don't necessarily want just anyone to view the site login or password details. Also, a status flag of 1, 2, or 3 doesn't mean much whereas Active, Injured, or Retired would mean more to visitors. Let's amend the example slightly.
Switch to Design view in Web Matrix, select the DataSource control, and in the Properties panel set the SelectCommand to the following:
SELECT PlayerID, PlayerName, Profile, JoinDate FROM [Players]
Select the MxDataGrid control and change the AutoGenerateFields property to false , then back to true to refresh the grid. Run the page again to see the screen shown in Figure 10-33:

Figure 10-33
How It Works
This simple example demonstrates how Web Matrix can make life easy for us! The results of this quick and simple page aren't exactly brilliant, but the information we want to display is on the form with minimal fuss. The MxDataGrid control can be customized to a great extent like the.NET DataGrid control, so that you can better control how the data is rendered.Let's take a quick look at the code that was generated for us:
<wmx:AccessDataSourceControl id="AccessDataSourceControl1"
runat="server"
ConnectionString="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=- 4;
Data Source=C:\BegASPNET\WroxUnited\Database\WroxUnited.mdb"
SelectCommand="SELECT PlayerID, PlayerName, Profile, JoinDate FROM
[Players]">
</wmx:AccessDataSourceControl>
The first control added to the page was the DataSourceControl that is either a SQLDataSourceControl or an AccessDataSourceControl , depending on the database that you are using. This control contains a connection string, and a SELECT statement to retrieve the data that we're interested in. This control is accompanied by the MxDataGrid :
<wmx:MxDataGrid id="MxDataGrid1" runat="server" BorderStyle="None"
BorderWidth="1px" DataKeyField="PlayerID"
CellPadding="3" BackColor="White" AllowPaging="true"
DataMember="Players" AllowSorting="true"
BorderColor="#CCCCCC"
DataSourceControlID="AccessDataSourceControl1">
<SelectedItemStyle font-bold="true" forecolor="White"
backcolor="#669999">
</SelectedItemStyle>
<ItemStyle forecolor="#000066"></ItemStyle>
<FooterStyle forecolor="#000066" backcolor="White"></FooterStyle>
<HeaderStyle font-bold="true" forecolor="White"
backcolor="#006699"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#000066"
backcolor="White" mode="NumericPages"></PagerStyle>
</wmx:MxDataGrid>
As you can see, this control contains templates just like any other repetitive data-bound control. In addition, this grid has a few properties different from the DataGrid control:
DataKeyField : The unique identifier for each row in the table
DataMember : The name of the table you want to view
DataSourceControlID : The name of the control that contains the data
Also included in the default implementation of this grid is a considerable amount of styling information that you might want to remove. When it comes to styling a site, it makes sense to remove this information to ensure that all pages follow the same style sheet. To further improve this page, you could manually specify the columns that appear in the grid and add formatting information for the JoinDate field. For example, you might want to remove the time portion of the date.To try this out, you can manually set the AutoGenerateFields property to false and then specify each field individually using the Fields collection editor. You could also alter the code as follows:
<wmx:MxDataGrid id="MxDataGrid1" ...
AutoGenerateFields="false">
<SelectedItemStyle font-bold="true" forecolor="White"
backcolor="#669999"></SelectedItemStyle>
<ItemStyle forecolor="#000066"></ItemStyle>
<FooterStyle forecolor="#000066" backcolor="White"></FooterStyle>
<HeaderStyle font-bold="true" forecolor="White"
backcolor="#006699"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#000066"
backcolor="White" mode="NumericPages"></PagerStyle>
<Fields>
<wmx:BoundField Visible="false" DataField="PlayerID">
</wmx:BoundField>
<wmx:BoundField DataField="PlayerName" HeaderText="Name">
</wmx:BoundField>
<wmx:BoundField DataField="Profile" HeaderText="Profile">
</wmx:BoundField>
<wmx:BoundField DataField="JoinDate" HeaderText="Join Date"
DataFormatString="{0:d}">
</wmx:BoundField>
</Fields>
</wmx:MxDataGrid>
This will change the appearance of the page as shown in Figure 10-34:

Figure 10-34
If you want to use the centralized connection string, you could alter the code as follows:
<wmx:AccessDataSourceControl id="AccessDataSourceControl1" runat="server"
SelectCommand="SELECT PlayerID, PlayerName, Profile, JoinDate FROM [Players]"
ConnectionString='<%# ConfigurationSettings.AppSettings("ConnectionString")%>'
</wmx:AccessDataSourceControl>
For this code to work correctly, you need to call the DataBind() method of the page object – you can do this by adding a simple Page_Load() method to your page as follows:
void Page_Load()
{
Page.DataBind();
}
Before we finish this section, flip back to Default.aspx and alter the following line of code as shown here:
<asp:HyperLink id="lnkPlayers" runat="server"
NavigateUrl="Players.aspx">
Players
</asp:HyperLink>
With this code, we can now link to the Players.aspx page from the main front page of the site.
Let's move on to the final part of this chapter, where we take a brief look at validation controls.
Validation Controls
Validation controls are designed to help ensure that data entered into a form conforms to some specific criteria. This reduces the chances of random gibberish being sent back to the server. Validation controls also demonstrate how ASP.NET Web controls abstract common tasks. Without validation controls, we would typically need to write our own client-side validation code (for example, client-side JavaScript) – a time consuming task indeed! By using validation controls within ASP.NET Web forms, our work is greatly simplified. Although validation involves a bit of overhead and some thought (for instance, what type of validation control to use and some knowledge of how to implement it), the benefits are that we end up with:
Easier code base to maintain
Better control over the user interface and the data that gets passed to our servers
Better experience for our users or customers
For example, using these controls, you can ensure that a user registration form contains valid data in all appropriate fields. This is done using the RequiredFieldValidator control before posting it to the server. You can also verify that an email address is in a valid format.The following is a complete list of validation controls that will give you an idea of what is possible:
Control | Purpose |
---|---|
CompareValidator | Compares a user's entry against a constant value (less than, equal to, greater than, and so on). For example, you can use this to check that a Password and a Confirm Password textbox contain identical data. |
CustomValidator | Checks the data entered by the user using validation logic from a custom method that you write – processed on the server or the client. |
RangeValidator | Checks that data entered by the user falls between specified lower and upper boundaries. Checks for ranges between pairs of numbers, alphabetic characters, and dates. Boundaries can be expressed as constants or as values derived from another control. |
RegularExpressionValidator | Checks that the entry matches a pattern defined by a regular expression. This type of validation allows you to check for predictable sequences of characters, such as those in social security numbers, e-mail addresses, telephone numbers, postal codes, IP addresses, and so on. |
RequiredFieldValidator | Ensures that the user does not skip an entry. |
Let's take a quick look at how we can add validation controls to the site.
Try It Out - Wrox United – Registering for Email Updates (Default.aspx)
We are going to add a textbox to the front page of the Web site to collect email addresses of fans who may want to receive information about upcoming events or emails containing match reports. In this example, we will simply add the textbox, a button for submitting the data, and some validation controls. We won't store the email address in this part of the example, since we'll be coming back to this example in the next chapter.
Start by re-opening Default.aspx . At the moment, we have code that displays a welcome message:
<p>
Welcome to the Wrox United Website! Please select one of the following:
</p>
Change the following section of code as shown here:
<table width="800">
<tr>
<td width="580">
<h2>Welcome to the Wrox United Website! Please select one of the
following:
</h2>
</td>
<td width="220" border="1">
</td>
</tr>
</table>
Switch back to Design view and you will see the screen shown in Figure 10-35:

Figure 10-35
Drag a Label control into the second cell of the top table, then a TextBox control, a Button
control, and finally a RegularExpressionValidator control. Set their properties as follows:
Control | Property | Value |
---|---|---|
Label | Id | lblRegister |
Text | Register for email updates | |
TextBox | Id | txtEmailAddress |
Button | Id | btnRegister |
Text | Register | |
Width | 60 | |
RegularExpressionValidator | Id | validEmail |
ErrorMessage | Please enter a valid email address | |
ControlToValidate | txtEmailAddress |
Notice that when you set the ControlToValidate property of the RegularExpressionValidator , the property box displays a combo box that lists all the available controls that can be validated (in this case, the only control that can be validated by this control is txtEmailAddress ).
We need to enter a validation expression, or else, not much will happen when we run the page. In the properties for the validEmail control, select the ValidationExpression property and click the ... button in the box to go to the expression builder.Select Internet E-mail Address from the list, as shown in Figure 10-36, and click OK:

Figure 10-36
You should now have the following code on your page:
<asp:Label id="lblRegister"
runat="server">Register for email updates</asp:Label>
<asp:TextBox id="txtEmailAddress" runat="server"></asp:TextBox>
<asp:Button id="btnRegister" runat="server" Text="Register"
Width="60px"></asp:Button>
<asp:RegularExpressionValidator id="validEmail" runat="server"
ErrorMessage="Please enter a valid email address"
ControlToValidate="txtEmailAddress"
ValidationExpression="\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
</asp:RegularExpressionValidator>
Run the page and try to enter different values into the textbox. You'll notice that if you enter some text that is not a valid email address, an error message appears when you click the button as shown in Figure 10-37. Also notice that the page doesn't reload when you click the button – the error message is generated using client-side script!

Figure 10-37
Alternatively, if you enter a valid email address, you will notice that a postback occurs when you click the button but no new information displays on the form. This is because we haven't yet handled the button click event on the server.
How It Works
Once again, we managed to create quite an interesting result using minimal code. Let's look at the code that was created when we added the validation control:
<asp:RegularExpressionValidator id="validEmail" runat="server"
ErrorMessage="Please enter a valid email address"
The first interesting property is the ErrorMessage property, which contains the text that is displayed if the validation fails.
ControlToValidate="txtEmailAddress"
The ControlToValidate property is quite self-explanatory. Notice, that the only control we could select in the Web Matrix Properties pane was the textbox. The textbox is the only control on the form that allows free text input, so it is the only one that can possibly be validated by this control.The final property contains the validation expression that we saw briefly when we were using the expression browser. The RegularExpressionValidator control works by comparing text that has been entered against this pattern. If it matches the rules specified by the pattern, validation is successful. If the text does not match this pattern, validation will fail:
ValidationExpression="\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
While a full explanation of regular expressions would take a long time, let's quickly look at what this expression means:
\w+ match one or more alphanumeric characters
( ... )* match zero or more instances of the contents of the brackets:
[-+.] match a hyphen, a plus sign, or a period
\w+ match one or more alphanumeric characters
@ matches an @ sign
\w+ match one or more alphanumeric characters
( ... )* match zero or more instances of the contents of the brackets:
[-.] an instance of a hyphen or a period
\w+ match one or more alphanumeric characters
\. matches a period
( ... )* match zero or more instances of the contents of the brackets:
[-.] an instance of a hyphen or a period
\w+ match one or more alphanumeric characters
Ok, so no one said these things were going to be too easy! However, a lot of this expression is repeated, and it means that all the following (and many more) are valid:
me@here.com
someone-else@somewhere.co.uk
thing.2@elsewhere-thing.com
Important | If you are a full time developer, it's definitely worth spending time looking at how regular expressions work – they are extremely powerful and extremely useful. The .NET Framework SDK includes information on all of the regular expression patterns available to .NET developers. |