ASP.NET AJAX - Beginning Visual Basic (2012)

Beginning Visual Basic(2012)

Chapter 10

ASP.NET AJAX

What You Will Learn in This Chapter:

· Using the UpdatePanel control to avoid page flicker

· Understanding the ScriptManager control that enables the Ajax functionality

· Using the UpdateProgress control to notify users about progress of an Ajax operation

· Creating WCF services and page methods that are accessible by your client-side script

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 10 download.

Over the past few years, Ajax has popularized itself immensely in the web development community. Although the technology that drives Ajax has been around for quite some time, it wasn't until the beginning of 2005 that it got an official name. Ajax, which stands for Asynchronous JavaScriptAnd XML, enables your client-side web pages to exchange data with the server through asynchronous calls, which means they don't block the user interface while running. Probably the most popular feature driven by Ajax is the flicker-free page that enables you to perform a postback to the server without refreshing the entire page. Note that the term Ajax doesn't really cover the underlying technology anymore. Asynchronous JavaScript is still used to make the calls, but in many situations XML as the data format has been replaced with JSON, as you see later in this chapter.

To enhance your website with Ajax features you can choose among different Ajax frameworks. In earlier versions of Visual Studio and ASP.NET, Microsoft shipped both a server-side framework as well as a client-side script library for Ajax interactions. This client-side script library—while still present in ASP.NET 4.5—is no longer the recommended solution. Instead, you're encouraged to use jQuery, which is discussed in detail in the next chapter.

The server-side part of Microsoft ASP.NET AJAX gives you a lot more than flicker-free postbacks. In addition to the controls that make flicker-free pages possible, Microsoft ASP.NET AJAX gives you a few more server controls to create rich, interactive, and responsive user interfaces.

The Correct Spelling of Ajax

You'll come across two different spellings of Ajax: using Pascal casing, or in all caps. I'll use the term Ajax when referring to the general concept, and I'll use ASP.NET AJAX when specifically referring to Microsoft's Ajax framework.

By the end of the chapter, you should have a good understanding of the various server controls that the ASP.NET AJAX Framework has to offer. You will also have a basic understanding of creating WCF Services and page methods using ASP.NET and how you can call them from client-side JavaScript code.

Introducing Ajax

In the first chapter of this book you learned how browsers interact with the server. The browser makes a request for a page using GET or POST, as you've seen in Chapter 4 and Chapter 9. The server processes that page and sends back the resulting HTML. The browser then parses that HTML and renders the page to the user, optionally downloading any external resources like images, script files, and cascading style sheets (CSS). When a user interacts with the page (for example, by clicking a button to submit a filled-in contact form) the page is posted back to the server, after which the entire page is loaded in the browser again. The left-hand side of Figure 10.1 shows a visual representation of this process.

Figure 10.1

10.1

Even though this model has been used for years to serve web pages, it has a few big drawbacks. First, because the entire page is loaded after a postback, the HTML sent to the browser is much larger than it needs to be. Think back to the contact form you created in the previous chapter. Right after the user has submitted the contact form, the server shows a Label control with the text Message Sent. It does that by fully loading a new page that hides the form controls and shows the message. Even though the rest of the page hasn't changed (the menu, the sidebar, the footer, and so on), they are still sent from the server to the client. Ideally, you would only want to send back the HTML that has changed. In the case of the contact form, that could be as little as the text Message Sent. The right-hand side of Figure 10.1 shows how this works. Rather than sending the entire page as a response, the server sends a partial response (containing little more than the text Message Sent), which is then used by the browser to update just the part of the page that has changed, leaving the rest of the page as it was.

The second drawback of a full page reload has to do with the way the browser renders the page. Because the entire page is replaced, the browser has to dismiss the old one and then draw the new one. This causes the page to “flicker,” which results in an unattractive user experience. You can deploy Ajax techniques to overcome these two problems, as you see in the remainder of this chapter.

The concepts behind Ajax have been around for many years. Browsers since Internet Explorer 5 have shipped with the XMLHttpRequest object that enabled you to make calls to the server from JavaScript to send and receive data. However, people also used other techniques to emulate the behavior of what is now called Ajax, including Macromedia Flash, iframe elements, or hidden frames.

However, when the term Ajax was introduced, things really took off. In an attempt to stay ahead of the curve, Microsoft started building ASP.NET AJAX, the Ajax framework that is now fully integrated in ASP.NET and Visual Studio 2012. This framework offers a number of benefits that you as a web developer can take advantage of to create responsive applications.

In particular, ASP.NET AJAX enables you to:

· Create flicker-free pages that enable you to refresh portions of the page without a full reload and without affecting other parts of the page

· Provide feedback to your users during these page refreshes

· Update sections of a page and call server-side code on a scheduled basis using a timer

· Access server-side WCF services and page methods and work with the data they return

The nice thing about ASP.NET AJAX is that it is very easy to get started with. Creating a flicker-free page is a matter of dragging and dropping a few controls from the Toolbox onto your page. When you understand the basics of the Ajax framework, you can extend your knowledge by looking at more advanced topics such as calling WCF services.

Using ASP.NET AJAX in Your Projects

ASP.NET AJAX is fully integrated in ASP.NET and VS, which means you can start using it right away. Each new ASP.NET 4.5 web project you create in VS is already Ajax-enabled. In addition, the Toolbox contains an AJAX Extensions category with a number of Ajax-related controls that you can use in your pages. Visual Studio also has great support for ASP.NET AJAX, giving you IntelliSense for the controls at the server as well as for the client-side JavaScript you'll write to interact with the client page and code running on the server.

Creating Flicker-Free Pages

To avoid full postbacks in your ASPX pages and update only part of the page, you can use the UpdatePanel server control. For this control to operate correctly, you also need a ScriptManager control. If you're going to use Ajax functionality in many of your ASPX pages, you can place theScriptManager in the master page, so it's available in all pages that are based on this master. You can have only one ScriptManager per page, so if you add one to a master page, you can't add another one to a content page. To access a ScriptManager control that is defined in a master page from a content page, you can use the ScriptManagerProxy control as explained later. You'll find these and other Ajax-related server controls in the AJAX Extensions category of the Toolbox, shown in Figure 10.2.

Figure 10.2

10.2

The following two sections introduce you to the UpdatePanel and ScriptManager controls. After the introduction you see how to make use of these controls in the pages in your Planet Wrox website. Later sections introduce you to the UpdateProgress, Timer, and ScriptManagerProxy controls.

The UpdatePanel Control

The UpdatePanel control is a key component in creating flicker-free pages. In its most basic application, you simply wrap the control around content you want to update, add a ScriptManager to the page, and you're done. Whenever one of the controls within the UpdatePanel causes a postback to the server, only the content within that UpdatePanel is refreshed.

To see what problems the UpdatePanel control solves and how it behaves in a client page, the following Try It Out shows a simple example that uses the panel to avoid page flicker during postbacks.

Try It Out: Adding an UpdatePanel to a Page

In this exercise, you add a Label and a Button control to a page. When you click the button in the browser, the Text property of the Label is updated with the current date and time at the server. To avoid the page flicker typically associated with postbacks, you then wrap the controls in anUpdatePanel to see how that control affects the behavior.

1. Open the Planet Wrox website in Visual Studio.

2. In the Demos folder, create a new Web Form called UpdatePanel.aspx using your custom template. Give the page a Title of UpdatePanel Demo.

3. Switch the new page into Design View and drag a Label control and a Button control from the Toolbox into the cpMainContent placeholder. If the ContentPlaceHolder suddenly gets as small as the Label, simply drop the Button on top of the Label. The Button is then placed before theLabel but if you now drag the Label on top of the Button again, the two change places.

4. Use the Properties Grid to clear the Text property of the Label control. To do this, right-click the Text property label in the Properties Grid and choose Reset.

5. Double-click the gray and read-only area of the page in Design View to set up a handler for its Load event and add the following code to the handler that VS added for you:

VB.NET

Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
  Label1.Text = System.DateTime.Now.ToString()
End Sub
 

C#

protected void Page_Load(object sender, EventArgs e)
{
  Label1.Text = System.DateTime.Now.ToString();
}

