Bill Evjen
September 2004
Applies to:
ASP.NET 2.0
Summary: While the new membership, profile, and personalization features of ASP.NET 2.0 have gotten most of the attention, there are also many other new controls being added. See many of these new controls in action in this excerpt from the Wrox book, ASP.NET 2.0 Beta Preview, by Bill Evjen. (40 printed pages)
Contents
BulletedList Server Control
HiddenField Server Control
FileUpload Server Control
MultiView and View Server Controls
Wizard Server Control
Customizing the side navigation
Examining the AllowReturn attribute
Working with the StepType attribute
Adding a header to the Wizard control
Working with the Wizard's navigation system
Utilizing Wizard control events
DynamicImage Server Control
Working with images from disk
Resizing images
Displaying images from streams
ImageMap Server Control
Summary
Related Books
When I sat in one of the first review sessions for ASP.NET 2.0 on the Microsoft campus in Redmond, Washington, I remember being amazed by the number of new server controls (in addition to many other new and exciting features) this newest release offered. The core infrastructure was already in place with ASP.NET 1.0/1.1; but with the much improved 2.0 release, the ASP.NET team was making the lives of developers even simpler.
The purpose of this large collection of new controls is to make you more productive. They enable you to introduce advanced functionality that would have been laboriously programmed or simply omitted in the past. For example, in the classic ASP days, few calendars were used on Internet Web sites. With the introduction of the Calendar server control in ASP.NET 1.0, calendar creation on a site became a trivial task. And with ASP.NET 1.0/1.1, it was rather difficult to build an image map on top of an image. Through the use of a new server control, this is now built into ASP.NET 2.0.
I covered a considerable number of these new controls in the preceding chapters, but I still have quite a few new server controls to discuss. This chapter takes a look at some of these new server controls and explains how to use them in ASP.NET 2.0 applications.
BulletedList Server Control
One common HTML Web page element is a collection of items in a bulleted list. The BulletedList server control is meant to easily display a bulleted list of items in an ordered (using the HTML <ol> element) or unordered (using the HTML <ul> element) fashion.
In addition to creating lists that are ordered or unordered, you can use this control to determine the style used for displaying the list.
The BulletedList control can be constructed of any number of <asp:ListItem> controls or be data-bound to a data source of some kind and populated based upon the contents retrieved. Listing 1 shows a bulleted list in its simplest form.
Listing 1: A simple BulletedList control
<%@ Page Language="VB" %>
<html >
<head runat="server">
<title>BulletedList Server Control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:BulletedList ID="Bulletedlist1" Runat="server">
<asp:ListItem>United States</asp:ListItem>
<asp:ListItem>United Kingdom</asp:ListItem>
<asp:ListItem>Finland</asp:ListItem>
<asp:ListItem>Russia</asp:ListItem>
<asp:ListItem>Sweden</asp:ListItem>
<asp:ListItem>Estonia</asp:ListItem>
</asp:BulletedList>
</form>
</body>
</html> The use of the <asp:BulletedList> element, along with the <asp:ListItem> elements, produces a simple bulleted list output like the one shown in Figure 1.
.gif)
Figure 1
The BulletedList control also enables you to easily change the style of the list with just one or two attributes. The BulletStyle attribute changes the style of the bullet that precedes each line of the bulleted list. This attribute has possible values of Numbered, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman, Disc, Circle, Square, NotSet, and CustomImage. Figure 2 shows an example of these styles (except the CustomImage setting, as it can be any image of your choice).
.gif)
Figure 2
When working with any of the numbered styles (Numbered, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman), you can also change the starting value of the first bulleted item in the list by using the FirstBulletNumber attribute. Setting this attribute's value to 5 when you use the UpperRoman setting results in the format illustrated in Figure 3.
.gif)
Figure 3
When you use the CustomImage setting in the BulletedList control, you must also use the BulletedImageUrl attribute in the following manner:
<asp:BulletedList ID="Bulletedlist1" Runat="server"
BulletStyle="CustomImage" BulletedImageUrl="~/myImage.gif">
The BulletedList control has an attribute called DisplayMode, which has three possible values: Text, HyperLink, and LinkButton. Text is the default and has been used so far in the examples. Using Text means that the items in the bulleted list are laid out only as text. HyperLink means that each of the items is turned into a hyperlink—any user clicking the link is redirected to another page. This page is specified by the <asp:ListItem> control's Value attribute. A value of LinkButton turns each bulleted list item into a hyperlink that posts back to the same page. It enables you to retrieve the selection that the end user makes, as illustrated in Listing 2.
Listing 2: Using the LinkButton value for the DisplayMode attribute
VB
<%@ Page Language="VB"%>
<script runat="server">
Sub BulletedList1_Click(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.BulletedListEventArgs)
Label1.Text = "The index of item you selected: " & e.Index & _
"The value of the item selected: " & _
BulletedList1.Items(e.Index).Text
End Sub
</script>
<html >
<head runat="server">
<title>BulletedList Server Control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:BulletedList ID="BulletedList1" Runat="server"
OnClick="BulletedList1_Click" DisplayMode="LinkButton">
<asp:ListItem>United States</asp:ListItem>
<asp:ListItem>United Kingdom</asp:ListItem>
<asp:ListItem>Finland</asp:ListItem>
<asp:ListItem>Russia</asp:ListItem>
<asp:ListItem>Sweden</asp:ListItem>
<asp:ListItem>Estonia</asp:ListItem>
</asp:BulletedList>
<asp:Label ID="Label1" Runat="server">
</asp:Label>
</form>
</body>
</html>
C#
<script runat="server">
void BulletedList1_Click(object sender,
System.Web.UI.WebControls.BulletedListEventArgs e)
{
Label1.Text = "The index of item you selected: " + e.Index +
"The value of the item selected: " +
BulletedList1.Items[e.Index].Text;
}
</script> In this example, the DisplayMode attribute is set to LinkButton, and the OnClick attribute is used to point to the BulletedList1_Click event. This event uses the BulletedListEventArgs object, which only exposes the Index property. Using this, you can determine the index number of the item selected.
You can directly access the Text value of a selected item by using the Items property, or you can use the same method to populate an instance of the ListItem object. You do so as shown here:
VB
Dim blSelectedValue As ListItem = BulletedList1.Items(e.Index)
C#
ListItem blSelectedValue = BulletedList1.Items[e.Index];
Now that you have seen how to create bulleted lists with items that you declaratively place in the code, take a look at how to create dynamic bulleted lists from items that are stored in a data store of some kind. For an example of how to use the BulletedList control to data-bind to results coming from a data store, look at the following example, where all information is retrieved from an XML file.
The first step is to create the XML file. For this example, I use the XML file shown in Listing 3.
Listing 3: FilmChoices.xml
<?xml version="1.0" encoding="utf-8"?>
<FilmChoices>
<Film>
<Title>Close Encounters of the Third Kind</Title>
<Year>1977</Year>
<Director>Steven Spielberg</Director>
</Film>
<Film>
<Title>Grease</Title>
<Year>1978</Year>
<Director>Randal Kleiser</Director>
</Film>
<Film>
<Title>Lawrence of Arabia</Title>
<Year>1962</Year>
<Director>David Lean</Director>
</Film>
</FilmChoices> To populate the BulletedList server control with the <Title> element from the FileChoices.xml file, use a DataSetDataSource control to access the file, as illustrated in Listing 4.
Listing 4: Dynamically populating a BulletedList server control
<%@ Page Language="VB" %>
<html >
<head runat="server">
<title>BulletedList Server Control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:BulletedList ID="Bulletedlist1" Runat="server"
DataSourceId="DataSetDataSource1" DataTextField="Title">
</asp:BulletedList>
<asp:DataSetDataSource ID="DataSetDataSource1"
Runat="server" DataFile="~/FilmChoices.xml">
</asp:DataSetDataSource>
</form>
</body>
</html> In this example, you use the DataSourceId attribute to point to the DataSetDataSource control (as you would with any control that can be bound to one of the data source controls). After you are connected to this data source control, you specifically point at the <Title> element by using the DataTextField attribute. After the two server controls are connected and the page is run, you get a bulleted list that is completely generated from the contents of the XML file. The result is shown in Figure 4.
.gif)
Figure 4
HiddenField Server Control
For many years now, developers have been using hidden fields in their Web pages to work with state management. The <input type="hidden"> element is ideal for storing items that have no security context to them. These items are simply placeholders for data points that you want to store in the page itself instead of using the Session object or intermingling the data with the view state of the page. View state is another great way to store information in a page, but many developers turn off this feature in order to avoid corruption of the view state or possibly degradation of page performance.
Any time a hidden field is placed within a Web page, it is not interpreted in the browser in any fashion, although it is completely viewable by end users if they look at the source of the HTML page.
Listing 5 is an example of using the HiddenField server control to hold a GUID that can be used from page to page simply by carrying over its value as the end user navigates through your application.
Listing 5: Working with the HiddenField server control
VB
<%@ Page Language="VB" %>
<script runat="server" language="vb">
Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
HiddenField1.Value = System.Guid.NewGuid().ToString()
End Sub
</script>
<html >
<head runat="server">
<title>HiddenField Server Control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:HiddenField ID="HiddenField1" Runat="Server" />
</form>
</body>
</html>
C#
<%@ Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
HiddenField1.Value = System.Guid.NewGuid().ToString();
}
</script> In this example, the Page_Load event populates the HiddenField1 control with a GUID. You can see the hidden field and the value created by looking at the source of the blank HTML page that is created. You should see a result similar to the following (though the GUID has a different value, of course):
<input type="hidden" name="HiddenField1" id="HiddenField1"
value="a031e77c-379b-4b4a-887c-244ee69584d5" />
On the page postback, ASP.NET can detect whether the HiddenField server control has changed its value since the last post. This enables you to change the HiddenField value with client-side script and then work with the changes in a page event.
The HiddenField server control has an event called ValueChanged that you can use when the value is changed:
VB
Sub HiddenField1_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs)
' Handle event here
End Sub
C#
void HiddenField1_ValueChanged(object sender, EventArgs e)
{
// Handle event here
} This ValueChanged event is triggered when the ASP.NET page gets posted back to the server if the value of the HiddenField server control has changed since the last time the page was drawn. If the value of the HiddenField control has not changed, this method is never triggered. Therefore, this method is useful when you act upon any changes to the HiddenField control—such as recording a value to the database or changing a value in the user's profile.
FileUpload Server Control
In ASP.NET 1.0/1.1, it was quite possible to upload files using the HTML FileUpload control. This HTML server control put an <input type="file"> element on your Web page to enable the end user to upload files to the server. In order to use this file, however, you had to make a couple of modifications to the page. You were, for example, required to add an enctype="multipart/form-data" to the page's <form> element.
ASP.NET 2.0 introduces a new FileUpload server control that makes the process of uploading files to a server even simpler than before. When giving a page the capability to upload files, you simply include the new <asp:FileUpload> control, and ASP.NET takes care of the rest, including adding the enctype attribute to the page's <form> element for you.
After the file is uploaded to the server, you can also take hold of the uploaded file's properties and either display them to the end user or use these values yourself in your page's code-behind. Listing 6 shows an example of using the new FileUpload control. The page contains a single FileUpload control, plus a Button and a Label control.
Listing 6: Uploading files using the new FileUpload control
VB
<%@ Page Language="VB"%>
<script runat="server">
Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
If FileUpload1.HasFile Then
Try
FileUpload1.SaveAs("C:\Uploads\" & _
FileUpload1.FileName)
Label1.Text = "File name: " & _
FileUpload1.PostedFile.FileName & "<br>" & _
"File Size: " & _
FileUpload1.PostedFile.ContentLength & " kb<br>" & _
"Content type: " & _
FileUpload1.PostedFile.ContentType
Catch ex As Exception
Label1.Text = "ERROR: " & ex.Message.ToString()
End Try
Else
Label1.Text = "You have not specified a file."
End If
End Sub
</script>
<html >
<head runat="server">
<title>FileUpload Server Control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:FileUpload ID="FileUpload1" Runat="server" />
<p>
<asp:Button ID="Button1" Runat="server" Text="Upload"
OnClick="Button1_Click" /></p>
<p>
<asp:Label ID="Label1" Runat="server"></asp:Label></p>
</form>
</body>
</html>
C#
<%@ Page Language="C#"%>
<script runat="server">
void Button1_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
try {
FileUpload1.SaveAs("C:\\Uploads\\" + FileUpload1.FileName);
Label1.Text = "File name: " +
FileUpload1.PostedFile.FileName + "<br>" +
FileUpload1.PostedFile.ContentLength + " kb<br>" +
"Content type: " +
FileUpload1.PostedFile.ContentType;
}
catch (Exception ex) {
Label1.Text = "ERROR: " + ex.Message.ToString();
}
else
{
Label1.Text = "You have not specified a file.";
}
}
</script> From this example, you can see that the entire process is rather simple. The single button on the page initiates the upload process. The first check done examines whether a file reference was actually placed within the <input type="file"> element. If a file was specified, an attempt is made to upload the referenced file to the server using the SaveAs method of the FileUpload control. This method takes a single String parameter, which should include the location where you want to save the file. In the String parameter that I use in Listing 6, you can see that I am saving the file to a folder called Uploads, which is located in the C:\ drive.
In addition to saving the file uploaded to the C:\Uploads folder, I give the saved file the same name as the file it was copied from. To do this, I use the PostedFile.FileName attribute. If you want to name the file something else, you simply use the SaveAs method in the following manner:
FileUpload1.SaveAs("C:\Uploads\UploadedFile.txt") You could also give the file a name that specifies the time it was uploaded:
FileUpload1.SaveAs("C:\Uploads\" & System.DateTime.Now.ToFileTimeUtc() & ".txt") In this case, the file is named according to the time the upload occurred.
After the upload is successfully completed, the Label control on the page is populated with the metadata of the uploaded file. In this case, the file's name, size, and content type are retrieved and displayed on the page for the end user. When the file is uploaded to the server, the page generated is similar to that shown in Figure 5.
.gif)
Figure 5
Uploading files to another server can be an error-prone affair. It is vital to upload files in your code using proper exception handling. This is why the file in the example is uploaded using a Try Catch statement. Another way to prevent the FileUpload control from ruining the experience of the end user is to use the FileUpload's AlternateText attribute:
<asp:FileUpload ID="FileUpload1" Runat="server"
AlternateText="Your browser does not allow uploads." />
This attribute assists end users whose browsers are unable to understand the <input type="file"> element. That specific user gets the text shown in the AlternateText attribute.
MultiView and View Server Controls
The MultiView and View server controls work together to give you the capability to turn on and off sections of an ASP.NET page. Turning sections on and off, which means activating or deactivating a series of View controls within a MultiView control, is similar to changing the visibility of Panel controls. For certain operations, however, you may find that the MultiView control is easier to manage and work with.
The sections, or views, do not change on the client-side; rather, they change with a postback to the server. In each view, you can put any number of elements and controls, and the end user can work through the views based upon the sequence numbers that you assign to the views.
You can build them (like all server controls) from the Source view or Design view. If working with Visual Studio 2005, you can drag and drop a MultiView control onto the design surface and then drag and drop any number of View controls inside the MultiView control. You place the elements you want within the View controls. After this is completed, you have something like the view in Figure 6.
.gif)
Figure 6
The other option is to create this directly in the code, as shown in Listing 7.
Listing 7: Using the MultiView and View server controls
VB
<%@ Page Language="VB"%>
<script runat="server">
Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If Not Page.IsPostBack Then
MultiView1.ActiveViewIndex = 0
End If
End Sub
Sub NextView(ByVal sender As Object, ByVal e As System.EventArgs)
MultiView1.ActiveViewIndex += 1
End Sub
</script>
<html >
<head runat="server">
<title>MultiView Server Control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:MultiView ID="MultiView1" Runat="server">
<asp:View ID="View1" Runat="Server">
Billy's Famous Pan Pancakes<p />
<i>Heat 1/2 tsp of butter in cast iron pan.<br />
Heat oven to 450 degrees Fahrenheit.<br />
</i><p />
<asp:Button ID="Button1" Runat="Server" Text="Next Step"
OnClick="NextView" />
</asp:View>
<asp:View ID="View2" Runat="Server">
Billy's Famous Pan Pancakes<p />
<i>Mix 1/2 cup flour, 1/2 cup milk and 2 eggs in bowl.<br />
Pour in cast iron pan. Place in oven.</i><p />
<asp:Button ID="Button2" Runat="Server" Text="Next Step"
OnClick="NextView" />
</asp:View>
<asp:View ID="View3" Runat="Server">
Billy's Famous Pan Pancakes<p />
<i>Cook for 20 minutes and enjoy!<br />
</i><p />
</asp:View>
</asp:MultiView>
</form>
</body>
</html>
C#
<%@ Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
MultiView1.ActiveViewIndex = 0;
}
}
void NextView(object sender, EventArgs e)
{
MultiView1.ActiveViewIndex += 1;
}
</script> In the example in Listing 7, you can see that three views are expressed in the MultiView control. Each view is constructed with an <asp:View> server control that also needs ID and Runat attributes added. A button is added to each of the first two views (View1 and View2) of the MultiView control. These buttons both point to a server-side event that triggers the MultiView control to progress onto the next view within the series of views.
Before either of the buttons can be clicked, the MultiView control's ActiveViewIndex attribute is assigned a value. By default, the ActiveViewIndex, which describes the view that should be showing, is set to -1. This means that no view shows when the page is generated. To start on the first view when the page is drawn, you set the ActiveViewIndex property to 0, which is the first view because this is a zero-based index. Therefore, the code from Listing 7 first checks to see if the page is in a postback situation and if not, the ActiveViewIndex is assigned to the first View control.
Each of the buttons in the MultiView control triggers the NextView method. This method simply adds one to the ActiveViewIndex value—thereby showing the next view in the series until the last view is shown. The view series is illustrated in Figure 7.
.gif)
.gif)
.gif)
Figure 7
In addition to the Next Step button on the first two views, you can also place a button in the last two views that allows the user to navigate backward through the views. To do this, create two buttons titled Previous Step in the last two views, both of which point to the following method in their OnClick events:
VB
Sub PreviousView(ByVal sender As Object, ByVal e As System.EventArgs)
MultiView1.ActiveViewIndex -= 1
End Sub
C#
void PreviousView(object sender, EventArgs e)
{
MultiView1.ActiveViewIndex -= 1;
} Here, the PreviousView method subtracts one from the ActiveViewIndex value, thereby showing the previous view in the view series.
Another option to spice up the MultiView control is to add a step counter that displays to a Label control which step the end user is currently performing in the series. In the Page_PreRender event, you add the following line:
VB
Label1.Text = "Step " & (MultiView1.ActiveViewIndex + 1).ToString() & _
" of " & MultiView1.Views.Count.ToString()
C#
Label1.Text = "Step " + (MultiView1.ActiveViewIndex + 1).ToString() +
" of " + MultiView1.Views.Count.ToString(); Now when working through the MultiView control, the end user sees "Step 1 of 3" on the first view, which changes to "Step 2 of 3" on the next view, and so on.
Wizard Server Control
Quite similar to the MultiView control, the Wizard server control enables you to build a sequence of steps that are displayed to the end user. Web pages are all about either displaying or gathering information and, in many cases, you don't want to display all the information at once—nor do you always want to gather everything from the end user at once. Sometimes, you want to trickle the information in from or out to the end user.
When you are constructing a step-by-step process that includes logic on the steps taken, use the Wizard control to manage the entire process. When working with the Wizard control for the first time, notice that this control allows for a far greater degree of customization than does the MultiView control.
In its simplest form, the Wizard control can be just an <asp:Wizard> element with any number of <asp:WizardStep> elements. In Listing 8, you create a Wizard control that works through three steps.
Listing 8: A simple Wizard control
<%@ Page Language="VB"%>
<html >
<head runat="server">
<title>Wizard server control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Wizard ID="Wizard1" Runat="server" SideBarEnabled="true"
ActiveStepIndex="0">
<WizardSteps>
<asp:WizardStep Runat="server" Title="Step 1">
This is the first step.</asp:WizardStep>
<asp:WizardStep Runat="server" Title="Step 2">
This is the second step.</asp:WizardStep>
<asp:WizardStep Runat="server" Title="Step 3">
This is the third and final step.</asp:WizardStep>
</WizardSteps>
</asp:Wizard>
</form>
</body>
</html> In this example, three steps are defined with the <asp:WizardSteps> control. Each step contains content. In this case, the content is simply text, but you can put anything you desire—such as other Web server controls or even user controls. The order in which the Wizard Steps are defined is completely based upon the order in which they appear within the <WizardSteps> element. Changing this order changes the order in which the end user sees them.
The <asp:Wizard> element itself contains a couple of important attributes. The first is the SideBarEnabled attribute. In this example, it is set to True—meaning that a side navigation system in the displayed control enables the end user to quickly navigate to other steps in the process. The ActiveStepIndex attribute of the Wizard control defines the first Wizard Step. In this case, it is the first step—0.
The three steps of this Wizard control are shown in Figure 8.
.gif)
.gif)
.gif)
Figure 8
From this example, you can see that the side navigation allows for easy access to the steps defined. The Wizard control also adds appropriate buttons to the steps in the process. The first step has simply a Next button, whereas the middle step has Previous and Next buttons, and the final step has a Previous and a Finish button. Therefore, the end user can navigate through the steps using either the side navigation or the buttons on each of the steps. You can customize the Wizard control in so many ways that it tends to remind me of the other rich Web server controls from ASP.NET, such as the Calendar control. Because so much is possible, I cover only a few of the basics that you are most likely to employ in some of the Wizard controls you build.
Customizing the side navigation
From the example in Figure 8, you can see that the steps are defined as Step 1, Step 2, and Step 3. The links are created based upon the Title property's value that you give to each of the <asp:WizardStep> elements in the Wizard control:
<asp:WizardStep Runat="server" Title="Step 1">
This is the first step.</asp:WizardStep>
By default, when creating Wizard Steps in Design view, each Wizard Step created is titled Step X (with X being the number in the sequence). You can easily change the value of the Title attributes of each of the Wizard Steps to define the steps as you see fit. Figure 9 shows the side navigation of the Wizard control with renamed titles.
.gif)
Figure 9
Examining the AllowReturn attribute
Some other interesting points of customization for the side navigation piece of the Wizard control include the AllowReturn attribute. By setting this attribute on one of the Wizard Steps to False, you can remove the capability for the end users to go back to the step after they have viewed it. This ensures that the end users cannot backward navigate to any of the viewed steps that contained this attribute, but they would be able to return to any steps that do not contain this attribute or which have this attribute set to True:
<asp:WizardStep Runat="server" Title="Step 1" AllowReturn="False">
This is the first step.</asp:WizardStep>
Working with the StepType attribute
Another interesting attribute in the <asp:WizardStep> element is the StepType attribute. The StepType attribute defines the structure of the buttons used on the steps. For instance, by default, the Wizard control places only a Next button on the first step. It understands that because this is the first step, you probably don't need the Previous button here. It also knows to use a Next and Previous button on the middle step, but it uses a Previous and a Finish button on the last step. It draws the buttons in this fashion because, by default, the StepType attribute is set to Auto, meaning that the Wizard control determines the placement of buttons. You can, however, take control of the StepType attribute in the <asp:WizardStep> element to make your own determination about which buttons are used for which steps.
Besides a StepType value of Auto, the other options include Start, Step, Finish, and Complete. A value of Start for the Wizard Step means that the step defined has only a Next button and nothing else. This simply allows the user to proceed to the next step in the series. A value of Step means that the Wizard Step has a Next and a Previous button. A value of Finish means that the step includes a Previous and a Finish button. The last one, Complete, is an interesting step that you can add to give some final message to the end user who is working through the steps of your Wizard control. In the Wizard control shown in Listing 8, for example, when the end user gets to the last step and clicks the Finish button, nothing happens, and the user just stays on the last page. You can address this by adding a final step to the collection of Wizard Steps to give a final message as shown in Listing 9.
Listing 9: Having a Complete step in the Wizard Step collection
<WizardSteps>
<asp:WizardStep Runat="server" Title="Step 1">
This is the first step.</asp:WizardStep>
<asp:WizardStep Runat="server" Title="Step 2">
This is the second step.</asp:WizardStep>
<asp:WizardStep Runat="server" Title="Step 3">
This is the third and final step.</asp:WizardStep>
<asp:WizardStep Runat="server" Title="Final Step" StepType="Complete">
Thanks for working through the steps.</asp:WizardStep>
</WizardSteps> When you run this Wizard control in a page, you still only see the first three steps in the side navigation. Because the last step has a StepType set to Complete, it does not appear in the side navigation list, and when the end user clicks the Finish button in Step 3, the last step—Final Step—is shown and no buttons are shown with it.
Adding a header to the Wizard control
The Wizard control enables you to place a header at the top of the control with the use of the HeaderText attribute in the main <asp:Wizard> element. This is illustrated in Listing 10.
Listing 10: Working with the HeaderText attribute
<asp:Wizard ID="Wizard1" Runat="server" SideBarEnabled="true" ActiveStepIndex="0"
HeaderText=" Step by Step with the Wizard control "
HeaderStyle-BackColor="DarkGray" HeaderStyle-Font-Bold="true"
HeaderStyle-Font-Size="20">
...
</asp:Wizard>
This code creates a header that appears on each of the steps in the Wizard. The result of this snippet is shown in Figure 10.
.gif)
Figure 10
Working with the Wizard's navigation system
As I stated earlier, the Wizard control allows for a very high degree of customization—especially in the area of style. You can customize every single aspect of the process, as well as how every element appears to the end user.
Pay particular attention to the options that are available for customization of the navigation buttons. By default, the Wizard Steps use buttons for Next, Previous, and Finish that are used throughout the entire series of steps. From the main <asp:Wizard> element, you can change everything about these buttons and how they work.
First, if you look through the long list of attributes available for this element, notice that one available button isn't shown by default. The Cancel button can be added by setting the value of the DisplayCancelButton attribute to True. As shown in Figure 11, this gives you a Cancel button within the navigation created for each and every step (including the final step in the series).
.gif)
Figure 11
After you decide which buttons to use within the Wizard navigation, you can choose the style of these buttons. By default, regular buttons appear, but you can change the button style with the CancelButtonType, FinishStepButtonType, FinishStepPreviousButtonType, NextStepButtonType, PreviousStepButtonType, and StartStepNextButtonType attributes. All these attributes change the style of the buttons used for the navigation system of the Wizard control. If you use any of these button types and want all the buttons consistently styled, you must change each attribute to the same value. The possible values of these button-specific elements include Button, Image, and Link. Button is the default and means that the navigation system uses buttons. A value of Image enables you to use image buttons, and Link turns a selected item in the navigation system into a hyperlink.
In addition to these button-specific attributes of the <asp:Wizard> element, you can also specify a URL to which the user is directed when the he clicks either the Cancel or Finish buttons. To redirect the user with one of these buttons, you use the CancelDestinationPageUrl or the FinishDestinationPageUrl attributes and set the appropriate URL as the destination.
Finally, you are not required to use the default text included with the buttons in the navigation system. You can change the text of each of the buttons with the use of the CancelButtonText, FinishStepButtonText, FinishStepPreviousButtonText, NextStepButtonText, PreviousStepButtonText, and StartStepNextButtonText attributes.
Utilizing Wizard control events
One of the most convenient capabilities of the Wizard control is that it enables you to divide large forms into logical pieces. The end user can then work step by step through each section of the form. The developer, dealing with the inputted values of the form, has a few options because of the various events that are available in the Wizard control.
The Wizard control exposes events for each of the possible steps that an end user might take when working with the control. The following table describes each of the available events.
| Event | Description |
| ActiveViewChanged | Triggers when the end user moves from one step to the next. It doesn't matter if the step is the middle or final step in the series. This event simply covers each step change generically. |
| CancelButtonClick | Triggers when the end user clicks the Cancel button in the navigation system. |
| FinishButtonClick | Triggers when the end user clicks the Finish button in the navigation system. |
| NextButtonClick | Triggers when the end user clicks the Next button in the navigation system. |
| PreviousButtonClick | Triggers when the end user clicks the Previous button in the navigation system. |
| SideBarButtonClick | Triggers when the end user clicks one of the links contained within the sidebar navigation of the Wizard control. |
By working with these events, you can create a multistepped form that saves all the end users' input information when they change from one step to the next. You can also use the FinishButtonClick event and save everything that was stored in each of the steps at the end of the process. The nice thing about the Wizard control is that it remembers all the end user's input in each of the steps by means of the view state in the page. This enables you to work with all these values in the last step. This also gives the end user the capability to work back to previous steps and change values before these values are saved to a data store of some kind.
The event appears in your code-behind or inline code as shown in Listing 11.
Listing 11: The FinishButtonClick event
VB
<script runat="server">
Sub Wizard1_FinishButtonClick(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.WizardNavigationEventArgs)
End Sub
</script>
C#
<script runat="server">
void Wizard1_FinishButtonClick(object sender, WizardNavigationEventArgs e)
{
}
</script> The main <asp:Wizard> element should also have the attribute shown in Listing 12 added to point at the new event.
Listing 12: The <asp:Wizard> element changes
<asp:Wizard ID="Wizard1" Runat="server" SideBarEnabled="true" ActiveStepIndex="0"
OnFinishButtonClick="Wizard1_FinishButtonClick">
DynamicImage Server Control
Before the DynamicImage control came along, the only way to work with images in ASP.NET was to point to actual physical image files using either the <asp:Image> element or a <img> HTML element. The physical images in ASP.NET 1.0/1.1 weren't always interpreted that well to other devices (especially smaller devices). It was also rather difficult and cumbersome to deal with images that were contained in a stream or received as a byte array.
ASP.NET 2.0 now includes a new .axd HttpHandler that is specifically developed for working with images not typically stored on disk.
Working with images from disk
Although the DynamicImage control is great for working with images that are in a stream, you can also use this control and simply point to images stored as a file on the server. This is illustrated in Listing 13.
Listing 13: Using the DynamicImage control with an image from disk
<%@ Page Language="VB"%>
<html >
<head runat="server">
<title>DynamicImage Server Control</title>
</head>
<body>
<form id="form1" runat="server">
<asp:DynamicImage ID="Dynamicimage1" Runat="server"
ImageFile="wrox_logo.gif">
</asp:DynamicImage>
</form>
</body>
</html> The DynamicImage control uses the ImageFile attribute, which points to a Wrox logo that sits on a hard drive and displays this image in the browser. If you look at the properties of the picture in the browser, you see that the image displayed is exactly the same as the image stored on disk. The browser is quite capable of displaying .gif files without any difficulty. Without this control, if you try to pull open this page in a cell phone browser that understands only WML files, you have a problem. WML browsers do not understand .gif images and cannot display them. This isn't a problem for the new DynamicImage control because it automatically converts the image to a format that the consuming browser understands. Therefore, if you pull up the page from Listing 13 in a phone browser and compare it to the same image invoked in a typical browser, the two look like Figure 12.
.gif)
Figure 12
In this example, the image shown in Microsoft Internet Explorer is a typical .gif image, whereas the image shown in the phone is a MIME-type image/vnd.wap.wbmp being automatically converted by the .axd HttpHandler. This feature alone makes it quite beneficial to work with the DynamicImage control.
Resizing images
Another outstanding feature of the new DynamicImage control is that, because it deals with the images as a stream, the control can perform modifications on the images before sending them onward to the container. For example, it can make larger images smaller.
As an example, I use a large image of my kids taken last winter that measures 1632 x 1224. This size is too large to display in the browser in a meaningful fashion. Therefore, I can use the DynamicImage control to manage the image's display size. Listing 14 shows how I can make the image smaller.
Listing 14: Resizing an image
<asp:DynamicImage ID="Dynamicimage1" Runat="server"
ImageFile="kids.jpg" Width="300">
</asp:DynamicImage>
To change the size of an image with the DynamicImage control, you use the Width or Height attributes of the <asp:DynamicImage> element. The image is automatically converted to the appropriate size. In this case, I use the Width attribute and give it a value of 300, which means that the width of the image is set to 300 pixels. No Height attribute is specified because the height of the image is changed along with the width of the image. The DynamicImage control constrains the proportions of the image in order to keep it set to a realistic factor. Remember that the large image is not actually compressed to a width of 300 pixels, but the image is redrawn so that it is only 300 pixels wide. This has huge ramifications, especially for the performance for your ASP.NET pages. My resized image is shown in Figure 13.
.gif)
Figure 13
Now that you can resize images on the fly, you can see that it is relatively easy to create dynamic thumbnails of your images to display in the browser.
Displaying images from streams
In the Web world, you can utilize a lot of images in your ASP.NET pages, but they come at you in a nontypical format. For instance, if you retrieve an image sent from an XML Web service, invariably you receive this image as a byte array. Getting an image as a byte array was always a tricky procedure in ASP.NET 1.0/1.1. You had to save the file to disk before you could use it on your page. Now, with the use of the DynamicImage control, you can retrieve images that come in this format and place them directly on your page without saving them to disk first. You can also create dynamic images using technologies such as GDI+ and write these images directly to the browser. Listing 15 shows you how to use GDI+ to display a dynamically created image directly in the browser.
Listing 15: A GDI+ image displayed with a DynamicImage control
VB
<%@ Page Language="VB"%>
<%@ Import Namespace="System.Drawing" %>
<script runat="server">
Public Function DrawText() As Bitmap
Dim myBitMap As Bitmap = New Bitmap(300, 300)
Dim myDraw As Graphics = Graphics.FromImage(myBitMap)
myDraw.DrawString("This is actually an image.", _
New Font("Arial", 12), Brushes.Black, 10, 10)
myDraw.DrawLine(New Pen(Color.Black), 2, 2, 10, 10)
myDraw.DrawLine(New Pen(Color.Black), 100, 100, 40, 40)
Return myBitMap
End Function
Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dynamicimage1.Image = DrawText()
End Sub
</script>
<html >
<head runat="server">
<title>GDI+</title>
</head>
<body>
<form id="form1" runat="server">
<asp:DynamicImage ID="Dynamicimage1" Runat="server">
</asp:DynamicImage>
</form>
</body>
</html>
C#
<%@ Page Language="C#"%>
<%@ Import Namespace="System.Drawing" %>
<script runat="server">
public Bitmap DrawText()
{
Bitmap myBitMap = new Bitmap(300, 300);
Graphics myDraw = Graphics.FromImage(myBitMap);
myDraw.DrawString("This is actually an image.",
new Font("Arial", 12), Brushes.Black, 10, 10);
myDraw.DrawLine(new Pen(Color.Black), 2, 2, 10, 10);
myDraw.DrawLine(new Pen(Color.Black), 100, 100, 40, 40);
return myBitMap;
}
void Page_Load(object sender, EventArgs e)
{
Dynamicimage1.Image = DrawText();
}
</script> From this example, you can see that a DrawText method performs some pretty non-artistic tasks in the creation of an image. In the Page_Load event, you use DynamicImage control's Image property and assign it the invocation of the DrawText method. As simply as that you create the image dynamically, which is then displayed in the browser (see Figure 14).
.gif)
Figure 14
ImageMap Server Control
Another image control that is new to ASP.NET 2.0 is the ImageMap server control. This control enables you to turn an image into a navigation menu. In the past, many developers would break an image into multiple pieces and put them together again in a table that reassembled the pieces into one image. In this way, when the end user clicks a particular piece of the overall image, the application can pick out which piece of the image was chosen and base its actions upon that particular selection.
With the new ImageMap control, you can take a single image and specify particular hotspots of the image using coordinates. An example is shown in Listing 16.
Listing 16: Specifying sections of the image that are clickable
VB
<%@ Page Language="VB"%>
<script runat="server">
Sub Imagemap1_Click(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.ImageMapEventArgs)
Response.Write("You selected: " & e.PostBackValue)
End Sub
</script>
<html >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ImageMap ID="Imagemap1" Runat="server" ImageUrl="kids.jpg"
Width=300 OnClick="Imagemap1_Click" HotSpotMode="PostBack">
<asp:RectangleHotSpot Top="0" Bottom="225" Left="0" Right="150"
AlternateText="Henri" PostBackValue="Henri">
</asp:RectangleHotSpot>
<asp:RectangleHotSpot Top="0" Bottom="225" Left="151" Right="300"
AlternateText="Sofia" PostBackValue="Sofia">
</asp:RectangleHotSpot>
</asp:ImageMap>
</form>
</body>
</html>
C#
<%@ page language="C#"%>
<script runat="server">
void Imagemap1_Click(object sender,
System.Web.UI.WebControls.ImageMapEventArgs e) {
Response.Write("You selected: " + e.PostBackValue);
}
</script> This page brings up an image of my two kids. If you click the left side of the image, you select Henri, and if you click the right side of the image, you select Sofia. You know which child you selected through a Response.Write statement, as shown in Figure 15.
.gif)
Figure 15
The ImageMap control enables you to specify hotspots in a couple of different ways. From the example in Listing 16, you can see that these hotspots are placed in a rectangular fashion using the <asp:RectangleHotSpot> element. This control takes the Top, Bottom, Left, and Right coordinates of the rectangle that is to be the hotspot. Besides the <asp:RectangleHotSpot> control, you can also use the <asp:CircleHotSpot> and the <asp:PolygonHotSpot> controls. Each control takes coordinates appropriate to its shape.
After you define the hotspots on the image, you can respond to the end user click of the hotspot in several ways. You first specify how you deal with the hotspot clicks in the root <asp:ImageMap> element with the use the HotSpotMode attribute.
The HotSpotMode attribute can take the values PostBack, Navigate, or InActive. In the previous example, the HotSpotMode value is set to PostBack—meaning that after the end user clicks the hotspot, you want to postback to the server and deal with the click at that point.
Because the HotSpotMode value is set to PostBack and you have created several hotspots, you must determine which hotspot is selected. You make this determination by giving each hotspot (<asp:RectangleHotSpot>) a postback value with the PostBackValue attribute. In the first hotspot, the example uses Henri as the value, whereas the second hotspot uses Sofia as the value.
The PostBackValue attribute is also the helper text that appears in the browser (in the yellow box) directly below the mouse cursor when the end user hovers the mouse over the hotspot.
After the end user clicks one of the hotspots from the previous example, the event procedure displays the value that was selected in a Response.Write statement.
Instead of posting back to the server, you can also navigate to an entirely different URL when a particular hotspot is selected. To accomplish this, you change the HotSpotMode attribute in the main <asp:ImageMap> element to the value Navigate. Then within the <asp:RectangleHotSpot> elements, simply use the NavigateUrl attribute and assign the location to which the end user should be directed if this particular hotspot is clicked:
<asp:ImageMap ID="Imagemap1" Runat="server" ImageUrl="kids.jpg"
HotSpotMode="Navigate">
<asp:RectangleHotSpot Top="0" Bottom="225" Left="0" Right="150"
AlternateText="Henri" NavigateUrl="HenriPage.aspx">
</asp:RectangleHotSpot>
<asp:RectangleHotSpot Top="0" Bottom="225" Left="151" Right="300"
AlternateText="Sofia" NavigateUrl="SofiaPage.aspx">
</asp:RectangleHotSpot>
</asp:ImageMap> Summary
New server controls are fun. When they were first introduced, I kept saying to myself, "this will save me a ton of time," "this will be so useful," "this is going in my next project," and so on. This chapter introduced you to some of these new controls and to the different ways you might incorporate them into your next projects.
The BulletedList control enables you to create all sorts of bulleted lists either directly from inline items or from items contained in a data store of some kind. The HiddenField control allows for server-side access to a very important HTML element that was formerly far more difficult to work with. Other controls covered include the FileUpload control, which enables you to upload files easily to the server. I also covered the MultiView and View controls for working through processes, the Wizard control for advanced process work, the DynamicImage control for working with images not found on disk, and finally the ImageMap control for creating hotspots on an image. All these controls are wonderful options to use on any of your ASP.NET pages, making it much easier to develop the functionality that your pages require.
Copyright Information
This chapter is taken from ASP.NET 2.0 Beta Preview by Bill Evjen, published by Wrox, an imprint of Wiley Publishing Inc, ISBN: 0764572865. Copyright © 2004 by Wiley Publishing, Inc. All Rights Reserved.
No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108 of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the appropriate per-copy fee to the Copyright Clearance Center, 222 Rosewood Drive, Danvers, MA 01923, (978) 750-8400, fax (978) 646-8600. Requests to the Publisher for permission should be addressed to the Legal Department, Wiley Publishing, Inc., 10475 Crosspoint Blvd., Indianapolis, IN 46256, (317) 572-3447, fax (317) 572-4355, E-Mail: brandreview@wiley.com.
Wiley, the Wiley Publishing logo, Wrox, the Wrox logo, Programmer to Programmer and related trade dress are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its affiliates, in the United States and other countries, and may not be used without written permission.
Related Books
ASP.NET 2.0 Beta Preview
About the author
Bill Evjen is an active proponent of .NET technologies and community-based learning initiatives for .NET. He is a technical director for Reuters, the international news and financial services company based in St. Louis, Missouri. Bill is the founder and executive director of the International .NET Association (INETA), which represents more than 100,000 members worldwide. Bill is also an author and speaker and has written such books as ASP.NET Professional Secrets, XML Web Services for ASP.NET, Web Services Enhancements, and the Visual Basic .NET Bible (all from Wiley).