Chapter 10
This chapter explains how ASP.NET server controls derive their properties and methods from the various classes and objects that make up the .NET Framework. This chapter is where you see the Wrox United application taking shape.
Exercise 1
Consider a use for an HTML tag with runat="server" in the Wrox United application in place of one of the existing Web controls and explain why the HTML control is able to achieve the same result as the Web control.
Solution
You can make this change in several places in Default.aspx – for example, the asp:Hyperlink
controls could be replaced with HTML anchor tags with a runat="server" attribute. You could also change the panel which displays match details and replaces it with a <div> tag:
<div id="pnlFixtureDetails" runat="server" visible="false">
<asp:Repeater id="MatchesByDateList" runat="server">
<headertemplate>
...
</headertemplate>
<itemtemplate>
...
</itemtemplate>
<separatortemplate>
...
</separatortemplate>
</asp:Repeater>
</div>
This works because the only property of the Panel control used in this example is the Visible
property; a property that is available to all server controls, including HTML controls. A<div> is a direct replacement for a Panel in this case.
Exercise 2
Add another event handler to the Teams.aspx page that reacts to the selection of a player's name and takes the reader to the Players.aspx page.
Solution
Add the following event-handling code to Players.aspx :
void PlayersList_ItemCommand(object sender, RepeaterCommandEventArgs e)
{
if(e.CommandName == "ShowPlayers")
{
Response.Redirect("Players.aspx");
}
}
Change the HTML as follows:
<asp:Repeater id="PlayersList" runat="server"
OnItemCommand="PlayersList_ItemCommand">
<ItemTemplate>
<asp:linkbutton
text='<%# DataBinder.Eval(Container.DataItem, "PlayerName") %>'
style="color:darkred"
runat="server" width="120" CommandName="ShowPlayers" />
<asp:Label
text='<%# DataBinder.Eval(Container.DataItem, "PositionName") %>'
id="playerposition" runat="server" />
<br />
</ItemTemplate>
<headerTemplate>
Players in: <%= selectedTeam %>
<hr color="#b0c4de" width="250px" />
</headerTemplate>
<footerTemplate>
<hr color="#b0c4de" width="250px" />
</footerTemplate>
</asp:Repeater>
Exercise 3
Amend the code in Default.aspx so that if a user selects a date in the calendar for which there are no matches, nothing is displayed in the panel.
Solution
There are two ways you can achieve this result. The first of these is to modify the EventCalendar_SelectionChanged() event handler as follows:
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;
}
}
Alternatively, you can change how non-match days are rendered in the calendar:
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");
// ensure match dates are hyperlinks
e.Day.IsSelectable = true;
}
else
{
e.Cell.Style.Add("font-weight", "lighter");
e.Cell.Style.Add("color", "DimGray");
// ensure non-match dates are not selectable
e.Day.IsSelectable = false;
}
}
Using the IsSelectable property, we can control whether or not a date is selectable. Setting this to false means that non-match days are not rendered as hyperlinks and therefore, will cause the panel to not be displayed. The code in the following chapters uses this property to achieve this result.
Exercise 4
Have a go at customizing Players.aspx : change the field that displays the name of the player into a hyperlink that, when clicked, will reveal a panel lower down on the page that lists the team or teams that the selected player is a member of. You will find that the Fields editor of the MxDataGrid is very useful for this (select the Fields property builder when you are in the Design view). You need to ensure that the clicking of the player name is handled correctly. You also need to add another method to extract team information (you may find that the DataReader that returns the list of teams from the Teams.aspx page is useful here).
Solution
First, we need to change the BoundField into a ButtonField :
<Fields>
<wmx:BoundField Visible="False" DataField="PlayerID"></wmx:BoundField>
<wmx:ButtonField DataTextField="PlayerName"
HeaderText="Name" CommandName="ShowPlayer"></wmx:ButtonField>
<wmx:BoundField DataField="Profile" HeaderText="Profile"></wmx:BoundField>
<wmx:BoundField DataField="JoinDate" HeaderText="Join Date"
DataFormatString="{0:d}"></wmx:BoundField>
</Fields>
Notice we set a CommandName property on the field as well to be able to intercept and handle commands. Let's now add a repeater that will show the teams for a particular player:
<p>
<asp:Repeater id="TeamList" runat="server" Visible="False">
<ItemTemplate>
<asp:Label
style="color:darkred"
text='<%# DataBinder.Eval(Container.DataItem, "TeamName") %>'
runat="server" width="120" />
<asp:Label
text='<%# DataBinder.Eval(Container.DataItem, "PositionName") %>'
id="playerposition"
runat="server" />
<br />
</ItemTemplate>
<headerTemplate>
<%= selectedPlayer %>'s Teams:
<hr color="#b0c4de" width="250px" />
</headerTemplate>
<footerTemplate>
<hr color="#b0c4de" width="250px" />
</footerTemplate>
</asp:Repeater>
</p>
The resultant page looks almost identical to the repeater from the Team page.We also need to tell the DataGrid that we'll be writing an ItemCommand() event handler for it:
<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"
AutoGenerateFields="False"
OnItemCommand="MxDataGrid1_ItemCommand">
Now, in the Code view, we need to change the whole page script:
private string selectedPlayer;
void Page_Load()
{
Page.DataBind();
}
private void MxDataGrid1_ItemCommand(object sender,
MxDataGridCommandEventArgs e)
{
if (e.CommandName.Equals("ShowPlayer"))
{
LinkButton thing = (LinkButton)e.CommandSource;
selectedPlayer = thing.Text;
TableCell cell = (TableCell)e.Item.Controls[0];
int SelectedPlayerID = int.Parse(cell.Text);
TeamList.DataSource = GetTeamsByPlayer(SelectedPlayerID);
TeamList.DataBind();
TeamList.Visible = true;
}
else
{
TeamList.Visible = false;
}
}
private System.Data.IDataReader GetTeamsByPlayer(int playerID)
{
string connectionString =
ConfigurationSettings.AppSettings["ConnectionString"];
System.Data.IDbConnection dbConnection =
new System.Data.OleDb.OleDbConnection(connectionString);
string queryString = "SELECT
[Players].[PlayerName],[Positions].[PositionName],[Teams].[TeamName]
FROM [Players], [Positions], [PlayerTeam], [Teams]
WHERE (([PlayerTeam].[PlayerID] = [Players].[PlayerID])
AND ([PlayerTeam].[Position] = [Positions].[PositionID])
AND ([PlayerTeam].[TeamID] = [Teams].[TeamID])
AND ([Players].[PlayerID] = @PlayerID))";
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 = "@PlayerID";
dbParam_teamID.Value = playerID;
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;
}
The GetTeamsByPlayer() method is a very simple alteration of GetPlayersByTeam() from the Teams
page. The really tricky part on this page is that we need to obtain the ID of the player that the user selects. In the Team page, that was easy – we packaged up each team's ID as the CommandArgument for the LinkButton in the teams DataList . But a DataGrid won't let us do that, so we need to find another solution. Remember the fields we set up on the players DataGrid ? At the left hand side there was an invisible column that contained the player ID. This is how we get it:
TableCell cell = (TableCell)e.Item.Controls[0];
int SelectedPlayerID = int.Parse(cell.Text);