6. Save all your changes and press Ctrl+F5 to open the page in your browser. The Label displays the current date and time. Click the Button control a few times. Note that each time you click the button, the page flickers and is then redrawn, displaying the updated date and time. Now take a look at the HTML that is used by the browser (right-click the page in the browser and choose View Source or View Page Source). Notice how the page contains a <span> element with the date and time that was sent from the server.

7. Close your browser, go back into VS, and switch the UpdatePanel.aspx page to Markup View. Make some room right before the Label control, and then type updatepanel and press Tab. VS inserts the code for an UpdatePanel and a <ContentTemplate> for you.

8. Next, cut both the closing </ContentTemplate> and the closing </UpdatePanel> tags and paste them below the button you created in step 3. You should end up with this markup:

<asp:UpdatePanel runat="server">
  <ContentTemplate>
    <asp:Label ID="Label1" runat="server"></asp:Label>
    <asp:Button ID="Button1" runat="server" Text="Button" />
  </ContentTemplate>
</asp:UpdatePanel>

9. Right before the opening tag of the UpdatePanel, drag a ScriptManager from the AJAX Extensions category of the Toolbox. Alternatively, type sm followed by the Tab key to insert the ScriptManager using a code snippet. Your code should look similar to this (although yourScriptManager may lack the ID attribute and may use a self-closing element when you use a code snippet):

<asp:Content ID="Content2" ContentPlaceHolderID="cpMainContent" runat="Server">
  <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
  <asp:UpdatePanel ID="UpdatePanel1" runat="server">

10. Save your changes and request the page in the browser again. Click the button a few times to update the label with the current date and time. Note that there is no page flicker now, and only the label is updated on the page. If you look at the source in the browser again, you see the<span> element that contains the date and time of the very first request. The updates to the label that were added by clicking the button are not a part of the HTML source because they have been added dynamically by the ASP.NET AJAX Framework to the browser's internal HTML.

How It Works

By wrapping the content in an UpdatePanel you define a region in your page that you want to refresh without affecting the entire page. In the example, the Button control inside the UpdatePanel caused a postback and thus a refresh of just the region in which the control is defined. Rather than replacing the entire page, only the part of the page that is wrapped in the UpdatePanel is refreshed, causing a flicker-free reload of the page.

If you analyze the data that gets sent from the server to the browser (using a network analysis tool like Fiddler, which you can download from www.fiddler2.com/fiddler2/), you would see that only a limited amount of data gets sent to the client. Rather than the full page (weighing around 12KB), only the following data is sent:

1|#||4|232|updatePanel|cpMainContent_ctl00|
<span id="cpMainContent_Label1">4/9/2012 11:56:09 AM</span>
<input type="submit" name="ctl00$cpMainContent$Button1" value="Button" 
id="cpMainContent_Button1" class="MyButton" style="background-color:#7A70A4;" />
|0|hiddenField|_EVENTTARGET||0|hiddenField|_EVENTARGUMENT||0|hiddenField|_
LASTFOCUS||1624|hiddenField|_VIEWSTATE|1Atsytn51usXShfT92FZnRfhkyw76l6TfovQaG
...
Demo|184|scriptBlock|ScriptPath|/ScriptResource.axd?d=zvkqIRNUspAvS1yKeFhMb_LRgBPQ
LrZDpLmd71civkClsZ5csFf1SkT-
k1NurvxrEjhFFVa7dJqUQpcX9l3wMJNiJeY5DJdOF5sqxTU0JGDbsEuI_njxenny6ggiBtc
4vOKR16h2V2npds3RA8dURw2&t=57d51992|

Note that I cut out a big piece of content including much of the View State of the page from the middle (represented by the three dots) to save some space in this book. If you look at this response, you'll recognize the HTML for the updated Label and the Button; the two controls that have been defined within the <ContentTemplate> of the UpdatePanel control. The remaining text is used by the ASP .NET AJAX Framework to maintain page state (using the _VIEWSTATE field) and to understand where to place the response in the page. Even though a lot of data still gets sent down the wire, it's far less than the original full page of around 12KB. This results in a faster response and a better user experience.

When you looked at the source of the page in the browser in step 10 you may have noticed that the page still contained the original source, not the updated source modified by the ASP.NET AJAX Framework. This sometimes makes it difficult to build, test, and debug Ajax applications because you cannot really see what data gets sent to the browser. Fortunately, many tools are available that help with this. Besides the aforementioned Fiddler tool, you're advised to take a look at the Microsoft Internet Explorer Developer Toolbar. It ships with Internet Explorer 8 and later and is accessible through the Tools ⇒ Developer Tools menu option.

Another great tool for debugging is Firebug, which integrates nicely with the Firefox browser. You can get the tool at http://getfirebug.com. Google's Chrome has a similar tool that you can open by clicking the Wrench icon and then choosing Tools ⇒ Developer tools.

In this exercise, you used two important AJAX Extensions controls. The ScriptManager—that you placed in UpdatePanel.aspx directly in this exercise—is a requirement for most Ajax functionality in an ASPX page to operate correctly. It serves as the bridge between the client page and the Microsoft ASP.NET AJAX Framework and takes care of things like registering the correct JavaScript files that are used in the browser. The UpdatePanel is then used to define regions you want to update without reloading the entire page. You see both controls in more detail in the following sections.

A Closer Look at the UpdatePanel

The UpdatePanel and its content is the only part of the page that is updated when you click a button (as discussed in the previous exercise). This is the default behavior of an UpdatePanel, where only its inner contents are refreshed by other server controls defined within the <ContentTemplate>element. However, the UpdatePanel can do more than this, as you see in the next section.

Common UpdatePanel Properties

The following table lists some of the important properties of the UpdatePanel that enable you to influence its behavior.

Property

Description

ChildrenAsTriggers

This property determines whether controls located within the UpdatePanel can cause a refresh of the UpdatePanel. The default value is True, as you saw in the previous exercise. When you set this value to False, you have to set the UpdateMode to Conditional. Note that controls defined within the UpdatePanel still cause a postback to the server with this property set to False; they just don't update the panel automatically anymore.

Triggers

The Triggers collection contains PostBackTrigger and AsyncPostBackTrigger elements. The first is useful if you want to force a complete page refresh, whereas the latter is useful if you want to update an UpdatePanel with a control that is defined outside the panel.

RenderMode

You can set this property to Block (the default) or Inline to indicate whether the UpdatePanel renders itself as a <div> or <span> element.

UpdateMode

This property determines whether the control is always refreshed (the UpdateMode is set to Always) or only under certain conditions, for example, when one of the controls defined in the <Triggers> element is causing a postback (the UpdateMode is set to Conditional). The default for this setting is Always.

ContentTemplate

Although not visible in the Properties Grid for the UpdatePanel, the <ContentTemplate> is an important property of the UpdatePanel. It's the container in which you place controls as children of the UpdatePanel. If you forget this required ContentTemplate, VS gives you a warning.

You see more of the UpdatePanel in later exercises in this chapter.

UpdatePanel Caveats

As useful as the UpdatePanel seems (and is), its usage comes at a price. Although it appears as if only part of the page is refreshed, the entire page (and all of its form data) is still posted back to the server. At the server, the page still goes through its normal life cycle and then sends back the HTML that is needed to update the page. However, the data that is sent back isn't in a very optimal format because it contains some overhead data (required by ASP.NET AJAX to understand how to interpret it). This means that the UpdatePanel carries some overhead in terms of form posts, page processing, and network traffic. Later in this chapter, you see some ways to get data to and from the server from client-side code that minimize this overhead.

As demonstrated in the previous exercise, the UpdatePanel control is capable of refreshing parts of a page. Controls that are defined either inside the UpdatePanel or outside of it can cause a refresh of the UpdatePanel. However, in order to function, the UpdatePanel needs a ScriptManager control that manages the client-side JavaScript, among other things.

The ScriptManager Control

The ScriptManager control serves as the bridge between the client page and the server. It manages script resources (the JavaScript files used at the client), takes care of partial-page updates as shown earlier, and handles interaction with your website for things like WCF services.

You usually place the ScriptManager control directly in a content page if you think you need Ajax capabilities on only a handful of pages. You briefly saw how this worked in the previous Try It Out exercise. However, you can also place the ScriptManager in a master page so it becomes available throughout the entire site. You do this in a later exercise in this chapter.

