Beginning ASP.NET 1.1 with Visual C# .NET 1002003 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Beginning ASP.NET 1.1 with Visual C# .NET 1002003 [Electronic resources] - نسخه متنی

Chris Ullman

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید











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.





Important

Due to page width limitations, the highlighted line in the following code snippet has been wrapped to two lines. You must ensure that the following statement is not wrapped in your code and is all on one line!


<?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


The

Repeater 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"/>

&nbsp;&nbsp;

<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"/>
&nbsp;&nbsp;
<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);





Important

Using a parameter ensures that the data passed into the method doesn't violate any database constraints. For example, using a parameter removes our obligation to escape text that would be invalid in a SQL statement, worry about inserting dates in an unsuitable format, and so on. Catching data type errors in code before data is passed to the database is a good way to work.


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;">&nbsp;</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.




/ 220