shirt, available in one size only"
id="btnBuyShirt"
onclick="AddItemToBasket"
width="100px"
text="Buy a shirt!"
CommandArgument="Shirt"
Row 2 ImageUrl="images/hat.jpg" "The official Wrox United hat!"
id="btnBuyHat"
onclick="AddItemToBasket"
width="100px"
text="Buy a hat!"
CommandArgument="Hat"
Row 3 ImageUrl="images/mascot.j pg"
"The Wrox United cuddly mascot – a must-have for the younger
supporters!"
id="btnBuyMascot"
onclick="AddItemToBasket"
width="100px"
text="Buy a Mascot!"
CommandArgument="Mascot"
4. In HTML view, you should have the following code generated for you automatically (alternatively, instead of dragging and dropping, you could type this lot in by hand if you wanted to):
<table width="600">
<tr>
<td><asp:Image id="imgCap" runat="server" ImageUrl="images/shirt.gif">
</asp:Image></td>
<td>The Wrox United shirt, available in one size only</td>
<td><asp:Button id="btnBuyShirt" onclick="AddItemToBasket"
runat="server" Text="Buy a shirt!" Width="100px"
CommandArgument="Shirt"></asp:Button> </td>
</tr>
<tr>
<td><asp:Image id="imgShirt" runat="server" ImageUrl="images/hat.jpg">
</asp:Image></td>
<td>The official Wrox United hat!</td>
<td><asp:Button id="btnBuyHat" onclick="AddItemToBasket"
runat="server" Text="Buy the hat!" Width="100px"
CommandArgument="Hat"></asp:Button> </td>
</tr>
<tr>
<td><asp:Image id="imgMascot" runat="server"
ImageUrl="images/mascot1.jpg">
</asp:Image></td>
<td>The Wrox United cuddy mascot - a must-have for the younger supporters!
</td>
<td><asp:Button id="btnBuyMascot" onclick="AddItemToBasket"
runat="server" Text="Buy the mascot!" Width="100px"
CommandArgument="Mascot"></asp:Button> </td>
</tr>
</table>
5. Add the following code below the table, while still in HTMLview:
<br/>
<p>
Your basket contains:
<asp:label id="lblBasketMessage" runat="server"></asp:label>
</p>
<p>
<asp:Repeater id="basketlist" runat="server">
<itemTemplate>
<asp:Label width="70" runat="server"
text='<%# ((DictionaryEntry)Container.DataItem).Key + "s: " %>'>
</asp:Label>
<asp:Label runat="server"
text='<%# ((DictionaryEntry)Container.DataItem).Value %>'>
</asp:Label>
<br />
</itemTemplate>
</asp:Repeater>
</p>
<p>
<asp:Button id="btnCheckOut" runat="server" Text="Checkout"></asp:Button>
<asp:Button id="btnEmptyBasket" onclick="btnEmptyBasket_Click"
runat="server" Text="Empty Basket"></asp:Button>
</p>
</form>
6. That's it for the HTML side of things! Switch to the Codeview and enter the following methods:
Note that the method signature for the AddItemToBasket()function is already generated for you, so make sure you don't add another copy of this or the page won't compile properly.
void Page_Load() {
if (Session["Basket"] == null) {
InitializeBasket();
} }
void btnEmptyBasket_Click(object sender, EventArgs e) {
InitializeBasket();
}
void AddItemToBasket(object sender, EventArgs e) {
// Each time this is run, get the CommandArgument property of the button //that fired the event, and use this to populate the Session object System.Web.UI.WebControls.Button theButton =
(System.Web.UI.WebControls.Button)sender;
string itemName = (string)theButton.CommandArgument;
System.Collections.Hashtable basketTable =
(System.Collections.Hashtable)Session["Basket"];
// Check whether the session contains an entry for that item // if no entry exists, create one with value 0
if (basketTable[itemName] == null) {
basketTable[itemName] = 0;
}
// Increment the counter for the selected item int itemCount = (int)basketTable[itemName];
basketTable[itemName] = itemCount + 1;
}
void InitializeBasket() {
System.Collections.Hashtable basketTable = new System.Collections.Hashtable();
Session["Basket"] = basketTable;
}
void Page_Prerender() {
System.Collections.Hashtable basketTable = (System.Collections.Hashtable)Session["Basket"];
basketlist.DataSource = basketTable;
basketlist.DataBind();
if ((basketTable.Count) == 0) {
lblBasketMessage.Text = "nothing - please buy something!";
} else {
lblBasketMessage.Text = "";
} }
You'll notice that there are quite a few methods in here, which we'll look at in detail in just a moment; these are designed to make the application more scalable Let's see how the page works, then the purpose of these methods will become a bit clearer. It's time to run the page and try it out! You should see the screen depicted in Figure 11-10:
Figure 11-10
How It Works
This simple example uses quite a few techniques to store data about the items in the shopping basket.
The data stored in the Sessionobject is a hashtable that stores name-value pairs. The key field for the hashtable is the name of the item added to the basket and the value is the quantity of that item. The event handlers on that page all perform different actions on the data stored in the session. Let's start by looking through the added code.
First, we added an HTML table and some simple controls to the page. Notice that the onClickattribute of each button in the table was set to fire the same event handler – AddItemToBasket(). Each button has a unique CommandArgumentproperty that describes what is added to the basket:
<table width="600">
<tr>
<td><asp:Image id="imgCap" runat="server" ImageUrl="images/shirt.gif">
</asp:Image></td>
<td>The Wrox United shirt, available in one size only</td>
<td><asp:Button id="btnBuyShirt" onclick="AddItemToBasket"
runat="server" Text="Buy a shirt!" Width="100px"
CommandArgument="Shirt"></asp:Button> </td>
</tr>
<tr>
<td><asp:Image id="imgShirt" runat="server" ImageUrl="images/hat.jpg">
</asp:Image></td>
<td>The official Wrox United hat!</td>
<td><asp:Button id="btnBuyHat" onclick="AddItemToBasket"
runat="server" Text="Buy the hat!" Width="100px"
CommandArgument="Hat"></asp:Button> </td>
</tr>
<tr>
<td><asp:Image id="imgMascot" runat="server"
ImageUrl="images/mascot1.jpg">
</asp:Image></td>
<td>The Wrox United cuddy mascot - a must-have for the younger supporters!
</td>
<td><asp:Button id="btnBuyMascot" onclick="AddItemToBasket"
runat="server" Text="Buy the mascot!" Width="100px"
CommandArgument="Mascot"></asp:Button> </td>
</tr>
</table>
Next, we added some more controls to display the contents of the basket and to provide the option of either clearing the basket or checking out via a checkout and payment process:
<br/>
<p>
Your basket contains:
<asp:label id="lblBasketMessage" runat="server"></asp:label>
</p>
<p>
<asp:Repeater id="basketlist" runat="server">
<itemTemplate>
The basketlist Repeatercontrol will be data-bound to a Hashtableobject (as we'll see in just a moment). This enables us to do some interesting data binding in this control. The first label in this control includes an interesting statement:
<asp:Label width="70" runat="server"
text='<%# ((DictionaryEntry)Container.DataItem).Key + "s: "%>'>
</asp:Label>
Notice that the textproperty has a data-binding statement in it. Here, the Keyof the current item in the Hashtable, which is used to populate this control, is displayed in this label along with some text. In this way, we take the name of each item, and we can pluralize it by adding "s" to the end of it. The colon is there to add a neat grammatical separator between the name of the item and the quantity. So we have Mascots: 3as the displayed text, having obtained the key Mascotfrom the Hashtable. Notice that we have to add a cast to this statement to tell the C# compiler that the DataItemis of type
DictionaryEntry, because Hashtables are collections of DictionaryEntrydata types.
The rest of the code in the HTML view of the page includes another data-binding expression to obtain the quantity of the current item in the shopping basket.
<asp:Label runat="server"
text='<%# ((DictionaryEntry)Container.DataItem).Value %>'>
</asp:Label>
<br />
</itemTemplate>
</asp:Repeater>
</p>
<p>
<asp:Button id="btnCheckOut" runat="server" Text="Checkout"></asp:Button>
<asp:Button id="btnEmptyBasket" onclick="btnEmptyBasket_Click"
runat="server" Text="Empty Basket"></asp:Button>
</p>
The online payment procedures that you would associate with the Checkoutbutton hasn't been implemented in the code – that's a bit beyond the scope of this chapter. For more information on online payments, you might want to consult www.paypal.com, one of many online payment service providers.
It's time to work through the methods in the code. They are presented in roughly the same order in which they will be processed when a page is requested by the browser (following the chain of events as they correspond to the page lifecycle).
Firstly, the Page_Load()event handler:
void Page_Load() {
if (Session["Basket"] == null) {
InitializeBasket();
} }
Each time the page is loaded, this method will check whether the Sessionobject contains a basket. If it doesn't, a new empty basket is created. We'll look at the InitializeBasket()method that does this in just a moment.
void btnEmptyBasket_Click(object sender, EventArgs e) {
InitializeBasket();
}
The btnEmptyBasket_Click()method also calls the InitializeBasket()method to clear out any existing basket data. The AddItemToBasket()method comes next, and this one is quite interesting. For starters, allthe three item buttons call thismethod, and they each pass a CommandArgumentproperty.
void AddItemToBasket(object sender, EventArgs e) {
// Each time this is run, get the CommandArgument property of the button that
// fired the event, and use this to populate the Session object System.Web.UI.WebControls.Button theButton =
(System.Web.UI.WebControls.Button)sender;
string itemName = (string)theButton.CommandArgument;
These two lines of code are all that's needed to get the string information that specifies which button the user clicked. Once you have this string (which is set to either Shirt, Hat, or Mascot), you can use it to add data to the session.
First, create a local Hashtableobject to temporarily store the contents of the Session's basket item. This will make the code easier to understand.
System.Collections.Hashtable basketTable =
(System.Collections.Hashtable)Session["Basket"];
The remainder of the code for this method uses basketTable, which is the hashtable representation of the contents of the Sessionobject's Basketitem, to add items to the session. Now, the fun thing is that because you didn't add a Newkeyword to the basketTable, you don't get a newHashtableobject.
Instead, you referto an existing Hashtable object, specifically the one that is stored in the Sessionobject.
This is an example of working with referencetypes. Because you are working with the contents of the Session's Basketitem via the basketTableHashtable; anything you do to the basketTablewill affect the contents of the Session.
A full discussion of value and reference types is beyond this chapter. For more information, you should read Professional ASP.NET 1.1, Wrox Press, ISBN: 0-7645-5890-0.
We check to see if the item added to the basket has been added before:
// Check whether the session contains an entry for that item // if no entry exists, create one with value 0
if (basketTable[itemName] == null)
The Newkeyword is not included in the Hashtable's declaration – this is very important, as you'll see in a moment!
{
basketTable[itemName] = 0;
}
If the item has never been in the basket before, it is added to the basket and initialized to 0to make it ready to receive a new quantity. In the next piece of code, one more of the selected item is added to the basket. Thus, if there were already three Mascots in the basket, clicking this button will add another one to the basket, resulting in a basket that contains four Mascots.However, if this were the first mascot you bought, you would end up with one mascot.
// Increment the counter for the selected item int itemCount = (int)basketTable[itemName];
basketTable[itemName] = itemCount + 1;
}
Let's look at the InitializeBasket()method mentioned earlier:
void InitializeBasket() {
System.Collections.Hashtable basketTable = new
System.Collections.Hashtable();
Session["Basket"] = basketTable;
}
In just two lines of code, a new Hashtableobject is created and the Session's Basketitem is set to point to it. The new object doesn't have any items in it, so it will be an 'empty' basket.
If this method was called in response to clicking the Empty Basketbutton, it's likely that you originally had a full basket, so where do all the original contents of the basket go?
Well, the answer is that by changing the Hashtable that the Basketitem is pointing to, we change which Hashtable is referenced by the Session. Thus, the old Hashtable (the old basket) is no longer pointed to by anything, and the .NET garbage collector sweeps the Hashtable away. The Hashtable is no longer wanted because no one is using it, so we can get rid of it to clear out some memory. New objects can now use the memory that was used by the old hashtable. The garbage collector is very efficient and gets rid of unreferenced objects on a regular basis. The memory that the old Hashtableobject was taking up is recycled, which means that you are less likely to run out of memory.
There is one last event handler to look at. This one handles the Prerender()event of the page. This event is always fired when a page is loaded and is your last chance to change anything just before the page is displayed:
void Page_Prerender() {
System.Collections.Hashtable basketTable = (System.Collections.Hashtable)Session["Basket"];
basketlist.DataSource = basketTable;
basketlist.DataBind();
This code uses the same Hashtable that is stored in the Sessionobject's Basketitem by pointing another Hashtable towards the same data. This data is stored in your computer's memory, so what you're doing is telling your program where to find that data. Again, any changes made to the
basketTablewill change the contents of the Basket(because you are changing the same object!) The basketListcontrol is a simple Repeatercontrol, like the ones used in the previous chapter. In this example, we bind the contents of the Hashtable to the Repeaterto display the contents of the basket on the page:
if ((basketTable.Count) == 0) {
lblBasketMessage.Text = "nothing - please buy something!";
} else {
lblBasketMessage.Text = "";
} }
Lastly, a message is displayed if the basket is empty. If the count of all the items in the basketTable Hashtable is 0, the Hashtable or the virtual basket is empty. All this is done by the Prerender()event handler to ensure that only the most recent data is displayed after any items that may have been added.
Remember that when you click a button, a postback is initiated and the page is reloaded. If we'd have displayed the contents of the basket by adding code to the Page_Load()event handler, we would not be displaying the most current data, but the data of what happened that lasttime the button was clicked, because the Page_Load()event handler will run beforethe button click event is handled. You can easily try this out for yourself – if you move all the contents of this method into the Page_Load()event handler, you'll see that the count of the number of items in the basket is one behind the actual count.
That's it for now – Sessions will be back later when we put together a fun example that adds some style to the Wrox United site. Let's move on to applications.
Applications
One step up from the session is the application. From the time an ASP.NET application is first loaded, to when the application is restarted, which could be due to a configuration change or the restarting of the Web server, you can store information related to that application in the Applicationobject. When the application is restarted, any information stored in the Applicationobject will be lost, so you need to decide carefully what to store in the application state.
For larger data or for data that doesn't often change, the ASP.NET Cache object can be very useful, as you'll see in the Caching section later in the chapter.
It's best to only store small amounts of data in the application state to minimize memory usage on the server. Small bits of data that change frequently but don't need to be saved when the application is restarted are best kept in the Sessionobject.
The actual order of events is: Page_Load()--> Control Events --> Prerender()
How Do Applications Work?
Applications are a bit simpler than sessions, since they run entirely on the server. When the application is running, you can use the same method of storing data in an application object as used with sessions All you need to do is enter an identifier and a value that can be of any type, even a dataset. The best way to look at how application state can be used is through an example. Let's take this opportunity to add another page to your site that has more interactivity than the previous pages!
Try It Out Using Application State
In this example, you will construct the world's simplest chat room! All you need is a new ASP.NET page on the Wrox United site, a few controls, and a bit of code, and you can then chat with anyone around the world.
1. The first step is to reopen Default.aspxand add a new hyperlink to the list of links on the left.
Call it lnkChat, and set its NavigateUrlproperty to Chat.aspx.
2. Create a new ASP.NET page called Chat.aspx. Switch to HTML view and enter the following code within the <form>tags:
<h1>Wrox United
</h1>
<h2>Online Chat
</h2>
<asp:TextBox id="txtChatBox" runat="server"
TextMode="MultiLine"
Height="200px" Width="550px" ReadOnly="true">
</asp:TextBox>
<br />
<br />
<table width="550">
<tbody>
<tr>
<td width="150">
Enter your name:
</td>
<td>
<asp:TextBox id="txtName" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td width="150">
Enter your message:
</td>
<td>
<asp:TextBox id="txtMessage" runat="server"
MaxLength="100" Width="402px">
</asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>