The ScriptManager class has a number of properties, of which most are used in advanced scenarios. In many situations, like updating sections of a page using the UpdatePanel as you just saw, you don't need to change any of the properties of the ScriptManager class. In other scenarios, you may need to change or set some of its properties. The following table lists some of the more common properties of the ScriptManager control.

Property

Description

AllowCustomErrorsRedirect

This property determines whether errors that occur during an Ajax operation cause the customized error page to be loaded. The default is True; with a setting of False, the error is shown as a JavaScript alert window in the browser or is hidden from the client when debugging is disabled. Note that if you haven't configured any customized error page, the error is always shown as a JavaScript alert, regardless of the value of this setting. Chapter 18 talks more about setting up customized error pages and debugging your application.

AsyncPostBackErrorMessage

When you're not using customized error pages, this property enables you to customize the error message that users see when an Ajax error occurs. It enables you to hide the dirty details from the users and instead present them a more friendly error message.

EnablePageMethods

This property determines whether client code is allowed to call methods defined in the page. You see how this works later.

EnablePartialRendering

This property determines whether the ScriptManager supports the partial rendering of the page using UpdatePanel controls. You should leave this setting to True, unless you want to block the partial updates for the entire page.

EnableCdn

With this property set to True, ASP.NET includes links to the client-side framework files on Microsoft's Content Delivery Network, rather than on your own server. This saves you some bandwidth and speeds up the initial load of the page if the user already had a cached copy of the files from visiting another site using these files.

AjaxFrameworkMode

Determines whether the Microsoft AJAX client library is included. This setting enables you to use the ScriptManager for server-related tasks (like registering client scripts) without embedding the client-side framework in the page.

Scripts

The <Scripts> child element of the ScriptManager control enables you to add additional JavaScript files that must be downloaded by the client at run time.

CompositeScript

Just like the <Scripts> element, the <CompositeScript> element enables you to add additional JavaScript files. However, files registered under <CompositeScript> are combined into a single, downloadable file, minimizing network overhead and improving performance.

Services

The <Services> element enables you to define WCF services that are accessible by your client-side pages. You see how to use WCF services in the second half of this chapter.

Although the UpdatePanel and the ScriptManager together are all you need to create flicker-free pages, ASP.NET AJAX offers more to enhance the user's experience in an Ajax-enabled website. One way to improve the user's experience is by using the UpdateProgress control, discussed next. Another option is to use the Timer control, which is discussed later in this chapter.

Providing Feedback to Users

Despite the visual problems that postbacks usually cause, they have one big advantage: the user can see something is happening. The UpdatePanel makes this a little more difficult. Users have no visual cue that something is happening until it has happened. To tell your users to hold on for a few seconds while their request is being processed, you can use the UpdateProgress control.

The UpdateProgress Control

You connect the UpdateProgress control to an UpdatePanel using the AssociatedUpdatePanelID property. Its contents, defined in the <ProgressTemplate> element, are then displayed whenever the associated UpdatePanel is busy refreshing. You usually put text such as “Please wait" or an animated image in this template to let the user know something is happening, although any other markup is acceptable as well.

In addition to the AssociatedUpdatePanelID and <ProgressTemplate> properties, the UpdateProgress control features the following properties you typically use.

Property

Description

DisplayAfter

Determines the time in milliseconds that the control waits before it displays its contents. This is useful when the refresh period is so short that a notification message would be overkill. The default is 500 milliseconds, which is half a second.

DynamicLayout

