User Controls - Beginning Visual Basic (2012)

Beginning Visual Basic(2012)

Chapter 8

User Controls

What You Will Learn in This Chapter:

· What user controls are, how they look, and why they are useful

· How to create user controls

· How to consume (or use) user controls in your pages

· How you can improve the usefulness of user controls by adding coding logic to them

Wrox.com Code Downloads for this Chapter

You can find the wrox.com code downloads for this chapter on the Download Code tab at www.wrox.com/remtitle.cgi?isbn=1118311809. The code is in the Chapter 8 download.

In addition to the master pages, themes, and skins discussed in Chapter 6, ASP.NET 4.5 has another feature that enables you to create reusable and thus consistent blocks of information: user controls.

User controls enable you to group logically related content and controls together so they can be treated as a single unit in content pages, master pages, and inside other user controls. A user control is actually a sort of mini-ASPX page in that it has a markup section and, optionally, a Code Behind file in which you can write code for the control. Working with a user control is very similar to working with normal ASPX pages, with a few minor differences.

In versions of ASP.NET before 2.0, user controls were often used to create blocks of reusable functionality that had to appear on every page in the site. For example, to create a menu, you would create a user control and then add that control to each and every page in the site. Because of the ASP.NET support for master pages, you don't need user controls for these scenarios anymore. This makes it easier to make changes to your site's structure. Despite the advantages that master pages bring, there is still room for user controls in your ASP.NET websites. For example, you can build a user control that displays a banner that is shown on some, but not all pages as you'll see in this chapter.

By the end of this chapter, you'll have a firm understanding of what user controls are and how they work, enabling you to create functional, reusable blocks of content.

Introduction to User Controls

User controls are great for encapsulating markup, controls, and code that you need repeatedly throughout your site. To some extent, they look a bit like server controls in that they can contain programming logic and presentation that you can reuse in your pages. However, rather than dragging existing ones from the VS Toolbox, you need to create your own user controls and then add them to your ASPX pages, as you learn how to do later in this chapter.

Though master pages enable you to create content that is displayed in all pages in your site, it's common to have content that should appear only on some, but not all, pages. For example, you may want to display a banner on a few popular pages, but not on the homepage or other common pages. Without user controls, you would add the code for the banner (an image, a link, and so on) to each page that needs it. When you want to update the banner (if you want to use a new image or link), you need to make changes to all pages that use it. If you move the banner to a user control and use that control in your content pages instead, all you need to change is the user control and the pages that use it pick up the change automatically. This gives you a flexible way to create reusable content.

User controls have the following similarities with normal ASPX pages:

· They have a markup section where you can add standard markup, server controls, and plain HTML.

· They can be created and designed with Visual Studio in Markup, Design, and Split View.

· They can contain programming logic, either inline or with a Code Behind file.

· They give you access to page-based information like Request.QueryString.

· They raise some (but not all) of the events that the Page class raises, including Init, Load, and PreRender.

You should also be aware of a few differences. User controls have an .ascx extension instead of the regular .aspx extension. In addition, user controls cannot be requested in the browser directly. Therefore, you can't link to them. The only way to use a user control in your site is by adding it to a content or master page or another user control (which eventually should be added to a page).

In the remainder of this chapter, you see how to create a user control that is capable of displaying banners. The user control can present itself as a horizontal or vertical banner to accommodate for differently sized regions in your pages. In the next section, you see how to create a user control. The sections that follow show you how to use that control in an ASPX page.

Creating User Controls

You add user controls to the site like any other content type: through the Add New Item dialog box. Similar to pages, you get the option to choose the programming language and whether you want to place the code in a separate Code Behind file. Figure 8.1 shows the Add New Item dialog box for a user control.

Figure 8.1

8.1

Once you add a user control to the site, it is opened in the Document Window automatically. The first thing you may notice is that a user control doesn't have an @ Page directive, but rather an @ Control directive, as shown in this example that uses Code Behind:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="WebUserControl.ascx.cs" 
      Inherits="WebUserControl" %>

This marks the file as a user control, so the ASP.NET run time knows how to deal with it. Other than that, the directive is similar to a standard ASPX page that doesn't use a master page.

With the user control open in the VS Document Window, you can use all the tools you have used in the previous seven chapters to create pages. You can use the Toolbox to drag controls in Markup and Design View, the CSS windows to change the look and feel and content of the user control, and the Properties Grid to change the properties of controls in your user controls. You can also write code that reacts to the events that the control raises.

To try this out yourself, the next exercise shows you how to create your first user control. In a later exercise, you see how to use the control in ASPX pages in your site.

Try It Out: Creating a User Control

In this exercise, you create a basic user control that displays a single vertical banner using an Image control. In later exercises, you see how to use this control in your pages and how to add another (horizontal) image.

For this exercise, you need two images that represent banners—one in portrait mode with dimensions of roughly 120 x 240 pixels, and one in landscape mode with a size of around 486 x 60 pixels. The Resources folder for this chapter's code download that comes with this book has these two images, but you could also create your own. Don't worry about the exact size of the images; as long as they are close to these dimensions, you should be fine.

1. Open the Planet Wrox site in VS.

2. If you haven't done so already, create a new folder called Controls in the root of the site. Although user controls can be placed anywhere in the site hierarchy, placing them in a separate folder makes them easier to find and manage.

3. Create another folder called Images at the root of the site.

4. Using File Explorer (Windows Explorer on Windows 7), open up the Resources folder for this chapter (at C:\BegASPNET\Resources\Chapter 08 if you followed the instructions in the introduction of this book). If you haven't done so already, you can download the necessary resources from www.wrox.com. Drag (or copy and paste) the files Banner120x240.gif and Banner486x60.gif from File Explorer into the Images folder you created in step 3. If you're using your own images, drag them into the Images folder as well and give them the same names.

5. Right-click the Controls folder and choose Add ⇒ Add New Item. In the dialog box that follows, choose your programming language, click Web User Control, and make sure that Place Code in Separate File is checked, as shown in Figure 8.1. Name the file Banner and then click Add to add the control to the site. Notice how VS adds the extension of .ascx for you automatically if you don't type it in. VS does this for all file types you add through the Add New Item dialog box so you don't need to type the extension yourself. Your Solution Explorer should now look like Figure 8.2.

Figure 8.2

8.2

6. Switch the user control to Design View and drag a Panel from the Standard category of the Toolbox onto the design surface. Using the Properties Grid, change the ID of the Panel to VerticalPanel.

7. From the Toolbox, drag an Image control into the Panel. Select the Image and then open the Properties Grid. Locate the ImageUrl property and click its ellipsis button, shown in Figure 8.3.

Figure 8.3

8.3

Browse to the Images folder, select the Banner120x240.gif image, and click OK to add it to the user control. Your Design View now looks like Figure 8.4.

Figure 8.4

8.4

8. Using the same Properties Grid, locate the AlternateText property and type This is a sample banner. Most browsers display the alternate text (rendered as a client-side alt attribute) only when the image cannot be displayed correctly. Some older browsers show the alternate text as the tooltip for the image when you hover your mouse over it.

9. Switch to Markup View and if your Panel control has Height and Width attributes that were added by default when you dragged it on the page, remove both of them.

10. Wrap the Image in a standard <a> element and set its href attribute to http://p2p.wrox.com. If you want, you can use the a code snippet to insert the bare link for you. To do this, type the letter a and then press Tab. VS inserts a link for you and enables you to type in the href value directly. When you then press Tab again, the content of the link is selected, which you can delete by pressing Del (the Image control will be the contents of the link). Finally, cut the closing </a> tag and move it to after the image.

11. Set the target of the anchor tag (<a>) to _blank to force the browser to open up the page in a new window when the image is clicked. When you're done, the code for the entire user control should look like the following code, except for the Language attribute that you may have set toVB and the AutoEventWireup that is False by default in VB.NET:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Banner.ascx.cs" 
       Inherits="Controls_Banner" %>
<asp:Panel ID="VerticalPanel" runat="server">
  <a href="http://p2p.wrox.com" target="_blank">
    <asp:Image ID="Image1" runat="server" AlternateText="This is a sample banner"
            ImageUrl="∼/Images/Banner120x240.gif" />
  </a>
</asp:Panel>

12. Save the changes by pressing Ctrl+S and then close the user control file by pressing Ctrl+F4.

How It Works

The design experience of user controls in the Visual Studio IDE is identical to that of pages. You can use drag and drop; the Toolbox; the Markup, Split, Design Views; and so on. This makes it easy to work with user controls because you can use all the familiar tools you also use for page development.

The control you just created displays a single image wrapped in an anchor element. In the next section, you see how to add the user control to the master page so it will be displayed in the Sidebar <aside> element of every page in the site. Later sections in this chapter show you how to add the other image that you can use to display a horizontal banner in individual content pages.

Adding User Controls to a Content Page or Master Page

To use a user control in a content or master page or in another user control, you need to perform two steps. First, you need to register the control by adding an @ Register directive to the page or control where you want the user control to appear. The second step involves adding the tags for the user control to the page and optionally setting some attributes on it.

A typical @ Register directive for a user control looks like this:

<%@ Register Src="ControlName.ascx" TagName="ControlName" TagPrefix="uc1" %>

The directive contains three important attributes, described in the following table.

Attribute

Description

Src

Points to the user control you want to use. To make it easier to move pages at a later stage, you can also use the tilde (∼) syntax to point to the control from the application root.

TagName

The name for the tag that is used in the control declaration in the page. You're free to make up this name, but usually it is the same as the name of the control.

TagPrefix

Holds the prefix of the TagName that is used in the control declaration. Just as ASP.NET uses the asp prefix to refer to its controls, you need to provide a prefix for your own user controls. By default, this prefix is uc followed by a sequential number, but you can also change it to your own liking—for example, to your own company name or a custom abbreviation.

Considering the user control you created in the preceding exercise, your @ Register directive could look like this:

<%@ Register Src="∼/Controls/Banner.ascx" TagName="Banner" TagPrefix="uc1" %>

When the control is registered, you can add it to the page using the TagPrefix:TagName construct, similar to the way you add standard server controls to a page. Given the @ Register directive for the banner control, you need the following markup to add the control to your page:

<uc1:Banner ID="Banner1" runat="server" />

This is the minimum code needed for a user control in a page. Note that the control is defined by a combination of the TagPrefix and the TagName. The other two attributes—ID and runat—are standard attributes that most controls in an ASP.NET page have.

Fortunately, in most cases, you don't have to type all this code yourself. When you drag a user control from the Solution Explorer into a page in Design View, VS adds the required code for you automatically. The following exercise demonstrates how this works.

Try It Out: Adding the User Control to Your Page