Determines whether the control takes up screen real estate when hidden. This maps directly to the CSS display: none; (when this setting is True/true) or visibility: hidden; (when it's False/false).

In the following exercise, you see how to combine the UpdatePanel, the ScriptManager, and the UpdateProgress controls to make the contact form user control flicker-free.

Try It Out: Flicker-free Pages—Putting It All Together

In this exercise, you modify the user control ContactForm.ascx that you created earlier, wrapping the entire control in an UpdatePanel so the page doesn't perform a full postback when you enter a message and click the Send button. To help users understand that the page is busy when the message is being sent, you add an UpdateProgress panel to the control. Inside this control you place an animated GIF image that is available in the code download for this book. Alternatively, you can go to www.ajaxload.info and create your own animated image.

1. Open the ContactForm.ascx user control from the Controls folder in Markup View and wrap the entire <table> element and the Label at the bottom of the control in an UpdatePanel with a <ContentTemplate>. You can do this by typing the code directly in Markup View, by using a code snippet, or by dragging the control from the Toolbox. Make sure the ID of the UpdatePanel is set to UpdatePanel1. You should end up with the following code:

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
  <ContentTemplate>
    <table class="auto-style1" runat="server" id="FormTable">
      ....
    </table>
    <asp:Label ID="Message" runat="server" Text="Message Sent" Visible="false" />
  </ContentTemplate>
</asp:UpdatePanel>

2. Save the changes to the control and then open the Frontend.master file from the MasterPages folder. Between the opening <form> tag and the <div> for the PageWrapper, add a ScriptManager control by dragging it from the Toolbox into the source of the page. You should end up with this code:

<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <div id="PageWrapper">

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

4. Open the UpdatePanel.aspx page you created in an earlier Try It Out and remove the ScriptManager control. Because this control is now declared in the master page, you can no longer redefine it in pages that are based on that master. Save and close the page.

5. Open the Contact.aspx page from the About folder in your browser and then fill in the contact form. Note that as soon as you click the Send button, the form disappears and is replaced with the label stating that the message is sent. Just as with the earlier UpdatePanel example, you'll notice no page flicker when the page reloads and displays the text Message Sent.

6. To keep the user updated on the progress while the message is delivered to the mail server, you should add an UpdateProgress control to the page. Inside this control, you add an animated image and some text informing the user the message is being sent. To add the image, locate the folder where you extracted the files that come with this book (at C:\BegASPNET\Resources) with File Explorer (Windows Explorer on Windows 7). Open the Chapter 10 folder and then the Monochrome folder. Drag the PleaseWait.gif file from File Explorer into the Images folder of the Monochrome theme under App_Themes. Repeat this process, but now drag PleaseWait.gif from the DarkGrey folder into its respective theme's Images folder. Figure 10.3 shows how both images should end up.

Figure 10.3

10.3

7. Open the Monochrome.css file, scroll all the way down to the end, and add the following rule:

.PleaseWait
{
  height: 32px;
  width: 500px;
  background-image: url(Images/PleaseWait.gif);
  background-repeat: no-repeat;
  padding-left: 40px;
  line-height: 32px; 
}

8. Copy the exact same rule into the DarkGrey.css file for the DarkGrey theme.

9. Switch back to the ContactForm.ascx user control and below the closing tag of the UpdatePanel at the end of the file, drag an UpdateProgress control from the AJAX Extensions category of the Toolbox. Set its AssociatedUpdatePanelID to UpdatePanel1, the ID of the UpdatePaneldefined earlier in the page.

10. Between the <UpdateProgress> tags create a <ProgressTemplate> element, and within this template, create a <div> element with its class attribute set to PleaseWait, the CSS class you created in step 7. Inside the <div> element, type some text to inform your users that they should hold on for a while. You should end up with this code:

</asp:UpdatePanel>
<asp:UpdateProgress ID="UpdateProgress1" runat="server"
               AssociatedUpdatePanelID="UpdatePanel1">
  <ProgressTemplate>
    <div class="PleaseWait">
      Please Wait...
    </div>
  </ProgressTemplate>
</asp:UpdateProgress>

11. To emulate a long delay while sending out the message so you can see the UpdateProgress control, add the following line of code to the Code Behind of the control, just after the lines that change the visibility of the controls in the method that sends out the e-mail:

VB.NET

Message.Visible = True
FormTable.Visible = False
System.Threading.Thread.Sleep(5000)
 

C#

Message.Visible = true;
FormTable.Visible = false;
System.Threading.Thread.Sleep(5000);

12. Save all your changes and open the Contact.aspx page from the About folder once again. Fill in the required details and click the Send button. Shortly after you click the button, you should see the UpdateProgress control appear that displays text and an animated image below the form, shown in Figure 10.4. Shortly after that, the UpdateProgress control and the entire form should disappear and you should be presented with the Message Sent text.

Figure 10.4

10.4

COMMON MISTAKES

If you don't see the described behavior, your browser may be working with an outdated version of the CSS files. Press Ctrl+F5 or Ctrl+R to get the latest version from the server and try again. Alternatively, you can clear the browser's cache.

How It Works

With the UpdatePanel in the user control, everything that falls within the ContentTemplate of the UpdatePanel will be updated upon postback, without affecting other parts of the page. This way, you can hide the form with the server controls and replace it with the Message Sent label without causing any page flicker.

To inform the user that his or her message is being sent, you also added an UpdateProgress control to the site. By default, this control will be shown when refreshing the UpdatePanel it is attached to takes longer than 500 milliseconds (half a second). The <ProgressTemplate> element for the control contained a simple <div> element with its class set to PleaseWait. You added the following CSS rule to the two CSS files for the themes:

.PleaseWait
{
  height: 32px;
  width: 500px;
  background-image: url(Images/PleaseWait.gif);
  background-repeat: no-repeat;
  padding-left: 40px;
  line-height: 32px; 
}

This code first sets the dimensions of the Update message to be 500 pixels wide and 32 pixels high. This is enough to span the width of the content block, giving you enough room for a longer message.

The code then adds the animated image as a background image. To prevent the image from being repeated in the background, the repeat property is set to no-repeat. Then the left padding is set to 40 pixels. This moves the text in the <div> to the right, so it appears next to the animated image. Finally, the line-height of the text is set to 32 pixels, the same height as the entire <div>. This centers the entire text block vertically within the <div> element and aligns it nicely with the animated image.

Finally, you added the following line of code to the handler that sends the message:

System.Threading.Thread.Sleep(5000);

This code halts the execution of the page for 5 seconds (the number you pass to the Sleep method is expressed in milliseconds) so you can get a good look at the message in the UpdateProgress control. In production code, you should remove this line, because it slows down the page considerably without adding any value to the page.

In addition to user-triggered page updates as you saw with the Send button, you can also trigger page refreshes programmatically at a specified interval, as discussed in the following section.

Note

When you wrap server-side functionality in an UpdatePanel, it may sometimes be hard to see if an error has occurred and what the exact error message is. For example, when sending the e-mail fails, you won't see the real error message because it's hidden in the JavaScript. To make it easier to see the error message in case something goes wrong, you can temporarily remove the UpdatePanel from the page, or comment out its closing and opening tags using the server-side comments tags <%-- and --%> like this:

<%--<asp:UpdatePanel ID="up1" runat="server"><ContentTemplate>--%>
... Existing content goes here
<%--</ContentTemplate></asp:UpdatePanel>--%>

The Timer Control

The Timer control that you find in the AJAX Extensions category of the Toolbox is great for executing server-side code on a repetitive basis. For example, you can use it to update the contents of an UpdatePanel every 5 seconds. The contents of this UpdatePanel could come from a variety of sources, such as a database with the latest forum posts on a forum or news items on a news site, an XML file with information to rotate advertisements in the browser, stock quotes from a stock web service, and more.

The Timer control is pretty simple to use. At a specified interval, the control fires its Tick event. Inside an event handler for this event you can execute any code you see fit. The following code snippet shows the markup for a simple UpdatePanel and a Timer control that you can place inside a content page based on your master page (because the master page already contains the required ScriptManager):

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
  <ContentTemplate>
    <asp:Label ID="Label1" runat="server"></asp:Label>
    <asp:Timer ID="Timer1" runat="server" Interval="5000" OnTick="Timer1_Tick" />
  </ContentTemplate>
</asp:UpdatePanel>

Note

When you're using VB.NET, you don't need the OnTick handler on the Timer control because that is taken care of with the Handles keyword in the Code Behind file in that language.

When the timer "ticks" it raises its Tick event, which you can handle with the following code:

VB.NET

Protected Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
  Label1.Text = System.DateTime.Now.ToString()
End Sub
 

C#

protected void Timer1_Tick(object sender, EventArgs e)
{
  Label1.Text = System.DateTime.Now.ToString();
}

When this code is run in the browser, the label will be updated with the current date and time every 5 seconds. If you want to make it tick slower or faster, you need to adjust its Interval property, which specifies the time in milliseconds.

This scenario with an auto-updating panel and the ability to refresh the content with a button click is quite common. The auto-refreshing panel is a non-intrusive way to feed the user the most up-to-date information from the server. In addition, you could offer your users a button to force a refresh of the data at any moment they choose. From a coding perspective, you wouldn't have to change much; you would call the same code (preferably wrapped in a separate method) from the Timer's Tick event handler and from the Button's Click event handler.

For more information about the Timer control, check out the MSDN documentation at http://tinyurl.com/TimerClass4-5.

You have now seen the most important server-side controls that the ASP.NET AJAX Framework has to offer. In the remainder of this chapter, you find a discussion of WCF services and page methods in your Ajax-enabled web pages. During the discussion of web services, you see how to use the ScriptManagerProxy, the final control in the AJAX Extensions category of the Toolbox.

Using Web Services and Page Methods in Ajax Websites

The ability to call web services and page methods from an Ajax-enabled ASP.NET website is a great addition to your web development toolkit. Being able to call a web service or page method means it's now much easier to access data at the server from client-side code, giving you a great alternative to full postbacks. The next section discusses web services, and a later section digs into ASP.NET page methods.

What Are Web Services?

Web services are essentially methods that you can call over the Internet and that can optionally return data to the calling code. This makes them ideal for exchanging data between different systems. Because web services are based on solid and well-understood standards, they make it easy to exchange data between different types of platforms and systems. For example, with a web service it's possible to exchange data between an ASP.NET website running on Microsoft Windows and a PHP-based site running on Linux. But at the same time, it's also possible to exchange data between an ASP.NET or PHP website and a client browser using JavaScript.

Introducing WCF

To build web services in an ASP.NET website, you use Windows Communication Foundation (WCF), Microsoft's platform for service-oriented applications using the .NET Framework. In previous versions of ASP.NET you could also make use of so-called ASMX web services, but these have now been deprecated in favor of WCF. However, this isn't really a problem because WCF can do anything that ASMX web services could do and much more.

WCF supports a number of different underlying network communication technologies such as HTTP, .NET Remoting, Microsoft Message Queuing, and Enterprise Services. This makes it an ideal platform for the exchange of data in a variety of scenarios such as locally on a single machine, on a corporate network, or over the Internet. For public-facing websites such as the Planet Wrox site, HTTP or HTTPS (the secured version of HTTP) is the natural choice because it will work cross-browser and across firewalls.

For information about the other supported technologies, check out this article on the MSDN website at http://msdn.microsoft.com/library/dd943056.aspx or get a copy of Professional WCF 4: Windows Communication Foundation with .NET 4 (Wrox, ISBN: 978-0-470-56314-4).

To build a WCF web service, you add a WCF service (with an .svc extension) to your project. As you see later, you have a few different templates available, each serving a different purpose. Inside this service file you define a Service Contract and an Operation Contract. The Service Contract defines the overall service and the Operation Contract defines the various methods that are available on the service. The following snippet shows a simple WCF service with a single method:

VB.NET

<ServiceContract(Namespace:="")>
<AspNetCompatibilityRequirements(
       RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>
Public Class NameService
  <OperationContract()>
  Public Function HelloWorld(name As String) As String
    Return String.Format("Hello {0}", name)
  End Function
End Class
 

C#

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(
      RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class NameService
{
  [OperationContract]
  public string HelloWorld(string name)
  {
    return string.Format("Hello {0}", name);
  }
}

You define the Service Contract by applying a ServiceContract attribute to your class. An attribute is like a little tag or label that you can stick on code elements, like classes, methods, properties, and so on, to mark that piece of code as something special. Other code interacting with the attributed code can then see what attributes that code contains and make decisions based on that information. Don't worry about that too much because you don't have to read those attributes yourself when working with web services. All you need to do is stick the attribute on a class or method to enable it for WCF.

In C# you use square brackets to wrap the attribute, whereas VB.NET uses angle brackets. You may also come across examples where the VB.NET attribute is followed by a space and an underscore, because previous versions of VB.NET required this. You don't need the underscore anymore, although it's perfectly valid to use it anyway.

With this attribute in place, you signal to the run time that you really want to expose this class as a WCF service.

The AspNetCompatibilityRequirements attribute that is applied to the service class determines how the WCF service behaves at run time. The Allowed value enables your service to run in what's called the ASP.NET Compatibility Mode, which runs the WCF service in a similar way ASMX services were run. When you set the value to NotAllowed, your services won't run correctly in your ASP.NET websites.

Besides the attribute on the class, each method you want to expose to the service is marked with the OperationContract attribute. This opt-in model enables you to create other methods (for example, helper methods that you call from your service methods) without exposing them to your service.

Calling Services from Client-Side Code

Calling a WCF service from a client HTML page is really simple. ASP.NET takes care of most of the hard work for you by generating the necessary JavaScript to interact with the service. All you have to do is register the service with the ScriptManager control and then call it from client-side code. Given the NameService you saw earlier, you set up the ScriptManager as follows:

<asp:ScriptManager ID="ScriptManager1" runat="server">
  <Services>
    <asp:ServiceReference Path="∼/WebServices/NameService.svc" />
  </Services>
</asp:ScriptManager>

In this example, the service file is called NameService.svc and is located in a folder called WebServices in the root of your website.

Once you set up the service, you can call the service from client-side JavaScript in an ASPX page like this:

NameService.HelloWorld('Imar', helloWorldCallback);
 
function helloWorldCallback(result)
{
  alert(result);
}

Note that this code uses a mix of camel case and Pascal case. To align with the .NET programming guidelines, the service method uses Pascal casing and is written as HelloWorld. In JavaScript it's common to write methods using camel case, and thus the callback method is written ashelloWorldCallback with a lowercase “h." I'll stick to these naming conventions throughout the chapter, which will help you determine if something is a pure client method, or a server-side method. To call a service method, you use ServiceName.MethodName. So, in the preceding example,NameService is the name of the service, and HelloWorld is the method you want to call. When you type this code in Visual Studio (and have registered the service in the ScriptManager for the master page that the content page is using), you get help from IntelliSense as shown in Figure 10.5.

Figure 10.5

10.5

Although the actual service definition has only a single parameter (the name parameter), the client side method in this example has four parameters. The first one is the name parameter that I set up in the HelloWorld service method. If your service expected more parameters, they would be listed here as well. The second parameter enables you to pass a success callback method—a method you define in your code that gets called when the service call completes successfully. In the code example, this method is called helloWorldCallback. You can name this method any way you want (as long as it's a valid name in JavaScript), but I prefer to call it serviceMethodNameCallback to clearly express for which code it serves as a callback. As a parameter, the callback method receives the value returned from the service. In this example, this result is a simple string, but you see later how you can also pass complex objects. The third parameter is also a callback method and is called when the service call somehow fails (for example, because the service encounters an error or is not available). The final parameter is called userContext and enables you to pass additional data to your callback methods. This is useful if you need additional context data to correctly process the success callback. In this example, the first parameter is required and the other three are optional. However, in most real-world scenarios you implement at least the onSuccess callback in order to work with the data returned from the service.

In this example, the code in the success callback helloWorldCallback is really simple; all it does is alert the value returned from the service. However, it doesn't have to be like this. The values you can return from the service are not limited to simple strings, as you see in the following section.

Exchanging Complex Objects with WCF

Although a simple string can sometimes be enough as the response from a service, you typically need more information. For example, you may want to load the last two reviews from a service call when a user clicks a refresh button. Rather than posting back the entire page, you could call a service, retrieve the reviews from the database, and then display them in the page somehow. Here's an example of how a service that retrieves reviews could look:

VB.NET

<ServiceContract(Namespace:="")>
<AspNetCompatibilityRequirements(
      RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>
Public Class ReviewsService
 
  <OperationContract()>
  Public Function GetLatestReviews() As List(Of Review)
    Dim temp As New List(Of Review) From
    {
      New Review() With {.Id = 1, 
       .Title = "21st Century Breakdown by Green Day"},
      New Review() With {.Id = 2, 
       .Title = "Sonic Youth: Daydream Nation live in Roundhouse, London"}
    }
    Return temp
  End Function
End Class
 
Public Class Review
  Public Property Id As Integer
  Public Property Title As String
End Class
 

C#

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(
         RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ReviewsService
{
  [OperationContract]
  public List<Review> GetLatestReviews()
  {
    List<Review> temp = new List<Review>()
    {
      new Review() {Id = 1, 
         Title = "21st Century Breakdown by Green Day"},
      new Review() {Id = 2, 
         Title = "Sonic Youth: Daydream Nation live in Roundhouse, London"}
    };
    return temp;
  }
}
 
public class Review
{
  public int Id { get; set; }
  public string Title { get; set; }
}

To show you how to work with complex data at the client, this code example returns two hard-coded Review instances. In later chapters you see how to work with reviews in a database so you could make this example truly dynamic. For now, it just serves the purpose of showing the reviews at the client.

The code sets up a generic collection of Review instances. For now, a Review is a simple class with two properties: an Id and a Title. The collection is then filled using a collection initializer that adds two Review instances, each of them created with an object initializer. Refer back to Chapter 5 for more information on collection and object initializers.

When you call the GetLatestReviews method from client code, you get back a collection of Review instances that you can loop over. The following code shows the getLatestReviewsCallback method that accomplishes this:

function getLatestReviewsCallback(result)
{
  var listItems = ‘';
  for (i = 0; i < result.length; i++)
  {
    listItems += ‘<li>’ + result[i].Title + ‘</li>';
  }
  document.getElementById('Reviews').innerHTML = listItems;
}

This code first declares a string that will hold the titles of the reviews. It then loops over the reviews in the result variable. JavaScript doesn't support foreach, but using a standard for loop you can easily access all items in the collection. Within the for loop, the review's title is retrieved usingresult[i].Title, which is then wrapped in a pair of <li> tags and appended to a string variable. In the end, the string is added as the innerHTML of an element called Reviews (which could look like this in the code: <ul id=”Reviews”></ul>) so the review titles end up in a bulleted list.

When you type this code, you'll notice you don't get IntelliSense for the result object. VS doesn't know the actual type of the result variable, and as such can't help you find properties such as Id and Title.

Note that this code is a bit clumsy. Using document.getElementById and innerHTML isn't the best way to write code like this. Therefore, the next chapter introduces you to better alternatives when it discusses jQuery. For now, though, this should suffice, showing the core concept of working with complex objects returned from a WCF service.

It's important to realize that the Reviews object you work with in JavaScript is not the exact same object as the one you use in the service. Your VB.NET or C# code targets the .NET Framework at the server, whereas your JavaScript runs at the client. To get the object to the client, WCFserializes the collection of reviews into JavaScript Object Notation (JSON)—a string representation of your objects that can be used directly in your JavaScript code. You see an example of JSON in a later exercise.

The web services in the Planet Wrox project will only be used to have a client page in the browser talk to the server and exchange data. So, in this site, both the server and the client are in the same web project—one executes at the client (the JavaScript that calls the web server), and the other lives at the server (the web service itself). From a security point of view, this is the easiest solution because both parts trust each other.

If you want your client-side pages to talk to a web service on a different domain, you could host a service on your own site that calls the remote web service. The client browser then calls your service, which in turns calls the remote service. This scenario falls outside the scope of this chapter, though.

You see this WCF theory in practice in the following exercise.

Creating Web Services

Creating WCF services with VS is pretty easy. Just as with all the other document types, VS comes with a template for a WCF service. You add the service to the site using the Add New Item dialog box. You then modify the service code to suit your requirements. Next, you register the service in aScriptManager or ScriptManagerProxy and then you're ready to call it from a client web page.

VS comes with a few different templates to create a WCF service. To create one that's callable from a website, you use the AJAX-enabled WCF Service template. When you add a service based on this template, VS adds the necessary configuration code to allow calling this service from a web page to the Web.config file for you. In addition, the coding model of this service is a bit easier than the standard WCF Service template because it stores all the code in a single class and file.

Try It Out: Creating a Web Service

In this exercise you create a simple “Hello World” web service. This service accepts your name as an input parameter and returns a friendly, personalized greeting. There's not much real-world usage for this exact web service, but because of the simplicity in the service itself, it's easy for you to focus on the underlying concepts.

1. Create a new folder called WebServices in the root of your site to group all web services in a single folder. This is not required, but helps in organizing your site.

2. Next, right-click this new folder and choose Add ⇒ Add New Item. Click the AJAX-enabled WCF Service item. Because the list of templates can be quite long, you can quickly find the right item by searching for WCF in the search box in the top-right corner of the Add New Item dialog box.

Make sure that you click the item for your programming language, and call the service NameService, as shown in Figure 10.6.

Figure 10.6

10.6

3. Click Add to add the service to the site. Notice how the .svc file is added to the WebServices folder and the Code Behind file (.vb or .cs) is placed in the site's App_Code folder shown in Figure 10.7.

Figure 10.7

10.7

4. Open the NameService Code Behind file from the App_Code folder, rename the DoWork method to HelloWorld, and change the code so it accepts a string and returns a personalized greeting. Notice that in VB you need to change the code from a Sub to a Function and have it return aString, and in C# from void to a string method, because the service method returns a string. You should end up with code like this:

VB.NET

Public Class NameService
...
  <OperationContract()>
  Public Function HelloWorld(name As String) As String
    Return String.Format("Hello {0}", name)
  End Function
End Class
 

C#

public class NameService
{
  ...
  [OperationContract]
  public string HelloWorld(string name)
  {
    return string.Format("Hello {0}", name);
  }
}

5. That's it. You just created a WCF service that can be called from client-side code. Note that if you try to request the .svc file in the browser directly, you get a screen similar to Figure 10.8.

Figure 10.8

10.8

You get this screen because by default, for security reasons, WCF services don't expose their meta data. This means they don't tell the outside world how they work and how to call them. If you want to test your WCF service you could enable it to publish its meta data by following the instructions on the service page shown in Figure 10.8. Once you've enabled the meta data, you get more information about testing the service using a WCF test client program. I usually don't do this for simple WCF services, though; it's often just as easy to call the service from a client-side page, as you see in a later Try It Out exercise.

How It Works

WCF services are essentially methods that can be called over a network, like the Internet or your local network. They are designed to enable applications to communicate and exchange data with each other. The default underlying message format for an Ajax-enabled WCF service is JSON. This is a very succinct way to exchange data. For example, the two reviews you saw in an earlier example are transferred from the server to the client using the following JSON code:

{"d":[{"_type":"Review:#","Id":1,"Title":"21st Century Breakdown 
    by Green Day"},{"_type":"Review:#","Id":2,"Title":
    "Sonic Youth: Daydream Nation live in Roundhouse, London"}]}

I split the code over multiple lines to make it more legible. In reality, this code was all placed on a single line. If you look at the comments above your service code file you see that you can also have it return XML. However, for most purposes, JSON is an excellent choice.

When you add a web service to your project, not all methods in this file become web-callable automatically. To expose a method as a service, you need to apply the OperationMethod attribute:

VB.NET

<OperationMethod()>
Public Function HelloWorld(name As String) As String
 

C#

[OperationMethod]
public string HelloWorld(string name)

With this attribute, the method is visible to the outside world, and can thus be accessed by external systems.

With the service created, the next steps are registering it with the ScriptManager and calling it from client code. These two topics are discussed next.

Configuring the ScriptManager

In an earlier section in this chapter you saw that the ScriptManager control is a required component in almost all Ajax-related operations. It registers client-side JavaScript files (those used by the Ajax framework and optionally your own), takes care of partial-page updates with the UpdatePanel, and handles interaction with the web services you have defined in your website. You can add a ScriptManager to an individual page or to the master page so it becomes available throughout your site.

When using web services, you also need to tell the ScriptManager that you want to expose your web service to client script. You have two ways to do this:

· In the ScriptManager in the master page

· In a content page that uses the web service, using the ScriptManagerProxy class

When you are going to use the web service in all or in most pages, you're best off declaring the web service in the master page's ScriptManager. You do this by giving the ScriptManager control a <Services> element that in turn contains one or more ServiceReference elements that point to your public services. For example, to make the NameService.svc service you created available in all pages in your site, you'd add the following highlighted code to the master page:

<asp:ScriptManager ID="ScriptManager1" runat="server">
  <Services>
    <asp:ServiceReference Path="∼/WebServices/NameService.svc" />
  </Services>
</asp:ScriptManager>

By referencing the service in the master page, it becomes available to all pages based on that master. This also means that each page will download the JavaScript files needed to run this service. This is a waste of bandwidth and resources if your page is not using the web service at all. So, for services that you use on only a few pages, you're better off referencing the service in the page itself. On a normal page that doesn't use a master page with a ScriptManager, you can simply add a ScriptManager to the Web Form directly. However, if you are using a master page that already has its own ScriptManager (as is the case with the pages in the Planet Wrox website), you need to use a ScriptManagerProxy control. Because you can have only one ScriptManager in a page, you can't add another one in a content page that uses your master page with the ScriptManager. Therefore, theScriptManagerProxy serves as a bridge between the content page and the ScriptManager in the master page, giving you great flexibility as to where you register your services.

When you have the ScriptManagerProxy in place, you add the exact same <Services> element to it as you saw with the ScriptManager itself:

<asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
  <Services>
    <asp:ServiceReference Path="∼/WebServices/NameService.svc" />
  </Services>
</asp:ScriptManagerProxy>

The following exercise demonstrates how to register and access your web service from client-side code using the ScriptManagerProxy.

Try It Out: Calling Web Services from Client-Side Code

In this exercise you register your web service in a ScriptManagerProxy control so it becomes available in one page only. You then write some client-side JavaScript code that accesses the service and then displays its return value.

1. First, create a page that uses your service and then registers it using a ScriptManagerProxy control. To do this, add a new Web Form in the Demos folder and call it WebServices.aspx. Make sure you base this page on your custom template, so it has the correct master page set and inherits from the BasePage class, and then give it a Title such as Web Services Demo. Once you've added the page, drag a ScriptManagerProxy control from the AJAX Extensions category of the Toolbox into the markup of the cpMainContent placeholder.

2. Within the ScriptManagerProxy element, add a <Services> element that in turn contains a ServiceReference with its Path set to the NameService you created earlier. Note that IntelliSense helps you pick the right file as soon as you type Path=" by showing you a list with files. Click Pick URL at the bottom of the list and browse to the service file in the WebServices folder. You should end up with this code in the WebServices.aspx page:

<asp:Content ID="Content2" ContentPlaceHolderID="cpMainContent" runat="Server">
  <asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
    <Services>
      <asp:ServiceReference Path="∼/WebServices/NameService.svc" />
    </Services>
  </asp:ScriptManagerProxy>
</asp:Content>

3. Right below the closing tag of the <ScriptManagerProxy>, add an Input (Text) and an Input (Button) by dragging them from the HTML category of the Toolbox. By using plain HTML elements and not ASP.NET Server Controls, you can see that the code you are going to write really executes at the client. Set the id of the text box to YourName and the id of the button to SayHello. Set the value of the button to Say Hello. You should end up with this markup:

</asp:ScriptManagerProxy>
<input id="YourName" type="text" />
<input id="SayHello" type="button" value="Say Hello" />

4. Below these two lines, add a client-side JavaScript block with the following code:

<input id="SayHello" type="button" value="Say Hello" />
<script type="text/javascript">
  function helloWorld()
  {
    var yourName = document.getElementById('YourName').value;
    NameService.HelloWorld(yourName, helloWorldCallback);
  }
 
  function helloWorldCallback(result)
  {
    alert(result);
  }
</script>

5. Add an onclick attribute to the button so that it calls the helloWorld method when you click it. Your code should look like this:

<input id="SayHello" type="button" value="Say Hello" onclick="helloWorld();" />

6. Save all your changes by pressing Ctrl+Shift+S, and then request the WebServices.aspx page in your browser. Enter your name and click the Say Hello button. If everything turned out well, you should be greeted with a message from the web service, repeating your name. Figure 10.9shows the alert window in Apple's Safari.

Figure 10.9

10.9

COMMON MISTAKES

If you get an error instead of this message box, or you see a small yellow triangle in the bottom-left corner of the screen, make sure you typed the JavaScript exactly as in the code snippet. JavaScript is case-sensitive, so make sure you get all the capitalization right. Also make sure that the JavaScript block you added in step 4 comes after the input box and button that you defined earlier. Finally, make sure that the path to your web service matches the actual path of your .svc service file.

How It Works

To expose a WCF service to the client-side script in your application, you need to register it in the Web .config file. In addition, you need to apply the correct attributes to the service class. Both actions are carried out for you when you add an AJAX-enabled WCF service to your project. If you look at the Web.config file you see something like this:

<system.serviceModel>
  <behaviors>
    <endpointBehaviors>
      <behavior name="NameServiceAspNetAjaxBehavior">
        <enableWebScript />
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
          multipleSiteBindingsEnabled="true" />
  <services>
    <service name="NameService">
      <endpoint address=""
               behaviorConfiguration="NameServiceAspNetAjaxBehavior"
        binding="webHttpBinding" contract="NameService" />
    </service>
  </services>
</system.serviceModel>

The service element defines a single service; the NameService in this case. If your site has more services available, you would add them to the same <services> element as the NameService. The binding and contract attributes tell the run time that this service is callable over HTTP and is implemented by the NameService class. The address attribute is left empty, which means the run time takes care of assigning an address. For more advanced scenarios you can enter a relative or absolute address here. The behaviorConfiguration points to a behavior calledNameServiceAspNetAjaxBehavior defined under the behaviors node. WCF separates behaviors from the actual service definition so you can reuse the same behavior across multiple services. In this case, the behavior is created to enable the service to be called by a web page through theenableWebScript element.

Once the service is created and registered in the Web.config file, you also need to register it with the ScriptManager. You could do this in the <Services> element of the ScriptManager in the master page. The downside of registering the web service in the master page is that its client JavaScript is referenced in each and every page in your site. For a service you only use once or twice, it's much better to add a ScriptManagerProxy to the specific page(s) and register the service there. Within your page, the ScriptManagerProxy control looks and acts like a normalScriptManager control. However, in reality it's just a proxy control that relays all its settings to the true ScriptManager in the master page, combining the settings for both controls. You used the ScriptManagerProxy control as follows to set up the <Services> element:

<asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
  <Services>
    <asp:ServiceReference Path="∼/WebServices/NameService.svc" />
  </Services>
</asp:ScriptManagerProxy>

All you need to do is refer to the service by setting the Path property. Just as with other server-side URLs you have seen in this book so far, you can use the tilde (∼) syntax to refer to the application's root.

Once you have registered the service, it becomes available in your client-side code. Note that IntelliSense in VS is smart enough to discover the WCF services you have defined and registered. As soon as you typed NameService followed by a dot in a client-side script block, IntelliSense kicked in again and showed the public methods it found. This makes it extremely easy to find the correct services you have defined in your site. This is a huge improvement over old versions of Visual Studio that had only a fixed number of JavaScript-related items in the IntelliSense list. Starting with Visual Studio 2008, IntelliSense is now actually able to look at your code and fill the IntelliSense list with the right variable names, methods, services, and so on that it finds in your code. In VS 2010 and VS 2012, Microsoft improved IntelliSense even further by improving the performance and the accuracy of the items shown in IntelliSense.

To see how the actual page works, and how it accesses the web service, take a look at the code in the <script> block.

The first code you need to look at is the helloWorld method:

function helloWorld()
{
  var yourName = document.getElementById('YourName').value;
  NameService.HelloWorld(yourName, helloWorldCallback);
}

First, this code gets a reference to the text box you created earlier. You then access its value property to get the name the user entered.

This name is then sent to the web service method HelloWorld with the following code:

  NameService.HelloWorld(yourName, helloWorldCallback);

The first argument of the call to HelloWorld is the argument that the web service method expects: a string holding your name. The second argument, helloWorldCallback, is a reference to another JavaScript method that is triggered when the service returns the result. By design, the call to the web service is made asynchronously. This means the call to the service is made in a separate thread and the helloWorld method exits shortly afterward. Because it can potentially take a long time for the web service to respond, you need to designate a method that is responsible for handling the response when it comes back from the service. In this case, the responsible method is called helloWorldCallback.

In addition to this success callback, you could add another one that is triggered when the web service somehow fails; for example, because the network connection is down or because the service threw an exception. In that case, the call to HelloWorld would look like this:

NameService.HelloWorld(yourName, helloWorldCallback, helloWorldErrorCallback);

The helloWorldErrorCallback function could then look like this:

function helloWorldErrorCallback(error)
{
  alert(error.get_message());
}

The error argument that is passed to this method has convenient methods and properties to display information about the exception. As in the example, you use get_message() to get at the original exception that occurred at the server. You should use this only during development to figure out the actual error. In Chapter 18 you learn more about dealing with errors at the server so the error details are never sent to the client's browser.

If everything goes according to plan, the call to helloWorld triggers the web service method HelloWorld. This method receives the name and returns a friendly welcome message.

The String.Format method takes a string that contains numeric placeholders wrapped in a pair of curly braces ({}). Then for each numeric value, you supply a string value as subsequent parameters. In the preceding example there is only one placeholder, but you can easily extend the call to the Format method with more parameters. For example, if you wanted to format a string with a first and last name, you'd use this code:

VB.NET

Return String.Format("Hello {0} {1}", firstName, lastName)
 

C#

return string.Format("Hello {0} {1}", firstName, lastName);

The String.Format method is great to make your strings much more readable. Instead of messy string concatenation using & or +, you simply define placeholders in the string, and then supply the values at run time.

Finally, the web service method returns the welcome message as a string. The web service run time then takes care of sending this return value to the calling code and then the helloWorldCallback method is invoked. This method has a result parameter that holds the return value of the web service:

function helloWorldCallback(result)
{
  alert(result);
}

In the preceding exercise the result is a simple string. This means you can use alert(result) to directly display the result in a JavaScript alert window.

In other situations, the result parameter could hold more complex objects that provide access to its properties or collections with objects, as you saw earlier.

The final thing you need to look at is how everything started in the first place. When you clicked the button, the client-side helloWorld function was triggered. This was done by adding an onclick attribute to the button that tells the browser which JavaScript method to call when you click the button. Again, better alternatives exist for the onclick attribute and the call to document.getElementById. Chapter 11, which discusses jQuery, shows you cleaner alternatives.

Obviously, the NameService you saw in this chapter has little real-world usage. However, the principles of web services you learned in this chapter are easily applied to more complex services as well, enabling you to access data on the server from client-side JavaScript with just a few lines of code.

You see the NameService again in Chapter 18 when debugging is discussed. In that chapter you step through the code line by line so you can see which code executes and in what order.

Although web services are extremely useful and pretty easy to create, they may be a bit of overkill at times. Sometimes you just need to send and receive a tiny bit of information to and from the page you're currently working with. You can do this using page methods, which are discussed next.

Introducing Page Methods

Page methods and WCF web services have a few things in common. In both cases, you can call them from the client using very little code. You can send data to them, and receive data back. Additionally, when calling them you can define success and failure callback methods. What's different is that page methods are defined directly in an existing ASPX page instead of a separate WCF service file. You can call them only from script running within that page. That makes them ideal for small, simple functionality that is limited in scope to the current page.

To enable page methods you need to set the property EnablePageMethods of the ScriptManager control to True. You cannot set this property on the ScriptManagerProxy class so you need to set it on the ScriptManager control directly, which is—in the case of the Planet Wrox website—placed in the master page. Once you have enabled page methods, setting them up and using them is a two-step process:

1. Create a public and static method (called a shared method in VB.NET) in the Code Behind of the page you're working with. You need to apply the WebMethod attribute to this method. The method can optionally receive data through its parameters and optionally return some data.

2. Write the necessary JavaScript to call the page method and work with its result.

3. You see how this works in the next exercise.

Note

A static method is a method that you call on a class, rather than on an instance of that class. In Chapter 5 you created the Calculator class that had four instance methods. This means that to use these methods, you need to create an instance of the class first using the new keyword (Newin VB.NET). If you change the methods to be static methods instead by adding the keyword static (Shared in VB.NET) after the public/Public access modifiers, you could directly call them on the Calculator class like this:

VB.NET

Dim result As Integer = Calculator.Add(4, 5)     ‘ result is now 9
 

C#

int result = Calculator.Add(4, 5);              // result is now 9

Static methods are often used for utility methods that don't require an object instance to hold its own state, but as you see next, you also need static methods if you want to implement page methods. Static methods and instance methods each serve a distinct purpose and are often not easily interchangeable. In the case of the Calculator class, which doesn't maintain state between method calls, static methods would have been a good option as well. Note that because the page methods are declared as static, you don't have access to other, instance-based members of the class. This means, for instance, that you don't have access to controls like buttons and text boxes.

Try It Out: Calling Page Methods from Client-Side Code

In this exercise, you modify the WebServices.aspx page and add a second button that calls a page method. To make it easy to compare the two techniques of calling code on the server, the page method you create is similar to the web service you created earlier.

1. Open up the Frontend.master master page in Markup View and set the EnablePageMethods attribute of the ScriptManager control to True:

<asp:ScriptManager ID="ScriptManager1" runat="server" 
        EnablePageMethods="true"></asp:ScriptManager>

2. Open the Code Behind of WebServices.aspx in the Demos folder and add the following server-side method within the Demos_WebServices class but outside the existing Page_Load method (in C#):

VB.NET

<WebMethod()> 
Public Shared Function HelloWorld(name As String) As String
  Return String.Format("Hello {0}", name)
End Function
 

C#

[WebMethod]
public static string HelloWorld(string name)
{
  return string.Format("Hello {0}", name);
}

Notice how this code is somewhat similar to what you defined in the WCF service. What's different is the inclusion of the static keyword (Shared in VB.NET) and the WebMethod attribute you apply to the method.

The WebMethod attribute won't be recognized directly. To fix this, type the following using or Imports statement at the top of the page, below the other statements:

VB.NET

Imports System.Web.Services
 

C#

using System.Web.Services;

Alternatively, click the attribute once in the code and then press Ctrl+. (Ctrl+Dot) to bring up a list with suggested options and choose the first item to have the code inserted for you.

3. Switch to Markup View and create a copy of the HTML button you created earlier, set its id to SayHelloPageMethod, set its onclick handler to helloWorldPageMethod();, and change its value to better describe what the button does. You should end up with code like this:

<input id="SayHelloPageMethod" type="button" 
      value="Say Hello with a Page Method" onclick="helloWorldPageMethod();" />

4. Implement the helloWorldPageMethod method as follows:

function helloWorldPageMethod()
{
  var yourName = document.getElementById('YourName').value;
  PageMethods.HelloWorld(yourName, helloWorldCallback);
}

You can add the method directly below the helloWorld function, in the same script block. Notice there's no need to write a new callback method to handle the return value of the call to helloWorld. You can easily reuse the one you created in the web service example because all it does is simply alert the return value.

5. Save all your changes and press Ctrl+F5 to run the page in the browser. Enter your name and click the Say Hello with a Page Method button. You should see the same message as you saw with the web service example.

COMMON MISTAKES

If you get an error about PageMethods not being defined, make sure you added the static or Shared keyword to the method's signature and make sure you set EnablePageMethods to True on the ScriptManager control in the master page.

How It Works

From a client code perspective, almost all code is identical to the web service example except for the actual call to the method. Rather than calling ServiceName.MethodName you now need to call PageMethods.MethodName, where PageMethods is a fixed name to refer to the ASP.NET AJAX JavaScript implementation to call a page method. When you click the button, the ASP.NET AJAX Framework sets up the necessary code to call the method that you defined in the Code Behind. Because the method is marked as static, the ASP.NET run time doesn't need to create an instance of the Page class (and consequently doesn't need to go through its entire life cycle) but can simply call the method. This results in a fast and efficient response from the method. However, there is one caveat you need to be aware of: because the method is static and applies to a class, rather than an instance of that class, you can't access instance members, such as the controls defined in the page, from the page method.

Page methods are ideal for sending and retrieving little bits of information that don't require a full postback or don't need the overhead of a web service. You can use them for all kinds of scenarios, including sending user data (such as a username, preferences, pages they have visited, and so on), getting up-to-date data from a database or a web service, and so on.

Practical Ajax Tips

Consider these tips to get the most out of ASP.NET AJAX:

· Because the content for an UpdateProgress panel is visible only during an Ajax page update, you'll find that it's hard to design its contents. You see the content only for a few seconds or less, and only after you cause a postback to the server. To make it easier to design an UpdateProgresspanel, you should first design the message outside of the UpdateProgress panel. For example, in the exercise from this chapter, you should move the <div id="PleaseWait"> outside any other controls so it's always visible. You can then change the HTML and the CSS for the <div> until it looks exactly right. Then you can move the <div> back into the UpdateProgress panel so it's shown only during a partial page update.

· Whenever you are using an UpdatePanel, consider adding an associated UpdateProgress control as well. Even if you don't see the need because the UpdatePanel refreshes really fast, it may be worth adding the UpdateProgress for people on slow computers or slow networks. Or better yet: add an UpdateProgress to the master page in a convenient and visible area of the page (in the Footer section, for example). Don't set AssociatedUpdatePanelID to anything so the progress panel will show on any Ajax callback. This way, you don't need lots of different waiting indicators in different areas of your site.

· Don't overuse UpdatePanel controls. In many situations, the perceived performance of an application increases when using UpdatePanel controls even if the true performance is the same. This is a good thing, because your users think your application is faster than without an UpdatePanel. However, using too many UpdatePanel controls may confuse your users, especially when the controls are not bound to an UpdateProgress control that tells your users something is going on. Consider web services and page methods instead, because they can decrease the overhead and the data that gets transferred over the wire.

· You may be tempted to wrap the entire contents of a master page in an UpdatePanel control to avoid page flicker when you browse from page to page. However, this won't work because browsing to a new page is a new GET request whereas the UpdatePanel control works only during postbacks.

Summary

Ajax is a broad and very interesting technology that can really add a lot of value to your site.

The UpdatePanel control enables you to create flicker-free pages in no time, whereas the ScriptManager control serves as the bridge between the server and the client and is responsible for tasks like registering the necessary client scripts. Other controls in the AJAX Extensions category of the Toolbox include the ScriptManagerProxy, the UpdateProgress, and the Timer controls.

Besides these very useful server-side controls, the ASP.NET AJAX Framework also comes with functionality that enables you to access web services and page methods in your site with just a few lines of code. Both web services and page methods can be used to exchange data with the server without blocking or fully reloading the user interface.

Although Ajax itself is a very compelling technology, it becomes even more useful in richer, data-driven scenarios. For example, using an UpdatePanel control around the records returned from a database to avoid page flicker when sorting, filtering, or paging your data greatly enhances the user's browsing experience. You learn how to work with databases in Chapter 12. With the knowledge about Ajax you gained from this chapter, you will quickly create flicker-free, database-driven web pages.

Exercises

1. The AJAX Extensions category of the Toolbox defines a ScriptManager and a ScriptManagerProxy. Explain the difference between these two controls, and explain when you should use the ScriptManager and when the ScriptManagerProxy.

2. How can you let your users know a partial page update is in progress?

3. To expose a method in your site as a web method that can be called by client-side script, you need to create a class and apply some attributes. Which class do you need to create, and which attributes do you need to apply?

4. What are the steps you need to take to expose and use a method in your page as a page method?

You can find answers to these exercises in Appendix A.

What You Learned in this Chapter

Ajax

Asynchronous JavaScript And XML, a term for a collection of techniques used to create flicker-free web pages and to interact with the server from client-side code

Attribute

A code element that can be applied to other elements such as classes and methods to change their meaning or behavior

Page method

A server-side static (Shared in VB.NET) method defined in a page that can be called from client script

ScriptManager control

A core component of the Microsoft ASP.NET AJAX Framework that takes care of managing client script files and server-side Ajax behavior

ScriptManagerProxy control

The bridge between a content page and the ScriptManager control defined in a master page

UpdatePanel control

A control that helps create flicker-free pages by only updating content defined within its <ContentTemplate> element

UpdateProgress control

A panel (a <div> or a <span>) that can be shown during the execution of an asynchronous Ajax operation

Web service method

A method that can be called over the Internet or local network by other applications