In this exercise, you add the user control Banner.ascx to the master page, so it displays a banner on each page in the site in the sidebar area.

1. Open up Frontend.master from the MasterPages folder and switch it into Design View.

2. Locate the drop-down list that enables you to select a theme, position your cursor right after the drop-down list, and press Enter three times to create some room.

3. From the Solution Explorer, drag the Banner.ascx file from the Controls folder into the empty spot you just created. Design View is updated and now looks like Figure 8.5.

Figure 8.5

8.5

COMMON MISTAKES

If your Design View doesn't look like this, but looks much closer to how the file ends up in the browser, you may still have the styleSheetTheme set in the Web.config file. Also, you may have more or fewer options selected in the View ⇒ Visual Aids or View ⇒ Formatting Marks menu, which may affect your display.

4. Switch to Markup View and locate the @ Register directive at the top of the file. If the Src attribute starts with two dots, change them to a tilde (∼):

<%@ Register Src="∼/Controls/Banner.ascx" TagName="Banner" TagPrefix="uc1" %>

5. Save the changes to the master page and close it.

6. Open the Monochrome.css file from its theme folder and add the following CSS code:

img
{
  border: 0;
}

7. Copy this rule set to the other theme (add it to DarkGrey.css).

8. Save all your changes, right-click Default.aspx in the root of your site in the Solution Explorer, and choose View in Browser.

9. The banner is now displayed below the drop-down list. Switch to the other theme and you'll see the same banner appear. When you click the banner, a new window is opened that takes you to the site you linked to in the previous exercise. If you don't see the banner appear at all, check to see if you're running ad-blocking software on Windows or in your browser. For example, Firefox has an add-on called Adblock Plus that may block the banner in your web page based on its dimensions.

How It Works

When you dragged the user control onto the design surface of the master page, VS performed two tasks: first it added the @ Register directive to tell the page where to look for the user control. It then added the control declaration right below the drop-down list.

When the page loads, the ASP.NET run time sees the control declaration and injects the output of the control at the specified location. In this example, the Panel, the <a> element, and the Image are inserted in the sidebar region of the page. If you look at the HTML for the page in the browser, you see the following code:

</select>
<br /><br />
<div id="Banner1_VerticalPanel">
  <a href="http://p2p.wrox.com" target="_blank">
    <img id="Banner1_Image1" src="Images/Banner120x240.gif" 
           alt="This is a sample banner" />
  </a>
</div>

The Panel control has been transformed into an HTML <div> element and the Image control into an <img> element. Because the anchor element (<a>) was defined with plain HTML in the user control, it ends up exactly as you wrote it. The AlternateText property has been changed to analt attribute.

Notice how the id of the panel has been changed from VerticalPanel to the client ID Banner1_VerticalPanel. This is necessary to give the <div> tag a unique client-side id attribute that is used in client-side scripting. The same has happened to the id of the <img> element. You see more about this in a later section of this chapter.

Normally, when you put an <img> element inside an <a> element to link it, the browser draws a border around the image. The border is usually blue for unvisited links and purple for links you have visited before, though you could have some CSS code targeting a elements that overrides these default colors. To remove that border completely, you need to add the following CSS:

img
{
  border: none;
}

When you add a user control to a page, VS usually refers to the control using a relative path. In this exercise, this path first contained two dots (..) to indicate the parent folder, followed by the Controls folder, and finally by the name of the control:

<%@ Register Src="../Controls/Banner.ascx" TagName="Banner" TagPrefix="uc1" %>

By changing the two dots to the tilde symbol, it becomes easier to move your pages around in your site because the Src attribute now always points to the Controls folder at the application's root, no matter where the page that consumes the control is located. If your Src attribute already contained the tilde, you don't have to change anything.

Though the tilde syntax makes your pages with user controls a little easier to manage, there is an even easier way to register your user controls sitewide.

Sitewide Registration of User Controls

If you have a control that you expect to use quite often on separate content pages in your site, like the banner in the previous examples, you can register the control globally in the Web.config file. This way it becomes available throughout the entire site, without the need to register it on every page. The following exercise shows how to do this.

Try It Out: Registering User Controls in the Web.config File

In this exercise, you register the Banner.ascx user control in the Web.config file. You can then remove the @ Register directive from the master page because it isn't needed anymore. After you have changed the Web.config file, adding the same user control to other pages will no longer add the @ Register directive to the page.

1. Open the Web.config file from the root of the site. If you're familiar with versions of ASP.NET prior to .NET 4, you may find the Web.config worryingly empty. But don't worry; the functionality it included has not been removed from .NET. The configuration information placed in the website's Web.config file has now been moved to the central Web.config and Machine.config files that apply to the entire machine instead. This gives you a much cleaner configuration file, making it easy to focus on your own stuff that you put in there.

2. Locate the <pages/> element that you used in Chapter 6 to apply the theme, and within its tags add the following bolded code that contains a <controls> element with a child <add/> element. Note: you may need to change the self-closing tag of the <pages> element to a full closing tag.

<pages theme="Monochrome">
  <controls>
    <add tagPrefix="Wrox" tagName="Banner" src="∼/Controls/Banner.ascx" />
  </controls>
</pages>

3. Save the changes and close the file.

4. Open the master page again in Markup View and locate the Banner control in the sidebar area. Change uc1 to Wrox:

<Wrox:Banner ID="Banner1" runat="server" />

If the declaration for your user control has its own closing tag, VS updates the closing tag for you automatically:

<Wrox:Banner ID="Banner1" runat="server"></Wrox:Banner>

5. Scroll all the way up in the master page file and remove the entire line with the @ Register directive.

6. Save and close the master page.

7. Open Default.aspx again in your browser. Note that the banner is still present in the sidebar area.

COMMON MISTAKES

If you get an error, verify that you added the correct code at the right location to the Web.config file. Also, make sure that you changed uc1 to Wrox in the control declaration and that you deleted the @ Register directive from the master page.

How It Works

Without the @ Register directive in the master page, the ASP.NET run time scans the Web.config file for controls that have been registered there. It then finds the registration of the Wrox:Banner control so it is able to successfully find the file using the src attribute and add its contents to the page hierarchy.

With the control registration added to the Web.config file, it's much easier to move or rename the control. Instead of finding and replacing the @ Register directive in all pages that use it, the only thing you need to change is the registration in the Web.config file. When you make a change there, all pages using the control will automatically find the new location or name of the user control.

Useful as user controls are, they have a few caveats that you need to be aware of.

User Control Caveats

Earlier you saw that the ID of the Panel control you added to the page was modified by the ASP.NET run time. Instead of getting a <div> element with its id set to VerticalPanel, you got the following id:

<div id="Banner1_VerticalPanel">
  ...
</div>

In many cases, this isn't problematic because you often don't need the client id. However, if you need to access this control from client-side JavaScript or CSS, it's important to understand why this id is modified.

Understanding and Managing Client IDs

By design, all elements in an HTML page need to be unique. Giving them an id attribute is not required, but if you do so, you have to make sure they are unique. To avoid conflicts in the final HTML code of the page, ASP.NET ensures that each server-side element gets a unique client id by prefixing them with the name of their naming container. Within a naming container all elements should have unique IDs. VS warns you when you try to add a control that doesn't have a unique server ID. For example, you get an error when you try to add a second panel with an ID ofVerticalPanel to the user control. But what if you place two Banner controls in the same page, or one in the master page and another in a content page? Potentially, you could end up with two client <div> elements with an ID of VerticalPanel. To avoid this problem, ASP.NET prefixes each element with the ID of its nearest naming container. For the Panel inside the user control it means it's prefixed with Banner1, the server-side ID of the user control in the master page.

You can use the ClientID of a control to get its full client-side id. The following snippet shows how to display the ClientID of the Panel control on a fictitious Label control within the Banner.ascx user control:

VB.NET

Label1.Text = VerticalPanel.ClientID

C#

Label1.Text = VerticalPanel.ClientID;

With this code, the Label control's Text property will contain Banner1_VerticalPanel, the client-side id of the Panel. You see a more practical example of using ClientID in the next chapter.

With an explicit ID, it's easier to predict the final id of a client-side HTML element, which in turn makes it easier to reference those elements in JavaScript or CSS.

Note

Because the ASP.NET run time can change the client id attributes of your HTML elements, you may have trouble using CSS ID selectors to refer to elements. The easiest way to fix this is to use Class selectors instead of ID selectors. Alternatively, you can use the control's ClientIDwhen using embedded style sheets.

ASP.NET 4 introduced a new option to influence the client ID: the ClientIDMode.

Introducing ClientIDMode

Starting with ASP.NET 4, each web control now has a ClientIDMode property that enables you to determine the way the client ID is made up. You can set the ClientIDMode to any of these four values:

Value

Description

AutoID

Generates the ID as it did in previous versions of ASP.NET.

Inherit

With this setting the control inherits its ClientIDMode value from its parent control.

Predictable

This value is mostly used for data-bound controls (discussed in Chapter 13 and later) and enables you to create predictable client IDs for repeating elements. The client ID of a control is generated by concatenating the client ID of the parent control and the server ID of the control itself. The ID can optionally be extended with a unique value for each element using theClientIDRowSuffix property.

Static

With this value the client ID will be exactly the same as the server ID that you set. This enables you to set the client ID explicitly, giving you greater control. However, it doesn't prevent you from assigning the same value twice, which may result in duplicate IDs in the client's HTML. Use with care!

The default value of ClientIDMode for the Page class is Predictable and the default value for a control is Inherit. This in turn means that the default ID generation mode for controls is effectively Predictable as they inherit that setting from Page. If you use Visual Studio to convert a Web project to ASP.NET 4.5 from an earlier version, Visual Studio automatically sets the site default to AutoID in the <pages> element in the Web.config file to maintain backwards compatibility like this:

<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>

Currently, the Planet Wrox website doesn't benefit a whole lot from changing the client IDs for any of the existing controls, so there's no need to change any of them right now. However, in later chapters, you use the ClientIDMode property again to create cleaner client IDs.

Although the current banner user control makes it easy to display a banner at various locations in your site, it isn't very smart yet. All it can do is display a linked image and that's it. To improve its usability, you can add behavior to the control so it can behave differently on different pages in your site.

Adding Logic to Your User Controls

Although using controls for repeating content is already quite useful, they become even more useful when you add custom logic to them. By adding public properties or methods to a user control, you can influence its behavior at run time. When you add a property to a user control, it becomes available automatically in IntelliSense and in the Properties Grid for the control in the page you're working with, making it easy to change the behavior from another file like a page.

To add properties or methods to a user control, you add them to the Code Behind of the control. The properties you add can take various forms. In its simplest form, a property looks exactly like the properties you saw in Chapter 5. For more advanced scenarios you need to add View Stateproperties which are able to maintain their state across postbacks. In the next two exercises you see how to create both types of properties.

Creating Your Own Data Types for Properties

To make the banner control more useful, you can add a second image to it that displays a horizontal banner. You could also add a property to the control that determines whether to display the vertical or horizontal image. You could do this by creating a numeric property of type System.Byte. Then 0would be vertical and 1 would be horizontal, for example. However, this makes it hard to remember what each number represents. You can make it a bit easier by creating a String property that accepts the values Horizontal and Vertical. However, strings cannot be checked at compile time, so you may end up with a spelling mistake, resulting in an error or in the incorrect banner being displayed. The .NET Framework supplies a nice way to solve this by enabling you to create your own data type in the form of an enumeration. With an enumeration, or enum for short, you assign numbers to human-friendly text strings. Developers then use this readable text, while under the hood the numeric value is used. The following snippet shows a basic example of an enum (notice how the VB example doesn't use commas, whereas they are required in C#):

VB.NET

Public Enum Direction
  Horizontal
  Vertical
End Enum

C#

public enum Direction
{
  Horizontal,
  Vertical
}

With these enums, the compiler assigns numeric values to the Horizontal and Vertical members automatically, starting with 0 and counting upward. You can also define numeric values explicitly if you want:

VB.NET

Public Enum Direction
  Horizontal = 0
  Vertical = 1
End Enum

C#

public enum Direction
{
  Horizontal = 0,
  Vertical = 1
}

The cool thing about enums is that you will get IntelliSense in code files, in the Properties Grid, and even in the code editor for your user controls. Figure 8.6 shows how IntelliSense kicks in for a VB.NET code file.

Figure 8.6

8.6

In Figure 8.7 you see the same list with values from the enum in the Properties Grid.

Figure 8.7

8.7

And in Figure 8.8 you see the same list appear for a property of a user control in Markup View.

Figure 8.8

8.8

Just as with other code files like classes, you should put your enums in a file under the App_Code folder. If you have more than one of them, you can store them all in the same file or create a separate file for each enum.

Enums are great for simple and short lists. They help you find the right item quickly without memorizing “magic numbers" like 0 or 1, but enable you to use human-readable text strings instead.

In the next exercise, you see how to create an enum and use it in your Banner user control.

Try It Out: Creating Smarter User Controls

In this exercise, you add a second banner to the user control. This banner displays as a horizontal image inside its own panel. To avoid the two banners showing up at the same time, you add a property that determines which banner to display. Pages that use the control can then define the correct banner.

1. Start by creating an enumeration that contains two members for the different directions: vertical and horizontal. To do this, right-click the App_Code folder and choose Add ⇒ Add New Item. Choose your programming language and add a class file called Direction.

2. Once the file opens, clear its contents and add the following code to it:

VB.NET

 Public Enum Direction
   Horizontal
   Vertical
 End Enum
 

C#

 public enum Direction
 {
   Horizontal,
   Vertical
 }

3. Save and close the file.

4. Open the Code Behind for the user control Banner.ascx and add the following property. To help you create properties, VS comes with a handy code snippet. To activate the snippet in C#, type prop and then press Tab twice. VS adds the code structure for an automatic property for you. For VB.NET, you can type Property and then press Tab. However, this creates a full property rather than an automatic property (which is what is used in this exercise), so you're better off typing in the code manually in this case. Once the code is inserted, you can press Tab again to move from field to field, each time typing the right data type or property name. Complete the code so it looks like this:

VB.NET

 Public Property DisplayDirection As Direction
 

C#

 public Direction DisplayDirection { get; set; }

COMMON MISTAKES

Make sure you add the property outside the Page_Load method (when you're working with C#) but before the closing End Class (in VB.NET) or the closing curly brace for the class (in C#).

Note that the name of the property is DisplayDirection and its data type is Direction, the enum you defined earlier.

5. Open the Markup View of Banner.ascx, copy the entire <asp:Panel>, and paste it right below the existing Panel. Name the control HorizontalPanel and set the ImageUrl of the image to ∼/Images/Banner486x60.gif. If you want to browse for the image instead of typing its path directly, position your cursor in Markup View after the opening quote of the ImageUrl attribute's value and press Ctrl+Space. Choosing Pick URL in the menu that appears enables you to browse for a file. Your code should now look like this:

<asp:Panel ID="HorizontalPanel" runat="server">
  <a href="http://p2p.wrox.com" target="_blank">
    <asp:Image ID="Image2" runat="server" AlternateText="This is a sample banner" 
            ImageUrl="∼/Images/Banner486x60.gif" />
  </a>
</asp:Panel>

6. Switch back to the Code Behind of the control and add the following bolded code to the Page_Load handler. In C#, the handler should already be there. In Visual Basic, you can choose (Page Events) from the left drop-down list at the top of the Document Window and Load from the right drop-down list to set up the handler, or you can double-click the user control in Design View.

VB.NET

 Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
   Select Case DisplayDirection
     Case Direction.Horizontal
       HorizontalPanel.Visible = True
       VerticalPanel.Visible = False
     Case Direction.Vertical
       VerticalPanel.Visible = True
       HorizontalPanel.Visible = False
   End Select
 End Sub
 

C#

 protected void Page_Load(object sender, EventArgs e)
 {
   switch (DisplayDirection)
   {
     case Direction.Horizontal:
       HorizontalPanel.Visible = true;
       VerticalPanel.Visible = false;
       break;
     case Direction.Vertical:
       VerticalPanel.Visible = true;
       HorizontalPanel.Visible = false;
       break;
   }
 }

7. Save and close the two files that make up the user control because you're done with them for now.

8. Open up the master page file once more in Markup View and locate the user control declaration. Right after the runat="server” attribute, add the following DisplayDirection attribute that sets the correct image type:

<Wrox:Banner ID=”Banner1" runat="server" DisplayDirection="Horizontal" />

IntelliSense will help you pick the right DisplayDirection from the list. If you don't get IntelliSense, wait a few seconds until VS has caught up with all the changes. Alternatively, save and close all open files (or restart VS), open the master page, and try again.

9. Save all changes and request Default.aspx in the browser. Note that the right sidebar area now contains the horizontal image, breaking the layout a little because the image is too wide for the sidebar. If you don't see the banner appear at all, check your system for ad-blocking software that may stop the banner image from appearing.

10. Switch back to the master page and change the DisplayDirection from Horizontal to Vertical. Save your changes and refresh the page in the browser. The sidebar should now display the vertical banner, as shown in Figure 8.9.

Figure 8.9

8.9

11. Open the AboutUs.aspx page from the About folder in Markup View. If you don't have that file, create it first. In the cpMainContent ContentPlaceHolder, add some text describing you or your organization and the reason you created the site. Switch to Design View and drop the Banner .ascx control from the Solution Explorer onto the design surface, right below the text you just added. VS detects the user control in the master page called Banner1 and assigns the ID of Banner2 to the user control you just dropped. Notice how you see four banners in Design View; two come from the Banner control in the master page and the other two come from the Banner control in the About Us page itself. At run time, two of them will be hidden.

12. Select the user control you just added in Design View, open its Properties Grid, and set the DisplayDirection to Horizontal.

13. Save all your changes and then press Ctrl+F5 to open the About Us page in your browser. In addition to the banner in the right sidebar, you should now also see the horizontal banner appear in the content area.

How It Works

The property called DisplayDirection gives your user control some extra behavior. Pages using the control should now set the DisplayDirection like this:

<Wrox:Banner ID="Banner1" runat="server" DisplayDirection="Horizontal" />

When the control instance is created by the ASP.NET run time, the value you set in the control declaration is assigned to the property DisplayDirection. If you don't assign a value, the default will be Horizontal for the current definition of the Direction enum as that's the first item in the list and has an implicit value of zero.

When the page loads, this code in the user control is executed:

VB.NET

 Select Case DisplayDirection
   Case Direction.Horizontal
     HorizontalPanel.Visible = True
     VerticalPanel.Visible = False
   Case Direction.Vertical
     VerticalPanel.Visible = True
     HorizontalPanel.Visible = False
 End Select

C#

 switch (DisplayDirection)
 {
   case Direction.Horizontal:
     HorizontalPanel.Visible = true;
     VerticalPanel.Visible = false;
     break;
   case Direction.Vertical:
     VerticalPanel.Visible = true;
     HorizontalPanel.Visible = false;
     break;
 }

This code uses a Select Case/switch block to determine which image to show. When the DisplayDirection equals Horizontal, the Visible property of HorizontalPanel is set to True and the other control is hidden. The same principle is applied to the Vertical setting.

Implementing View State Properties

In addition to the DisplayDirection, another useful property for the user control would be the URL that the banner links to. In the next section, you see how to implement this and learn how to create a View State property called NavigateUrl that is able to survive postbacks.

Try It Out: Implementing the NavigateUrl Property

To be able to set the URL that a user is taken to programmatically, you need to be able to access the anchor tag that is defined in the control's markup. To do this, you need to give it an id and a runat=”server” attribute. To be able to programmatically set the new NavigateUrl property that you'll add to the Banner control and to ensure that this property survives postbacks, you need to implement a View State property. To show you why you need a View State property, the first three steps of this exercise have you modify the About Us page so it sets the DisplayDirection of the Banner control programmatically. You'll then cause a postback so you can see that the value for the direction gets lost because it doesn't maintain its state in View State. The second part of the exercise then shows you how to implement the NavigateUrl property that is able to maintain its state.

1. Open the Code Behind of AboutUs.aspx and add the following bolded code to the Page_Load event handler. If the handler isn't there yet, switch to Design View and double-click somewhere on the gray area of the page.

VB.NET

 Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
   If Not Page.IsPostBack Then
     Banner2.DisplayDirection = Direction.Vertical
   End If
 End Sub
 

C#

 protected void Page_Load(object sender, EventArgs e)
 {
   if (!Page.IsPostBack)
   {
     Banner2.DisplayDirection = Direction.Vertical;
   }
 }

Verify that the Banner user control has an ID of Banner2 in the Markup View of the page, or update your code accordingly.

2. Switch to Design View and add a Button control by dragging it from the Standard category of the Toolbox on top of the Banner.ascx control that is placed inside the page (not the one that's defined in the master page). There's no need to write any code for the Button control's Clickevent.

3. Save the page and open it in your browser. Because of the code in Page_Load, the first time the page loads, the banner at the bottom of the screen displays the vertical banner. Now click the button so the page will reload. This time, the page displays the horizontal image. Because theDisplayDirection of the Banner control is set in Page_Load only when Page.IsPostBack is False, that setting is lost when you post back, causing the banner to revert to its default setting of Horizontal.

4. To avoid this problem with the NavigateUrl, you need to implement it as a View State property where the ViewState collection is used as the backing store to store the underlying value. That way, the value is sent to the browser and back to the server with every request. To implement the property, add the following code to the Code Behind of the Banner.ascx user control, right below the DisplayDirection property you created earlier.

Remember, you don't have to type all this code manually because the download that comes with this book contains all the code shown.

VB.NET

 Public Property NavigateUrl() As String
   Get
     Dim _navigateUrl As Object = ViewState("NavigateUrl")
     If _navigateUrl IsNot Nothing Then
       Return CType(_navigateUrl, String)
     Else
       Return "http://p2p.wrox.com"  ‘ Return a default value
     End If
   End Get
   Set(Value As String)
     ViewState("NavigateUrl") = Value
   End Set
 End Property
 

C#

 public string NavigateUrl
 {
   get
   {
     object _navigateUrl = ViewState["NavigateUrl"];
     if (_navigateUrl != null)
     {
       return (string)_navigateUrl;
     }
     else
     {
       return "http://p2p.wrox.com";  // Return a default value
     }
   }
   set
   {
     ViewState["NavigateUrl"] = value;
   }
 }

5. Switch to Markup View of the user control and add runat="server" attributes to both links. Give the link in the vertical panel an id of VerticalLink and the other an id of HorizontalLink. They should end up looking like this:

 <a href="http://p2p.wrox.com" target="_blank" runat="server" id="VerticalLink">
...
 <a href="http://p2p.wrox.com" target="_blank" runat="server" id="HorizontalLink">

6. Switch back to the Code Behind of the user control (press F7) and modify the Page_Load handler of the user control so it also sets the HRef property of the anchor element:

VB.NET

 Case Direction.Horizontal
   HorizontalPanel.Visible = True
   VerticalPanel.Visible = False
   HorizontalLink.HRef = NavigateUrl
 Case Direction.Vertical
   VerticalPanel.Visible = True
   HorizontalPanel.Visible = False
   VerticalLink.HRef = NavigateUrl
 

C#

 case Direction.Horizontal:
   HorizontalPanel.Visible = true;
   VerticalPanel.Visible = false;
   HorizontalLink.HRef = NavigateUrl;
   break;
 case Direction.Vertical:
   VerticalPanel.Visible = true;
   HorizontalPanel.Visible = false;
   VerticalLink.HRef = NavigateUrl;
   break;

7. Save the changes and go back to the Code Behind of AboutUs.aspx. Modify the code so it sets the NavigateUrl property of the Banner control to a different URL. You should overwrite the code that sets the DisplayDirection.

VB.NET

 Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
   If Not Page.IsPostBack Then
     Banner2.NavigateUrl = "http://imar.spaanjaars.com"
   End If
 End Sub
 

C#

 protected void Page_Load(object sender, EventArgs e)
 {
   if (!Page.IsPostBack)
   {
     Banner2.NavigateUrl = "http://imar.spaanjaars.com";
   }
 }

8. Save all your changes and then request the AboutUs.aspx page in your browser by pressing Ctrl+F5.

COMMON MISTAKES

Make sure you get a fresh browser window, so close any windows you may have open first.

Click the horizontal banner at the left side of the page. A new window pops up, showing the URL you set in the previous step.

9. Close this new window and click the button you added to AboutUs.aspx earlier to cause the page to post back to the server. Once the page is reloaded, click the banner image again. You are taken to the same site as in step 8. This illustrates the point that the NavigateUrl property is now able to maintain its value across postbacks, unlike the DisplayDirection property you added to the user control earlier.

How It Works

In the first three steps, you witnessed the behavior of non–View State properties. You started off by writing some code in the Page_Load event handler that sets the DisplayDirection programmatically:

VB.NET

 If Not Page.IsPostBack Then
   Banner2.DisplayDirection = Direction.Vertical
 End If

C#

 if (!Page.IsPostBack)
 {
   Banner2.DisplayDirection = Direction.Vertical;
 }

Because of the check for Page.IsPostBack, this code only fires when the page loads the first time. It doesn't fire when the page is reloaded due to a postback. When it fires, it sets the DisplayDirection property of the Banner control so the banner displays the correct image. However, as soon as the page is posted back, this value is lost and the control reverts to its default direction of Horizontal. One way to overcome this problem is to make sure the code fires both the first time and on subsequent postbacks. Removing the check for Page.IsPostBack is enough to accomplish this. However, this is not always a desired solution. Imagine you're getting the correct display direction from a database. Because fetching data from a database is a costly operation, you want to minimize the number of times you hit the database. In such scenarios, developers are likely to fetch the data only when the page loads the first time and expect it to stay around on subsequent postbacks. That is exactly what the NavigateUrl property does. You set its value once and it stays available, even if you post the page back to the server. This is accomplished with a View State property.

To see how this works, take a look at the setter of that property first:

VB.NET

 Public Property NavigateUrl() As String
   ...
   Set(Value As String)
     ViewState("NavigateUrl") = Value
   End Set
 End Property

C#

 public string NavigateUrl
 {
   ...
   set
   {
     ViewState["NavigateUrl"] = value;
   }
 }

When you assign a value to the NavigateUrl property, its value is stored in the ViewState collection. You can see the ViewState collection as a bag that enables you to store data that you can retrieve again after a postback. You identify values in View State using a unique key. In the example the key equals the name of the property so it's easy to see they belong together. Once you assign a value to a View State property, it's stored in the page in the hidden _VIEWSTATE field that you learned about in Chapter 4. This means it gets sent to the browser when the page loads and it is sent back to the server when the page is posted back again.

When the postback occurs, the code in Page_Load in the user control fires again. Just as with the initial request, the code accesses the NavigateUrl property in the Select Case/switch block:

VB.NET

 Case Direction.Horizontal
   ...
   HorizontalLink.HRef = NavigateUrl
   ...

C#

 case Direction.Horizontal:
   ...
   HorizontalLink.HRef = NavigateUrl;
   ...

The value for NavigateUrl is returned by the getter of the property:

VB.NET

 Public Property NavigateUrl() As String
   Get
     Dim _navigateUrl As Object = ViewState("NavigateUrl")
     If _navigateUrl IsNot Nothing Then
       Return CType(_navigateUrl, String)
     Else
       Return "http://p2p.wrox.com"  ‘ Return a default value
     End If
   End Get
   ...
 End Property

C#

 public string NavigateUrl
 {
   get
   {
     object _navigateUrl = ViewState["NavigateUrl"];
     if (_navigateUrl != null)
     {
       return (string)_navigateUrl;
     }
     else
     {
       return "http://p2p.wrox.com";  // Return a default value
     }
   }
   ...
 }

This code first tries to get the value from View State using ViewState("NavigateUrl") in VB.NET or ViewState["NavigateUrl"] in C#, which uses square brackets to access items in a collection. If the value that is returned is Nothing or null, the getter returns the default value for the property: http://p2p.wrox.com.

However, if the value is not Nothing, it is cast to a string using CType in VB.NET and (string) in C# and eventually returned to the calling code. At the end, the NavigateUrl returned from the View State property is assigned to the HRef property of the anchor tag again, which is then used as the URL users are taken to when they click the image.

View State Considerations

Although View State is designed to overcome the problems of maintaining state as outlined in the previous exercise, you should carefully consider whether or not you use it. The values you store in View State are sent to the browser and back to the server on every request. When you store many or large values in View State, this increases the size of the page and thus negatively impacts performance. Never store large objects like database records in View State; it's often quicker to get the data fresh from the database on each request than passing it along in the hidden View State field if the amount of data that needs to be stored is large. Also, because the View State is stored within the page and is thus transferred over the wire, you shouldn't use it to store sensitive values such as passwords.

Practical Tips on User Controls

The following list provides some practical tips on working with user controls:

· Don't overuse user controls. User controls are great for encapsulating repeating content, but they also make it a little harder to manage your site because code and logic is contained in multiple files. If you're not sure if some content will be reused in another part of the site, start by embedding it directly in the page. You can always move it to a separate user control later if the need arises.

· Keep user controls focused on a single task. Don't create a user control that is able to display five different types of unrelated content with a property that determines what to display. This makes the control difficult to maintain and use. Instead, create five lightweight controls and use them appropriately.

· When you create user controls that contain styled markup, don't hardcode style information like the CssClass for the server controls contained in the user control. Instead, consider creating separate CssClass properties on the user control, which are then used to set the CssClass of your server controls. This improves the reusability of your user control, making it easier to incorporate the control in different designs.

Summary

User controls can greatly improve the maintainability of your site. Instead of repeating the same markup and code on many different pages in your site, you encapsulate the code in a single control, which you can then use in different areas of your site.

To improve the usefulness of your controls, you can add behavior to them. It's common to create controls with properties you can set in consuming pages, enabling you to change the behavior of the control at run time. Although View State properties can solve some of the state issues you may come across, you should carefully consider whether you really need them. Because these properties add to the size of the page, they can have a negative impact on your site's performance.

You can further improve the Banner control by keeping track of the number of times each image has been clicked. The Planet Wrox site doesn't implement this, but with the knowledge you gain in the chapters about database interaction, this is easy to implement yourself.

In the next chapter you create another user control that serves as a contact form. By building the form as a user control, it's easy to ask your users for feedback from different locations in the site.

Exercises

1. In this chapter you saw how to create a standard property and a View State property. What is the main difference between the two? And what are the disadvantages of each of them?

2. Currently, the DisplayDirection property of the Banner control doesn't maintain its state across postbacks. Change the code for the property so it is able to maintain its state using the ViewState collection, similar to how NavigateUrl maintains its value.

3. What are the two main benefits of using a custom enumeration like Direction over built-in types like System.Byte or String?

You can find answers to these exercises in Appendix A.

What You Learned in this Chapter

@ Control directive

A set of instructions that marks a file as a User Control and define its behavior.

@ Register directive

Used to register user controls and point to their source inside pages, master pages, and other user controls.

AlternateText

The property that enables you to set the alt attribute on images that is shown when the image cannot be displayed.

Machine.config

The central .NET configuration file that applies to your entire system and provides defaults for your website's settings.

User Control

A block of content (stored in a file with an .ascx extension and an optional Code Behind file) that can be reused in pages, master pages, and other user controls in your site.

View State Properties

Properties at the page, master page, or user control level that store their values in View State so they can survive postbacks.

ViewState collection

The ViewState collection is the property on the Page, UserControl, and MasterPage classes that enables you to store and retrieve values using View State.