Introduction
Silverlight enables development of
the next generation of Microsoft .NET-based media experiences and rich
interactive applications (RIAs) for the Web. Silverlight is delivered as a
cross-platform and cross-browser plug-in that exposes a programming framework
and features that are a subset of the .NET Framework and Windows Presentation Foundation (WPF).
Silverlight
System Requirements
With any
web-centric technology, it’s keenly important to have compatibility with the widest
possible range of computers and devices. Although Silverlight is still
evolving, it’s clearly stated
mandate is to
“support all major browsers on Mac OS X and Windows.” Currently, Silverlight’s
cross-browser compatibility stacks up fairly well:
• Windows
computers: Silverlight works on PCs with Windows 7, Windows Vista, and
Windows XP. The minimum browser versions that Silverlight supports are Internet
Explorer 6, Firefox 1.5, and Google Chrome 4.0. Silverlight will also work in
Windows 2000, but only with Internet Explorer 6. Other browsers, such as Opera
and Safari (for Windows), aren’t currently supported.
• Mac
computers: Silverlight works on Mac computers with OS X 10.4.8 or newer,
provided they have Intel hardware (as opposed to the older PowerPC hardware).
The minimum browser versions that Silverlight supports are Firefox 2 and Safari
3.
• Linux
computers: Although Silverlight 4 doesn’t currently work on Linux, the Mono
team has created an open source Linux implementation of Silverlight 1 and
Silverlight 2, and implementations of Silverlight 3 and Silverlight 4 are
planned for the future. This project is known as Moonlight, and it’s being
developed with key support from Microsoft.
In response to the overwhelming
developer community anticipation for the arrival of Silverlight, many
developers and companies have become early adopters of the technology.
Some example sites are listed below:
- World
Series of Poker
- Discovery
Channel
- The
Emmys
- Home
Shopping Network (HSN)
- World
Wrestling Entertainment (WWE)
- Fox
- XBOX
360
- Netflix
- uses Silverlight to allow subscribers to instantly watch movies on their
PCs or Intel-based Macs.
The .NET Framework
Microsoft introduced the .NET
Framework in 2000 as a new approach to software development. The .NET Framework
borrowed ideas from the best practices in the software industry as well as
brought new ideas to the table in an effort to meet developer needs and
requests.
Virtually all programming languages
manage data at some point. The primary reason that communication between
applications created using C++, Visual Basic, Visual FoxPro, and other
languages was difficult was because each language stored data being managed in
a unique set of data types. For example, an integer in one language may not
represent an integer in another language. Data needed to be converted to common
data types to communicate between languages.
The .NET Framework introduced a
common set of data types (the Common Type System) that is used by all
.NET-compliant languages (C++, Visual Basic, C#, etc). Thus all languages can
easily intercommunicate. Furthermore, all .NET-compliant languages render
a common result when compiling code, Microsoft Intermediate Language (MSIL).(see
footnote) MSIL can be deployed to any
platform running the Common Language Runtime (CLR). Currently, the CLR is only
available for Microsoft Windows although an open source version of the CLR was
created for Linux (called the Mono
Project). Code written by using the .NET
Framework is compiled instead of interpreted resulting in much better
performance than Java and competing technologies.
Microsoft took the Web development
industry by storm with their .NET upgrade to ASP. ASP.NET put a
new face on Web development through compiled code architecture and improved
state management and it provides access to the full functionality of the .NET
Framework. ASP.NET is built around XML, supports the latest Web development
standards, and allows for the creation of advanced Web Services.
The .NET Framework also provides
improved data access and database integration through ADO.NET. For more
information, see MSDN.
There are many more features and
benefits of the .NET Framework than those mentioned here. The .NET Framework
has become the leading software development environment available.
Introducing Silverlight
What's
New in the .NET Framework
As of the writing of this course,
version 3.5 of the .NET Framework has been released. Version 3.5 includes
features that encompass all facets of Windows, Web, and network development:
- Includes
just under 10,000 classes, methods, and properties.
- Complies
with the latest Web development standards.
- Introduces
revolutionary technologies used in Windows Vista development, rich media
and user experiences, workflow management, security and authorization, and
complete distributed communication protocols.
The .NET Framework can also be fully
extended by developers to create custom classes and types. The functionality of
the .NET Framework spans the server, the workstation, and the Web. The four
primary additions to the .NET Framework as of version 3.0 are:
- Windows
Presentation Foundation (WPF)
- Windows
Communication Foundation (WCF)
- Windows Workflow Foundation (WF)
- CardSpace
Windows Presentation Foundation (WPF)
WPF is used to develop elaborate
user interfaces like those that adorn Windows Vista and managed advanced media
streaming and integration. WPF is a complete revamp of Windows Forms so that
user interface, graphic, and media development is now designed around the .NET
Framework.
Windows Communication Foundation (WCF)
WCF encompasses the ASP.NET Web
Services and .NET remoting functionality that was contained in the .NET
Framework 2.0 as well as new communication technologies.
Windows Workflow Foundation (WF)
WF is used to model complex workflow
processes.
CardSpace
CardSpace is the embodiment of new
security and user authorization functionality.
ASP.NET
AJAX
ASP.NET AJAX was developed to
improve performance in the browser by making post backs and calls between the
browser and server asynchronously. ASP.NET AJAX uses new built-in types and
controls and JavaScript.
Both ASP.NET and ASP.NET AJAX are
heavily dependent upon the ASP.NET page event life cycle, are tightly coupled
to the server, and have a tough time competing with advanced, media-rich
plug-in solutions such as Adobe Flash. Additionally, it is difficult to create
Web applications that offer a consistent experience across all supported
browsers and platforms by using ASP.NET and AJAX. In 2006, Microsoft began
developing a solution to extend into the browser and offer media experiences
more robust than competing plug-in solutions.
In 2007, Microsoft introduced
Silverlight. (see
footnote) Silverlight is a free plug-in
that encompasses a subset of functionality from the .NET Framework and WPF. In
a manner similar to the JVM (see
footnote), Silverlight runs in the browser as
a "sandbox" - a secure zone installed into the browser that
accommodates Silverlight functionality while completely protecting the host
platform from any possibly adverse actions performed by Silverlight.
Silverlight
Architecture
Unlike ASP.NET, the bulk of Silverlight processing occurs on
the client machine thus decreasing server resource utilization and improving
the Web experience on the client. The figure below shows the difference between
ASP.NET processing and Silverlight processing:
When a client initially attempts to
run a Silverlight application, if the Silverlight plug-in has not been
installed on the client machine, it will be downloaded and installed. Upon
subsequent requests to run the application, the application will instantiate on
the client machine and make requests for resources from the server only when
necessary. The Silverlight plug-in can be thought of as a scaled-down version
of the full .NET Framework. It only contains those classes and functionality
that are applicable to a Silverlight Web client and those were streamlined and
optimized for use on the Web client machine.
Silverlight was designed using the
same design paradigm as ASP.NET. Each page of a Silverlight application
includes an associated code behind file that includes the code that handles
events fired by the page. Silverlight
resembles WPF in that it uses
Extensible Application Markup Language (XAML) to construct the user interface (presentation layer). As Silverlight
applications are composed of text-based files that include markup and code,
they can be created using any text editor; however, more advanced tools and
development environments such as Visual Studio or Expression Blend simplify the
task significantly.
Silverlight
Technologies
Version 1.0 of Silverlight used
JavaScript and supported the industry-leading Windows Media Services enabling
delivery of audio and video that includes 2D and vector graphics.
Version 2 includes all features of
version 1.0 and:
- support
for the .NET Framework.
- support
for .NET-compliant programming languages such as C#, Visual Basic, Python,
and Ruby.
- support
for database operations and language-integrated query (LINQ).
The figure below illustrates the major differences between
version 1.0 and version 2:
The diagram located at ClassFiles/WhatIsSilverlight/Demos/SilverlightTechnologyMap.gif gives
a broad picture of the technologies to be supported by Silverlight version 2.
Silverlight
Hosting
Microsoft Silverlight functionality
is completely encapsulated within the Silverlight plug-in. Web applications
typically require the server hosting the Web application to meet minimum
requirements. Silverlight applications simply require a Web server to be equipped
as they would be for hosting HTML documents. Silverlight applications can be
hosted on any Web server accessible to the target audience. The two most
commonly used Web servers are Microsoft Internet Information Server (IIS) and
Apache.
Executing a Silverlight application
on a Web client machine is a two-step process. First, the application will
detect if the Silverlight plug-in is installed on the Web client machine. If
the plug-in is not installed, the user will be prompted with an option to
download the plug-in. If the user opts to do so, a request will be made of the
Web server to download and install the plug-in. The Silverlight plug-in is
embodied in a .dll executable file that is loaded into the Web client browser
memory once installed. The only interaction required by the Web client when
installing the Silverlight plug-in is to grant permission for the plug-in to be
installed. Various Web servers, including Microsoft Internet Information Server
(IIS), may require slight configuration modifications so that the Silverlight
executable file will be downloaded to the Web client when requested.
Second, once the Silverlight plug-in
is installed on the Web client machine, the Silverlight application itself must
be downloaded. A Silverlight application may consist of many types of files.
Slight configuration modifications may be necessary on the Web server, such as
MIME types, so that XAML and XAP files are associated with Silverlight and
downloaded correctly to the Web client machine when requested.
Once the Silverlight plug-in is
installed on a Web client machine and a Silverlight application is downloaded,
the Silverlight application is then hosted on the Web client machine. There are
some requirements necessary for the Web client machine as discussed in the
sections below, however all media players, audio and video codecs, compilers
and the runtime are encapsulated in the Silverlight plug-in.
Linux
Many developers are unaware that a
version of the .NET Framework exists for the Linux operating system. Linux is
an open source operating system that is supported heavily in the online
community. The version of the .NET Framework that supports Linux is named the
Mono project and was also developed by the open source community. The
developers of the Mono project keep the project close to in sync with the .NET
Framework when updates are released by Microsoft and created an initial,
limited version of Silverlight (called "Moonlight") that supports
Linux in approximately 21 days! (see
footnote) You can get up to date
information on this project at http://www.mono-project.com/Moonlight.
Future
Platforms
The Silverlight plug-in renders
graphics and multimedia using a vector-based graphics engine. Vector graphics
can easily be scaled from very small displays to very large displays of varying
resolutions with virtually no loss of image quality. Silverlight on a Windows
Mobile device will accommodate delivering live, streaming, high quality video
to smart phones and similar devices. The goal is to enable developers to
deliver rich interactive applications (RIA) to any type of device.
Microsoft has announced support for
Silverlight on mobile devices with a limited initial support for Windows Mobile
and the Nokia S60 models. You can learn more about this future support athttp://www.microsoft.com/silverlight/overview/mobile.aspx.
Silverlight vs.
Flash
The most
successful browser plug-in is Adobe Flash, which is installed on more than 90
percent of the world’s web browsers. Flash has a long history that spans more
than ten years, beginning as a straightforward tool for adding animated
graphics and gradually evolving into a platform for developing interactive
content. It’s perfectly reasonable for .NET developers to create websites that
use Flash content. However, doing so requires a separate design tool and a
completely different programming language (ActionScript) and programming
environment (Flex). Furthermore, there’s no straightforward way to integrate
Flash content with server-side .NET code. For example, creating Flash
applications that call .NET components is awkward at best. Using server-side
.NET code to render Flash content (for example, a custom ASP.NET control that
spits out a Flash content region) is far more difficult.
Silverlight aims
to give .NET developers a better option for creating rich web content.
Silverlight provides a browser plug-in, with many similar features to Flash,
but it’s one that’s designed from the ground up for .NET. Silverlight natively
supports the C# language and embraces a range of .NET concepts. As a result,
developers can write client-side code for Silverlight in the same language they
use for server-side code (such as C# and VB), and they can use many of the same
abstractions (including streams, controls, collections, generics, and LINQ).
The Silverlight plug-in has an impressive list of features, some of which are
shared in common with Flash and a few of which are entirely new and even
revolutionary. Here are some highlights:
• 2D drawing:
Silverlight provides a rich model for 2D drawing. Best of all, the content you
draw is defined as shapes and paths, so you can manipulate this content on the
client side. You can even respond to events (like a mouse click on a portion of
a graphic), which makes it easy to add interactivity to anything you draw.
• Controls:
Developers don’t want to reinvent the wheel, so Silverlight is stocked with a few
essentials, including buttons, text boxes, lists, and even a DataGrid. Best of
all, these basic building blocks can be restyled with custom visuals if you
want all of the functionality but none of the stock look.
• Animation:
Silverlight has a time-based animation model that lets you define what should
happen and how long it should take. The Silverlight plug-in handles the sticky
details, such as interpolating intermediary values and calculating the frame
rate.
• Media:
Silverlight provides playback of a range of video standards, including
highdefinition H.264 video and AAC audio. Silverlight doesn’t use the Windows
Media Player ActiveX control or browser plug-in—instead, you can create any
front end you want, and you can even show video in full-screen mode.
• The Common
Language Runtime: Most impressively, Silverlight includes a scaled-down
version of the CLR, complete with an essential set of core classes, a garbage
collector, a just-in-time (JIT) compiler, support for generics, threading, and
so on. In many cases, developers can take code written for the full .NET CLR
and use it in a Silverlight
application with
only moderate changes.
• Networking:
Silverlight applications can call old-style ASP.NET web services (.asmx) or
Windows Communication Foundation (WCF) web services. They can also send
manually created XML requests over HTTP and even open direct socket connections
for fast two-way communication. This gives developers a great way to combine
rich clientside code with secure server-side routines.
• Data
binding: Although it’s not as capable as in its big brother (WPF),
Silverlight data binding provides a convenient way to display large amounts of
data with minimal code. You can pull your data from XML or in-memory objects,
giving you the ability to call a web service, receive a collection of objects,
and display their data in a web page—often
with just a
couple of lines of code.
• Multithreading:
A Silverlight application can take advantage of the multithreading capability
of modern operating systems. You can easily run background code to perform
time-consuming tasks (such as contacting a web service or refreshing a
calculation).
Visual Studio
vs. Expression Blend
If you’re still trying to
understand how Visual Studio and Expression Blend stack up, here’s a quick
overview:
• Visual Studio 2010: It
has everything you need to develop Silverlight applications, with a visual
designer for Silverlight pages. Using this designer, you can drag, drop, and
draw your user interface into existence (which isn’t always the best idea), and
you can get a live preview of what it looks like (which is terrifically
useful).
• Expression Blend 4: It
provides the rich support for creating Silverlight user interface, with visual
tools that surpass Visual Studio. For certain types of user interface grunt
work (for example, creating a nice gradient fill), it’s a remendous help.
Expression Blend also supports a fun application prototyping tool called
SketchFlow and includes a decent coding editor that’s designed to look like
Visual Studio. However, it lacks many advanced and important development tools,
such as debugging, code refactoring, and project source control.
Understanding
Silverlight Websites
You can create two types of
Silverlight websites in Visual Studio or Expression Blend:
• An ordinary website with HTML
pages: In this case, the entry point to your Silverlight application is a basic
HTML file that includes a Silverlight content region.
• ASP.NET website: In this case,
Visual Studio creates two projects—one to contain the Silverlight application
files and one to hold the server-side ASP.NET website that will be deployed
alongside your Silverlight files. The entry point to your Silverlight
application can be an ordinary HTML file, or it can be an ASP.NET web page that
includes server generated content. So, which approach is best? No matter which
option you choose, your Silverlight application will run the same way—the
client browser will receive an HTML document, which will include a Silverlight
content region, and the Silverlight code will run on the local computer, not
the web server. However, the ASP.NET web approach makes it easier to mix
ASP.NET and Silverlight content. This is usually a better approach in the
following cases:
• You want to create a website
that contains both ASP.NET web pages and Silverlightenhanced
pages.
• You want to create a
Silverlight application that calls a web service, and you want to design the
web service at the same time (and deploy it to the same web server).
• You want to generate
Silverlight content indirectly, using specialized ASP.NET web controls.
Creating a Stand-Alone
Silverlight Project
The easiest way to start using
Silverlight is to create an ordinary website with HTML pages and no server-side
code. Here’s how:
1. Select File ➤ New
➤ Project in Visual Studio, choose the
Visual C# ➤ Silverlight
group of project types, and then select the Silverlight Application template.
As usual, you need to pick a project name and a location on your hard drive
before clicking OK to create the project.
2. At this point, Visual Studio
will prompt you to choose whether you want to create a fullfledged ASP.NET
website that can run server-side code along with your Silverlight project (see
Figure 1-1). Uncheck the “Host the Silverlight application in a new Web site”
option to keep things simple.
3. Underneath,
choose the version of Silverlight application that you want to create. If you
aren’t using any of the new features in Silverlight 4, you’ll get slightly more
reach with Silverlight 3 (which, at the time of this writing, is still
installed on more computers). If you haven’t installed the Silverlight 4 Tools
for Visual Studio 2010, you won’t get an option for creating Silverlight 4
applications.
Every
Silverlight project starts with a small set of essential files, as shown in
Figure 1-2. All the files that end with the extension .xaml use a flexible
markup standard called XAML, which you’ll dissect in the next chapter. All the
files that end with the extension .cs hold the C# source code that powers your
application.
Creating a Simple Silverlight
Page
As you’ve
already learned, every Silverlight page includes a markup portion that defines
the visual appearance (the XAML file) and a source code file that contains
event handlers. To customize your first Silverlight application, you simply
need to open the MainPage.xaml file and begin adding markup. Visual Studio
gives you two ways to look at every XAML file—as a visual preview (known as the
design surface) or the
underlying markup (known as the XAML
view). By default, Visual Studio shows both parts, stacked one on the
other. Figure 1-4 shows this view and points out the buttons you can use to
change your vantage point.
To get started, you can try
creating the page shown in the following example, which defines a block of text
and a button. The portions in bold have been added to the basic page template
that Visual Studio generated when you created the project.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignWidth="300" d:DesignHeight="400">
Margin="5">
This creates a page that has a
stacked arrangement of two elements. On the top is a block of
text with a simple message.
Underneath it is a button.
Adding Event-Handling Code
You attach event handlers to the
elements in your page using attributes, which is the same approach that
developers take in WPF, ASP.NET, and JavaScript. For example, the Button
element exposes an event named Click that fires when the button is triggered
with the mouse or keyboard. To react to this event, you add the Click attribute
to the Button element and set it to the name of a method in your code:
Margin="5">
This example assumes that you’ve
created an event-handling method named cmdClickMe_Click. Here’s what it looks
like in the MainPage.xaml.cs file:
private void
cmdClickMe_Click(object sender, RoutedEventArgs e)
{
lblMessage.Text = "Goodbye,
cruel world.";
}
Creating an ASP.NET-Hosted
Silverlight Project
Although
Silverlight does perfectly well on its own, you can also develop, test, and
deploy it as part of an ASP.NET website. Here’s how to create a Silverlight
project and an ASP.NET website that uses it in the same solution:
1. Select File ➤ New
➤ Project in Visual Studio, choose the
Visual C# ➤ Silverlight
group of project types, and then select the Silverlight Application template.
It’s a good idea to use the “Create directory for solution” option so you can
group together the two projects that Visual Studio will create—one for the
Silverlight assembly and one for
ASP.NET website.
2. Ordinarily,
Visual Studio assumes you want to use the latest and greatest version of .NET
for the server-side portion of any web applications you create. If this isn’t
what you want, you can pick a different version of .NET from the drop-down list
at the top of the New Project window. For example, if you pick .NET Framework
3.5, your ASP.NET website will be configured to use this slightly older version
of .NET.
3. Once you’ve
picked the solution name and project name, click OK to create it.
4. Make sure the
option “Host the Silverlight application in a new website” is checked.
5. Supply a
project name for the ASP.NET website. By default, it’s your project name with
the added text .Web at the end, as shown in Figure 1-7.
6. In the
drop-down list underneath, choose the way you want Visual Studio to manage your
project—either as a web project or as a website. The choice has no effect on
how Silverlight works. If you choose Web Project, Visual Studio uses a project
file to track the contents of your web application and compiles your web page
code into a single
assembly before
you run it. If you choose Web Site, Visual Studio simply assumes everything in
the application folder is part of your web application. Your web page code will
be compiled the first time a user requests a page (or when you use the
precompilation tool aspnet_compiler.exe). For more information about the
difference between web projects and projectless websites, as well as other
ASP.NET basics, refer to Pro ASP.NET 4 in C# 2010.
7. Choose
whether you want to create a Silverlight 3 or Silverlight 4 application in the
Silverlight Version list.
8. You can also
choose to enable WCF RIA services, which are a set of web services that help
you bridge the gap between the client-side world of Silverlight and the
server-side world of ASP.NET. For a basic Silverlight website, leave this
option unchecked.
9.
Finally,
click OK to create the solution.
There are two
ways to integrate Silverlight content into an ASP.NET application:
• Create HTML files with Silverlight content:
You place these files in your ASP.NET website folder, just as you would with
any other ordinary HTML file. The only limitation of this approach is that your
HTML file obviously can’t include ASP.NET controls, because it won’t be
processed on the server.
• Place Silverlight content inside an ASP.NET web form: In this
case, the
Of course,
you’re also free to mingle both of these approaches and use Silverlight content
in dedicated HTML pages and inside ASP.NET web pages in the same site. When you
create a Silverlight project with an ASP.NET website in Visual Studio, you’ll start
with both. For example,
if your
Silverlight project is named SilverlightApplication1, you can use SilverlightApplication1TestPage.html
or SilverlightApplication1TestPage.aspx.
Figure 1-8 shows
how a Silverlight and ASP.NET solution starts. Along with the two test pages,
the ASP.NET website also includes a Default.aspx page (which can be used as the
entry point to your ASP.NET website), a web.config file (which allows you to
configure various website settings), and a Silverlight.js file (which has JavaScript
helper functions for creating and initializing the Silverlight content region).
The Silverlight
and ASP.NET option provides essentially the same debugging experience as a
Silverlight-only olution. When you run the solution, Visual Studio compiles
both projects and copies the Silverlight assembly to the ClientBin folder in
the ASP.NET website. (This is similar to assembly references—if an ASP.NET
website references a private DLL, Visual Studio automatically copies this DLL
to the Bin folder.)
Once both
projects are compiled, Visual Studio looks to the startup project (which is the
ASP.NET website) and looks for the currently selected page. It then launches
the default browser and navigates to that page. The difference is that it
doesn’t request the start page directly from the file system. Instead, it
communicates with its built-in test web server. This web server automatically
loads on a randomly chosen port. It acts like a scaled-down version of IIS but
accepts requests only from the local computer. This gives you the ease of
debugging without needing to configure IIS virtual directories. Figure 1-9
shows the same Silverlight application you considered earlier but hosted by
ASP.NET.
The HTML Entry Page
The last ingredient
in the deployment picture is the HTML test page. This page is the entry point
into your Silverlight content—in other words, the page the user requests in the
web browser. In a stand-alone Silverlight project, Visual Studio names this
file TestPage.html. In an ASP.NET-hosted Silverlight project, Visual Studio
names it to match your project name. Either way, you’ll probably want to rename
it to something more appropriate.
The HTML test page
doesn’t actually contain Silverlight markup or code. Instead, it simply sets up
the content region for the Silverlight plug-in, using a small amount of
JavaScript. (For this reason, browsers that have JavaScript disabled won’t be
able to see Silverlight content.)
Here’s a slightly
shortened version of the HTML test page that preserves the key details:
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SilverlightApplication1</title>
<style
type="text/css">
...
</style>
<script
type="text/javascript">
...
</script>
</head>
<body>
<form
id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object
data="data:application/x-silverlight-2,"
type="application/x-silverlight-2"
width="100%" height="100%">
<param
name="source" value="SilverlightApplication1.xap"
/>
<param
name="onError" value="onSilverlightError"
/>
<param
name="background" value="white"
/>
<param
name="minRuntimeVersion" value="4.0.50401.0"
/>
<param
name="autoUpgrade" value="true"
/>
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50401.0"
style="text-decoration:none">
<img
src="http://go.microsoft.com/fwlink/?LinkId=108181"
alt="Get
Microsoft Silverlight" style="border-style:none"/>
</a>
</object>
<iframe
id="_sl_historyFrame"
style="visibility:hidden;height:0px;width:0px;border:0px"></iframe>
</div>
</body>
</html>
XAML
XAML (short for
Extensible Application Markup Language and pronounced zammel) is a
markup language used to instantiate .NET objects. Although XAML is a technology
that can be applied to many different problem domains, it was initially
designed as part of Windows Presentation Foundation (WPF), where it allows
Windows developers to construct rich user interfaces. You use the same standard
to build user interfaces for Silverlight applications. Conceptually, XAML plays
a role that’s a lot like HTML and is even closer to its stricter cousin, XHTML.
XHTML allows you to define the elements that make up an ordinary web page.
Similarly, XAML allows you to define the elements that make up a XAML content
region. To manipulate XHTML elements, you can use client-side JavaScript. To
manipulate XAML elements, you write client-side C# code. Finally, XAML and XHTML
share many of the same
syntax
conventions. Like XHTML, XAML is an XML-based language that consists of
elements that can be nested in any arrangement you like.
XAML Basics
The XAML standard is quite
straightforward once you understand a few ground rules:
• Every element in a XAML
document maps to an instance of a Silverlight class. The name of the element
matches the name of the class exactly.
For example, the element
• As with any XML document, you
can nest one element inside another. As you’ll see, XAML gives every class the
flexibility to decide how it handles this situation. However, nesting is
usually a way to express containment—in
other words, if you find a Button element inside a Grid element, your user
interface probably includes a Grid that
contains a Button inside.
• You can set the properties of
each class through attributes. However, in some situations, an attribute isn’t
powerful enough to handle the job. In these cases,
you’ll use nested tags with a special syntax.
Before continuing,
take a look at this bare-bones XAML document, which represents a blank page (as
created by Visual Studio). The lines have been numbered for easy reference:
1 <UserControl
x:Class="SilverlightApplication1.MainPage"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6 mc:Ignorable="d"
d:DesignWidth="640" d:DesignHeight="480">
7
8 <Grid
x:Name="LayoutRoot">
9 </Grid>
10 </UserControl>
Core Silverlight Namespaces
The first two
namespaces are the most important. You’ll need them to access essential parts
of the Silverlight runtime:
• http://schemas.microsoft.com/winfx/2006/xaml/presentation is the
core Silverlight namespace. It encompasses all the essential Silverlight
classes, including the UserControl and Grid. Ordinarily, this namespace is
declared without a namespace
prefix, so it becomes the default
namespace for the entire document. In other words, every element is
automatically placed in this namespace unless you specify otherwise.
• http://schemas.microsoft.com/winfx/2006/xaml is the XAML
namespace. It includes various XAML utility features that allow you to
influence how your document is interpreted. This namespace is mapped to the
prefix x. That means you can
apply it by placing the namespace prefix before the name of an XML element or attribute (as in and
x:Class="ClassName").
Properties and Events in XAML
So far, you’ve
considered a relatively unexciting example—a blank page that hosts an empty
Grid control. Before going any further, it’s worth introducing a more realistic
page that includes several elements. Figure 2-1 shows an example with an
automatic question answerer.
The
eight ball page includes four elements: a Grid (the most common tool for
arranging layout in Silverlight), two TextBox objects, and a Button. The markup
that’s required to arrange and configure these elements is significantly longer
than the previous examples. Here’s an abbreviated listing that replaces some of
the details with an ellipsis (. . .) to expose
the overall structure:
<UserControl
x:Class="EightBall.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400"
Height="300">
<Grid
x:Name="grid1">
<Grid.Background>
...
</Grid.Background>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<TextBox
x:Name="txtQuestion" ... >
</TextBox>
<Button
x:Name="cmdAnswer" ... >
</Button>
<TextBox
x:Name="txtAnswer" ... >
</TextBox>
</Grid>
</UserControl>
Complex Properties
As
handy as type converters are, they aren’t practical for all scenarios. For
example, some properties are full-fledged objects with their own set of
properties. Although it’s possible to create a string representation that the
type converter could use, that syntax might be difficult to use and prone to
error.
Fortunately,
XAML provides another option: property-element
syntax. With propertyelement syntax, you add a child element with a name
in the form Parent.PropertyName.
For example, the Grid has a Background property that allows you to supply a
brush that’s used to paint the area behind the elements. If you want to use a
complex brush—one more advanced than a solid color fill—you’ll need to add a
child tag named Grid.Background, as shown here:
<Grid
x:Name="grid1">
<Grid.Background>
...
</Grid.Background>
...
</Grid>
The
key detail that makes this work is the period (.) in the element name. This
distinguishes properties from other types of nested content. This still leaves
one detail—namely, once you’ve identified the complex property you want to
configure, how do you set it? Here’s the trick. Inside the nested element, you
can add another tag to instantiate a specific class. In the eight ball example
(shown in Figure 2-1), the background is filled with a gradient. To define the
gradient you want, you need to create a LinearGradientBrush object. Using the
rules of XAML, you can create the LinearGradientBrush object using an element with
the name LinearGradientBrush:
<Grid
x:Name="grid1">
<Grid.Background>
<LinearGradientBrush>
</LinearGradientBrush>
</Grid.Background>
...
</Grid>
The
LinearGradientBrush is part of the Silverlight set of namespaces, so you can
keep using the default XML namespace for your tags. However, it’s not enough to
simply create the LinearGradientBrush—you also need to specify the colors in
that gradient. You do this by filling the LinearGradientBrush.GradientStops property
with a collection of GradientStop objects. Once again, the GradientStops
property is too complex to be set with an attribute value alone. Instead, you
need to rely on the propertyelement syntax:
<Grid
x:Name="grid1">
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
...
</Grid>
Finally,
you can fill the GradientStops collection with a series of GradientStop
objects. Each GradientStop object has an Offset and Color property. You can
supply these two values using the ordinary property-attribute syntax:
<Grid
x:Name="grid1">
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop
Offset="0.00" Color="Yellow" />
<GradientStop
Offset="0.50" Color="White" />
<GradientStop
Offset="1.00" Color="Purple" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
...
</Grid>
Any
set of XAML tags can be replaced with a set of code statements that performs
the same task. The tags shown previously, which fill the background with a
gradient of your choice, are equivalent to the following code:
LinearGradientBrush
brush = new LinearGradientBrush();
GradientStop
gradientStop1 = new GradientStop();
gradientStop1.Offset
= 0;
gradientStop1.Color
= Colors.Yellow;
brush.GradientStops.Add(gradientStop1);
GradientStop
gradientStop2 = new GradientStop();
gradientStop2.Offset
= 0.5;
gradientStop2.Color
= Colors.White;
brush.GradientStops.Add(gradientStop2);
GradientStop
gradientStop3 = new GradientStop();
gradientStop3.Offset
= 1;
gradientStop3.Color
= Colors.Purple;
brush.GradientStops.Add(gradientStop3);
grid1.Background
= brush;
Element-to-Element Binding
In
the previous section, you saw how to use the StaticResource markup extension,
which gives XAML additional capabilities (in this case, the ability to easily
refer to a resource that’s defined elsewhere in your markup). You’ll see the
StaticResource at work throughout the examples in this book. Another markup
extension that gets heavy use is the Binding expression, which sets up a
relationship that funnels information from a source object to a target control.
In
Chapter 16, you’ll use binding expressions to create data-bound pages that
allow the user to review and edit the information in a linked data object. But
in this chapter, you’ll take a quick look at a more basic skill—the ability to
connect two elements together with a binding expression.
One-Way Binding
To understand how you
can bind an element to another element, consider the simple window shown in
Figure. It contains two controls: a Slider and a TextBlock with a single line
of text. If you pull the thumb in the slider to the right, the font size of the
text is increased immediately. If you pull it to the left, the font size is
reduced.
Clearly,
it wouldn’t be difficult to create this behavior using code. You would simply
react to the Slider.ValueChanged event and copy the current value from the
slider to the TextBlock. However, data binding makes it even easier.
When
using data binding, you don’t need to make any change to your source object
(which is the Slider in this example). Just configure it to take the right
range of values, as you would ordinarily.
<Slider
x:Name="sliderFontSize" Margin="3"
Minimum="1"
Maximum="40" Value="10">
</Slider>
The
binding is defined in the TextBlock element. Instead of setting the FontSize
using a literal value, you use a binding expression, as shown here:
<TextBlock
Margin="10" Text="Simple Text" x:Name="lblSampleText"
FontSize="{Binding
ElementName=sliderFontSize, Path=Value}"
>
</TextBlock>
Two-Way Binding
Interestingly,
there’s a way to force values to flow in both directions: from the source to
the target and from the target to the source. The trick is to set the Mode
property of the Binding. Here’s a revised bidirectional binding that allows you
to apply changes to either the source or the target and have the other piece of
the equation update itself automatically:
<TextBlock
Margin="10" Text="Simple Text" Name="lblSampleText"
FontSize="{Binding
ElementName=sliderFontSize, Path=Value, Mode=TwoWay}"
>
</TextBlock>
In this example, there’s no
reason to use a two-way binding, because you can solve the problem by
manipulating the value of the slider rather than changing the font size of the
TextBlock. However, consider a variation of this example that includes a text box
where the user can set the font size precisely (see Figure)
Here,
the text box needs to use a two-way binding, because it both receives the bound
data value and sets it. When the user drags the slider (or clicks a button),
the text box receives the new slider value. And when the user types a new value
in the text box, the binding copies the value to the slider.
Here’s
the two-way binding expression you need:
<TextBox
Text="{Binding ElementName=lblSampleText, Path=FontSize, Mode=TwoWay}">
</TextBox>
The Layout Containers
A Silverlight window
can hold only a single element. To fit in more than one element and create a
more practical user interface, you need to place a container in your page and
then add other elements to that container. Your layout is determined by the
container that you use. All the Silverlight layout containers are panels that
derive from the abstract System.Windows.Controls.Panel class.
Simple Layout with the StackPanel
The StackPanel is one
of the simplest layout containers. It simply stacks its children in a single
row or column. These elements are arranged based on their order. For example,
consider this page, which contains a stack with one TextBlock and four buttons:
<UserControl
x:Class="Layout.SimpleStack"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel
Background="White">
<TextBlock
Text="A Button Stack"></TextBlock>
<Button
Content="Button 1"></Button>
<Button
Content="Button 2"></Button>
<Button
Content="Button 3"></Button>
<Button
Content="Button 4"></Button>
</StackPanel>
</UserControl>
The StackPanel can
also be used to arrange elements horizontally by setting the Orientation
property:
<StackPanel
Orientation="Horizontal" Background="White">
Margins
There’s
an obvious problem with the StackPanel example in its current form. A
well-designed page doesn’t just contain elements—it also includes a bit of
extra space in between the elements. To introduce this extra space and make the
StackPanel example less cramped, you can set control margins.
When
setting margins, you can set a single width for all sides, like this:
<Button
Margin="5" Content="Button
3"></Button>
Alternatively, you
can set different margins for each side of a control in the order left, top,
right, bottom:
<Button
Margin="5,10,5,10" Content="Button
3"></Button>
In code, you can set
margins using the Thickness structure:
cmd.Margin = new Thickness(5);
The WrapPanel and DockPanel
Obviously,
the StackPanel alone can’t help you create a realistic user interface. To
complete the picture, the StackPanel needs to work with other more capable
layout containers. Only then can you assemble a complete window. The most
sophisticated layout container is the Grid, which you’ll consider later in this
chapter. But first, it’s worth looking at the WrapPanel and DockPanel, which
are two simple layout containers that are available as part of the Silverlight
Toolkit. Both complement the StackPanel by offering different layout behavior.
To
use the WrapPanel or the DockPanel, you need to add a reference to the
System.Windows.Controls.Toolkit.dll assembly where they are defined. To get
this assembly, you must install the Silverlight Toolkit, which is available at http://silverlight.codeplex.com.
Once you’ve added the
assembly reference, you need to map the namespace so it’s available in your
markup, as shown here:
<UserControl x:Class="Layout.WrapAndDock" ...
xmlns:toolkit=
"clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit">
You can now define
the WrapPanel and DockPanel using the namespace prefix toolkit:
<toolkit:WrapPanel
...></toolkit:WrapPanel>
You can skip a few
steps by adding the WrapPanel from the Toolbox. Visual Studio will then
add the appropriate
assembly reference, map the namespace, and insert the XML markup for
the control.
The WrapPanel
The WrapPanel lays
out controls in the available space, one line or column at a time. By default,
the WrapPanel.Orientation property is set to Horizontal; controls are arranged
from left to right and then on subsequent rows. However, you can use Vertical
to place elements in multiple columns.
Here’s an example
that defines a series of buttons with different alignments and places them into
the WrapPanel:
<toolkit:WrapPanel
Margin="3">
<Button
VerticalAlignment="Top" Content="Top
Button"></Button>
<Button
MinHeight="60" Content="Tall
Button"></Button>
<Button
VerticalAlignment="Bottom" Content="Bottom
Button"></Button>
<Button
Content="Stretch Button"></Button>
<Button
VerticalAlignment="Center" Content="Centered
Button"></Button>
</toolkit:WrapPanel>
The DockPanel
The
Silverlight Toolkit also includes a layout container called the DockPanel. It
stretches controls against one of its outside edges. The easiest way to
visualize this is to think of the toolbars that sit at the top of many Windows
applications. These toolbars are docked to the top of the window. As with the
StackPanel, docked elements get to choose one aspect of their layout. For
example, if you dock a button to the top of a DockPanel, it’s stretched across the
entire width of the DockPanel but given whatever height it requires (based on
the content and the MinHeight property). On the other hand, if you dock a
button to the left side of a container, its height is stretched to fit the
container, but its width is free to grow as needed. The obvious question is
this: How do child elements choose the side where they want to dock? The answer
is through an attached property named Dock, which can be set to Left, Right,
Top, or Bottom. Every element that’s placed inside a DockPanel automatically
acquires this property.
Here’s
an example that puts one button on every side of a DockPanel:
<toolkit:DockPanel
LastChildFill="True">
<Button
toolkit:DockPanel.Dock="Top" Content="Top
Button"></Button>
<Button
toolkit:DockPanel.Dock="Bottom" Content="Bottom
Button"></Button>
<Button
toolkit:DockPanel.Dock="Left" Content="Left
Button"></Button>
<Button
toolkit:DockPanel.Dock="Right" Content="Right
Button"></Button>
<Button
Content="Remaining Space"></Button>
</toolkit:DockPanel>
The Two-Step Layout Process
Every
panel uses the same plumbing: a two-step process that’s responsible for sizing
and arranging children. The first stage is the measure pass, and it’s at
this point that the panel determines how large its children want to be. The
second stage is the layout pass, and it’s at this point that each
control is assigned its bounds. Two steps are required, because the panel might
need to take into account the desires of all its children before it decides how
to partition the
available
space.
You
add the logic for these two steps by overriding the oddly named
MeasureOverride() and ArrangeOverride() methods, which are defined in the
FrameworkElement class as part of the Silverlight layout system. The odd names
represent that the MeasureOverride() and ArrangeOverride() methods replace the
logic that’s defined in the MeasureCore() and ArrangeCore() methods that are
defined in the UIElement class. These methods are not overridable.
MeasureOverride()
The
first step is to determine how much space each child wants using the
MeasureOverride() method. However, even in the MeasureOverride() method,
children aren’t given unlimited room. At a bare minimum, children are confined
to fit in the space that’s available to the panel. Optionally, you might want
to limit them more stringently. For example, a Grid with two proportionally
sized rows will give children half the available height. A StackPanel will
offer the first element all the space that’s available and then offer the
second element whatever’s left, and so on.
Every
MeasureOverride() implementation is responsible for looping through the
collection of children and calling the Measure() method of each one. When you
call the Measure() method, you supply the bounding box—a Size object that
determines the maximum available space for the child control. At the end of the
MeasureOverride() method, the panel returns the space it needs to display all
its children and their desired sizes. Here’s the basic structure of the
MeasureOverride() method, without the specific sizing details:
protected override Size
MeasureOverride(Size panelSpace)
{
// Examine all the
children.
foreach (UIElement
element in this.Children)
{
// Ask each child
how much space it would like, given the
//
availableElementSize constraint.
Size availableElementSize
= new Size(...);
element.Measure(availableElementSize);
// (You can now
read element.DesiredSize to get the requested size.)
}
// Indicate how
much space this panel requires.
// This will be
used to set the DesiredSize property of the panel.
return new Size(...);
}
The
Measure() method doesn’t return a value. After you call Measure() on a child,
that child’s DesiredSize property provides the requested size. You can use this
information in your calculations for future children (and to determine the
total space required for the panel). You must call Measure() on each
child, even if you don’t want to constrain the child’s size or use the
DesiredSize property. Many elements will not render themselves until you’ve
called Measure(). If you want to give a child free rein to take all the space
it wants, pass a Size object with a value of Double.PositiveInfinity for both
dimensions. (The ScrollViewer is one element that uses this strategy, because
it can handle any amount of content.) The child will then return the space it
needs for all its content. Otherwise, the child will normally return the space
it needs for its content or the space that’s available—whichever is smaller. At
the end of the measuring process, the layout container must return its desired
size. In a
simple
panel, you might calculate the panel’s desired size by combining the desired
size of every child.
ArrangeOverride()
Once
every element has been measured, it’s time to lay them out in the space that’s
available. The layout system calls the ArrangeOverride() method of your panel,
and the panel calls the Arrange() method of each child to tell it how much
space it’s been allotted. (As you can probably guess, the Arrange() method
triggers the ArrangeOverride() method, much as the Measure() method triggers
the MeasureOverride() method.) When measuring items with the Measure() method,
you pass in a Size object that defines the bounds of the available space. When
placing an item with the Arrange() method, you pass in a System.Windows.Rect
object that defines the size and position of the item. At this point,
it’s as though every element is placed with Canvas-style X and Y coordinates
that determine the distance between the top-left corner of your layout
container and the element.
Here’s
the basic structure of the ArrangeOverride() method, without the specific sizing
details:
protected
override Size ArrangeOverride(Size panelSize)
{
//
Examine all the children.
foreach
(UIElement element in this.Children)
{
//
Assign the child its bounds.
Rect
elementBounds = new Rect(...);
element.Arrange(elementBounds);
//
(You can now read element.ActualHeight and element.ActualWidth
//
to find out the size it used.)
}
// Indicate how
much space this panel occupies.
// This will be
used to set the ActualHeight and ActualWidth properties
// of the panel.
return arrangeSize;
}
The Application Model
The Application Class
You
took your first look at the App.xaml file. Much as every XAML page is a template
for a custom class that derives from System.Windows.UserControl, the App.xaml
file is a template for a custom class (named App by default) that derives from System.Windows.Application.
You’ll find the class definition in the App.xaml.cs file:
public
partial class App : Application
{
... }
When
the Silverlight plug-in loads your application, it begins by creating an
instance of the App class. From that point on, the application object serves as
your entry point for a variety of application-specific features, including
application events, resources, and services.
Accessing the Current Application
You can retrieve a
reference to the application object at any time, at any point in your code, using
the static Application.Current property.
However, this
property is typed as a system.Windows.Application object. To use any custom
properties or methods that you’ve added to the derived application class, you
must cast the reference to the App type. For example, if you’ve added a method
named DoSomething() to the App.xaml.cs file, you can invoke it with code like
this:
((App)Application.Current).DoSomething();
This technique allows
you to use your custom application class as a sort of switchboard for global
tasks that affect your entire application. For example, you can add methods to
your application class that control navigation or registration, and you can add
properties that store global data. You’ll see the App class used this way in
examples throughout this book.
Application
Events
You took your
first look at the life cycle of a Silverlight application. Here’s a quick
review:
1. The user
requests the HTML entry page in the browser.
2. The browser
loads the Silverlight plug-in. It then downloads the XAP file that contains your
application.
3. The
Silverlight plug-in reads the AppManifest.xml file from the XAP to find out
what assemblies your application uses. It creates the Silverlight runtime
environment and then loads your application assembly (along with any dependent
assemblies).
4. The
Silverlight plug-in creates an instance of your custom application class (which
is defined in the App.xaml and App.xaml.cs files).
5. The default
constructor of the application class raises the Startup event.
6. Your
application handles the Startup event and creates the root visual object for
your application.
From this point
on, your page code takes over, until it encounters an unhandled error (UnhandledException) or finally ends (Exit). These
events—Startup, UnhandledException, and Exit—are the core events that the
Application class provides. Along with these standards, the Application class
includes two events—InstallStateChanged and CheckAndDownloadUpdateCompleted—that
are designed for use with the out-of-browser applications. If you look at the
contents of the App.xaml.cs file, you’ll see that in Visual Studio, the
application
constructor contains some pregenerated code. This code attaches an event
handler
to the
three application events:
public
App()
{
this.Startup
+= this.Application_Startup;
this.Exit
+= this.Application_Exit;
this.UnhandledException
+= this.Application_UnhandledException;
InitializeComponent();
}
As with the page and
element events you’ve considered in earlier chapters, there are two ways to
attach application event handlers. Instead of using code, you can add event
attributes to the XAML markup, as shown here:
<Application
... x:Class="SilverlightApplication1.App"
Startup="Application_Startup"
>
There’s
no reason to prefer one approach to the other. By default, Visual Studio uses
the code approach shown first. In the following sections, you’ll see how you
can write code that plugs into the
application
events.
Application Startup
By default, the
Application_Startup method creates the first page and assigns it to the Application.RootVisual
property, ensuring that it becomes the top-level application element— the
visual core of your application:
private void Application_Startup(object
sender, StartupEventArgs e)
{
this.RootVisual
= new MainPage();
}
Although you can
change the root visual by adding or removing elements, you can’t reassign the
RootVisual property at a later time. After the application starts, this
property is essentially read-only.
Initialization Parameters
The
Startup event passes in a StartupEventArgs object, which includes one
additional detail: initialization parameters. This mechanism allows the page
that hosts the Silverlight control to pass in custom information. This is
particularly useful if you host the same Silverlight application on
different pages or you want the Silverlight application to vary based on
userspecific or session-specific information. For example, you can customize
the application’s view depending on whether users are entering from the
customer page or the employee page. Or, you may choose to load different
information based on the product the user is currently viewing. Just remember
that the initialization parameters come from the tags of the HTML entry page,
and a malicious user can alter them.
For example, imagine
you want to pass a ViewMode parameter that has two possible values, Customer or
Employee, as represented by this enumeration:
public enum ViewMode
{
Customer, Employee
}
You need
to change a variety of details based on this information, so it makes sense to
store it somewhere that’s accessible throughout your application. The logical
choice is to add a property to your custom application class, like this:
private ViewMode
viewMode = ViewMode.Customer;
public ViewMode
ViewMode
{
get { return
viewMode; }
}
This
property defaults to customer view, so it needs to be changed only if the web
page specifically requests the employee view. To pass the parameter into your
Silverlight application, you need to add a element to the markup
in the Silverlight content region. This parameter must have the name initParams.
Its value is a comma-separated list of name-value pairs that set your custom parameters.
For example, to add a parameter named viewMode, you add the following line (shown
in bold) to your markup:
<div
id="silverlightControlHost">
<object
data="data:application/x-silverlight,"
type="application/x-silverlight-2"
width="100%" height="100%">
<param
name="source" value="TransparentSilverlight.xap"/>
<param
name="onerror" value="onSilverlightError"
/>
<param
name="background" value="white"
/>
<param
name="initParams" value="viewMode=Customer"
/>
...
</object>
<iframe
style="visibility:hidden;height:0;width:0;border:0px"></iframe>
</div>
Then, you can
retrieve this from the StartupEventArgs.InitParams collection. However, you
must check first that it exists:
private void Application_Startup(object
sender, StartupEventArgs e)
{
// Take the view
mode setting, and store in an application property.
if (e.InitParams.ContainsKey("viewMode"))
{
string view
= e.InitParams["viewMode"];
if (view
== "Employee") this.viewMode
= ViewMode.Employee;
}
// Create the root
page.
this.RootVisual
= new Page();
}
If you have many
possible values, you can use the following leaner code to convert the string to
the corresponding enumeration value, assuming the text matches exactly:
string view
= e.InitParams["viewMode"];
try
{
this.viewMode
= (ViewMode)Enum.Parse(typeof(ViewMode), view, true);
}
catch { }
Now, different pages
are free to pass in a different parameter and launch your application with
different view settings. Because the view information is stored as a property
in the custom application class (named App), you can retrieve it anywhere in
your application:
lblViewMode.Text = "Current
view mode: " +
((App)Application.Current).ViewMode.ToString();
Figure shows what
you’ll see when you run the test page that uses the Customer
view mode.
Unhandled Exceptions
Although
you should use disciplined exception-handling code in situations where errors
are possible (for example, when reading a file, downloading web content, or
accessing a web service), it’s not always possible to anticipate all sources of
error. If your application encounters an error that isn’t handled, it will end,
and the Silverlight content region will revert to a blank space. If you’ve
included JavaScript code that reacts to potential errors from the Silverlight
plug-in, that code will run. Otherwise, you won’t receive any indication about
the error that’s just occurred. The Application.UnhandledException event gives
you a last-ditch chance to respond to an exception before it reaches the
Silverlight plug-in and terminates your application. This code is notably
different from the JavaScript error-handling code that you may add to the page,
because it has the ability to mark an exception as handled. Doing so
effectively neutralizes the exception, preventing it from rising to the plug-in
and ending your application.
Here’s an example
that checks the exception type and decides whether to allow the application to
continue:
public void Application_UnhandledException(object
sender,
ApplicationUnhandledExceptionEventArgs
e)
{
if (e.ExceptionObject
is FileNotFoundException)
{
// Suppress the
exception and allow the application to continue.
e.Handled = true;
}
}
Ideally, an
exception like this should be handled closer to where it occurs—for example, in
your page code, when you’re performing a task that may result in a
FileNotFoundException. Application-level error handling isn’t ideal, because
it’s difficult to identify the original process that caused the problem and
it’s awkward to notify the user about what went wrong. But application-level
error handling does occasionally offer a simpler and more streamlined way to handle
certain scenarios—for example, when a particular type of exception crops up in
numerous places. After you’ve neutralized the error, it makes sense to notify
the user. One option is to call a custom method in your root visual. For
example, this code calls a custom ReportError() method in the MainPage class,
which is the root visual for this application:
MainPage rootPage
= (MainPage)this.RootVisual;
rootPage.ReportError(e.ExceptionObject);
Now the
MainPage.ReportError() method can examine the exception object and display the
appropriate message in an element on the page. In an effort to make your
applications a little more resilient, Visual Studio adds a bit of boilerplate
error-handling code to every new Silverlight application. This code checks
whether a debugger is currently attached.
public void Application_UnhandledException(object
sender,
ApplicationUnhandledExceptionEventArgs
e)
{
if (!System.Diagnostics.Debugger.IsAttached)
{
// Suppress the
exception and allow the application to continue.
e.Handled = true;
try
{
// Build an error
message.
string errorMsg
= e.ExceptionObject.Message +
e.ExceptionObject.StackTrace;
errorMsg =
errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");
// Use the
Window.Eval() method to run a line of JavaScript code that
// will raise an
error with the error message.
System.Windows.Browser.HtmlPage.Window.Eval(
"throw new
Error(\"Unhandled Error in Silverlight 2 Application " +
errorMsg + "\");");
}
catch {}
}
}
Essentially, this
code converts a fatal Silverlight error to a relatively harmless JavaScript error.
The way the JavaScript error is dealt with depends on the browser. In Internet
Explorer, a yellow alert icon appears in the status bar. (Double-click the
alert icon to get the full error details, as shown in Figure 6-2.) In Firefox,
a script error message appears. Either way, the error won’t stop your
application from continuing.
Custom Splash Screens
If a
Silverlight application is small, it downloads quickly and appears in the
browser. If a Silverlight application is large, it may take a few seconds to
download. As long as your application takes longer than 500 milliseconds to
download, Silverlight shows an animated splash screen.
The built-in splash screen isn’t
too exciting—it displays a ring of blinking circles and the
percentage of
the application that’s been downloaded so far (see Figure)
If you don’t
like the stock splash screen, you can easily create your own (see Figure). Essentially,
a custom splash screen is a XAML file with the graphical content you want to display
and a dash of JavaScript code that updates the splash screen as the application
is downloaded. You can’t use C# code at this point, because the Silverlight
programming environment hasn’t been initialized yet. However, this isn’t a
major setback, because the code you need is relatively straightforward. It
lives in one or two event-handling functions that are triggered as content is
being downloaded and after it’s finished, respectively. And because JavaScript
is syntactically similar to C#, you won’t have much trouble putting together
the code you need.
Placing Resources in the
Application Assembly
This is the standard
approach, and it’s similar to the approach used in other types of .NET applications
(such as WPF applications). For example, if you want to show an image in Silverlight’s
Image element, begin by adding the image file to your project. By default,
Visual Studio gives image files the Resource build action, as shown in Figure.
(To change the build action of an existing file, select it in the Solution
Explorer, and make a new selection in the Build Action box in the Properties
pane.)
Now, when you
compile your application, the resource will be embedded in the project assembly,
and the project assembly will be placed in the XAP file.
Using
an embedded resource is easy because of the way Silverlight uses URIs. If you
use a relative URI with the Image (for graphics) or MediaElement (for sound and
video files), Silverlight checks the assembly for a resource with the right
name. That means this is all you need to use the resource shown in Figure:
<Image
Source="grandpiano.jpg"></Image>
Using Subfolders
It’s possible to use
the folders to group resource files in your project. This changes how the
resource is named. For example, consider Figure 6-6, which puts the
grandpiano.jpg file in a subfolder named Images.
Now, you need to use
this URI:
<Image
Source="Images/grandpiano.jpg"></Image>
Programmatically Retrieving a
Resource
Using
resources is easy when you have an element that supports Silverlight’s URI
standard, such as Image or MediaElement. However, in some situations, you need
to manipulate your resource in code before handing it off to an element, or you
may not want to use an element at all. For example, you may have some static
data in a text or binary file that’s stored as a resource. In your code, you
want to retrieve this file and process its data.
To
perform this task, you need the help of the Application.GetResourceStream()
method. It allows you to retrieve the data for a specific resource, which you
indicate by supplying the correct URI. The trick is that you need to use the
following URI format: AssemblyName;component/ResourceFileName For example, if you
have a resource named ProductList.bin in a project named
SilverlightApplication1, you use this line of code:
StreamResourceInfo sri
= Application.GetResourceStream(new Uri("SilverlightApplication1;component/ProductList.bin", UriKind.Relative));
The
GetResourceStream() method doesn’t retrieve a stream. Instead, it gets a
System.Windows.Resources.StreamResourceInfo object, which wraps a Stream
property (with
the underlying
stream) and a ContentType property (with the MIME type). Here’s the code that
creates a
BinaryReader object for the stream:
BinaryReader reader
= new BinaryReader(sri.Stream);
You can now use the
methods of the binary reader to pull each piece of data out of the file. The
same approach works with StreamReader (for text-based data) and XmlReader (for
XML data). But you have a slightly easier option when XML data is involved,
because the XmlReader.Create() method accepts either a stream or a URI string
that points to a resource. So, if you have a resource named ProductList.xml,
this code works:
StreamResourceInfo sri
= Application.GetResourceStream(
new Uri("SilverlightApplication1;component/ProductList.xml", UriKind.Relative));
XmlReader reader
= XmlReader.Create(sri.Stream, new XmlReaderSettings());
So does this more
streamlined approach:
XmlReader reader
= XmlReader.Create("ProductList.xml")
Class Library Assemblies
So
far, the examples you’ve seen in this book have placed all their code into a
single assembly. For a small or modest-sized Silverlight application, this
straightforward design makes good sense. But it’s not hard to imagine that you
might want to factor out certain functionality and place it in a separate class
library assembly. Usually, you’ll take this step because you want to reuse that
functionality with more than one Silverlight application. Alternatively, you
may want to break it out it so it can be coded, compiled, debugged, and revised
separately, which is particularly important if that code is being created by a
different development team. Creating a Silverlight class library is easy. It’s
essentially the same process you follow to create and use class library
assemblies in ordinary .NET applications. First, create a new project in Visual
Studio using the Silverlight Class Library project template. Then, add a
reference in your Silverlight application that points to that project or
assembly. The dependent assembly will be copied into the XAP package when you
build your application.
Using Resources in an Assembly
Class
libraries give you a handy way to share resources between applications. You can
embed a resource in a class library and then retrieve it in your application.
This technique is easy—the only trick is constructing the right URIs. To pull a
resource out of a library, you need to use a URI that includes the application
in this format:
/ClassLibraryName;component/ResourceFileName
This
is the same format you learned about earlier, in the section “Programmatically
Retrieving a Resource,” but with one addition: now, the URI begins with a
leading slash, which represents the root of the XAP file. This URI points to
the dependent assembly in that file and then indicates a resource in that
assembly. For example, consider the ResourceClassLibrary assembly in Figure. It
includes a resource named happyface.jpg, and that file has a build action of
Resource.
Here’s an image file
that uses the resource from the class library:
<Image
Source="/ResourceClassLibrary;component/happyface.jpg"></Image>
Downloading Assemblies on Demand
In
some situations, the code in a class library is used infrequently, or perhaps
not at all for certain users. If the class library contains a significant
amount of code or (more likely) has large embedded resources such as graphics,
including it with your application will increase the size of your XAP file and
lengthen download times needlessly. In this case, you may want to create a
separate component assembly—one that isn’t downloaded until you need it. This
scenario is similar to on-demand resource downloading. You place the separate
resource in a separate file outside of the XAP file but on the same website.
Before
you use assembly downloading, you need to make sure the dependent assembly
isn’t placed in the XAP file. To do so, select the project reference that
points to the assembly. In the Properties window, set Copy Local to false.
Next, make sure the assembly is copied to the same location as your website. If
you’re using an ASP.NET test website, that means you must add the assembly to
the ClientBin folder in the test website. (You can’t try this example with a
simple HTML test page, because WebClient doesn’t work when you run a
Silverlight application
from
the file system.)
To
implement on-demand downloading of assemblies, you need to use the WebClient
class you saw earlier, in conjunction with the AssemblyPart class. The
WebClient retrieves the assembly, and the AssemblyPart makes it available for
downloading:
string uri = Application.Current.Host.Source.AbsoluteUri;
int index
= uri.IndexOf("/ClientBin");
// In this example,
the URI includes the /ClientBin portion, because we've
// decided to place
the DLL in the ClientBin folder.
uri =
uri.Substring(0, index) + "/ClientBin/ResourceClassLibrary.dll";
// Begin the
download.
WebClient webClient
= new WebClient ();
webClient.OpenReadCompleted
+= webClient_OpenReadCompleted;
webClient.OpenReadAsync(new Uri(uri));
When the assembly is
downloaded, you use the AssemblyPart.Load() method to load it into
the current
application domain:
private void webClient_OpenReadCompleted
(object sender,
OpenReadCompletedEventArgs
e)
{
if (e.Error
!= null)
{
// (Add code to
display error or degrade gracefully.)
}
else
{
AssemblyPart assemblypart
= new AssemblyPart();
assemblypart.Load(e.Result);
}
}
Supporting Assembly Caching
As you learned,
assembly caching is a feature that allows Silverlight to download class library
assemblies and store them in the browser cache. This way, these assemblies
don’t need to be downloaded every time the application is launched.
By default, the assemblies you
build won’t support assembly caching. However, you can add this support by
satisfying two requirements. First, your assembly must have a strong name.
Second, your assembly needs a special type of XML file that describes its
contents, called an .extmap.xml file. The following sections walk you
through both requirements, and you can refer to the downloadable code for this
chapter to assembly caching in action with a custom assembly.
The Strong Key Name
To support assembly caching, your
class library assembly needs a strong name, which will uniquely identify it in
the browser cache and prevent naming conflicts. To create a strong key for your
assembly, follow these steps:
1. Double-click the Properties
item in the Solution Explorer.
2. Click the Signing tab.
3. Select the “Sign the assembly”
option.
4. In the “Choose a strong key
name” list, choose to show the Create Strong Name
Key dialog box.
5. To finish creating your key,
you’ll need to supply a file name (like MyKey.snk) and,
optionally, a password.
5.
Click
OK. Visual Studio will create the new key file and add it to your class library
project.
This creates a strong key file
and uses it for your assembly. From this point on, every time you compile your
project, Visual Studio uses the strong key to sign the final assembly. Before
you can continue to the next step, you need to know public key token of the key
pair that’s used to sign your assembly. Unfortunately, Visual Studio doesn’t
provide an easy way to get this information (at least not without a plug-in of
some sort). Instead, you need to resort to the sn.exe command-line tool. First,
choose Microsoft Visual Studio 2010 ➤ Visual
Studio Tools ➤ Visual Studio
Command Prompt. Once you’ve loaded the Visual Studio command prompt, change to
the directory that holds your key file. Then, run the following two commands (replacing
MyKey.snk with the name of your key):
sn –p MyKey.snk MyKey.bin
sn –t MyKey.bin
When you complete the second
command, you’ll see a message like this:
Microsoft (R) .NET Framework
Strong Name Utility Version 3.5.30729.1
Copyright (c) Microsoft
Corporation. All rights reserved.
Public key token is e6a351dca87c1032
The bold part is the piece of
information you need for the next step: creating a .extmap.xmlfile for
your assembly.
The .extmap.xml File
The .extmap.xml file
is an ordinary text file that holds XML content. It’s named to match your
assembly. For example, if you have a class library assembly named
CacheableAssembly.dll, you’ll need to create a file named
CacheableAssembly.extmap.xml. The presence of this file tells Silverlight that
your assembly supports assembly caching. To make life easy, you can add the
.extmap.xml file to your class library project. Select it in the Solution
Explorer, and set Build Action to None and the Copy to Output Directory setting
to “Copy always.” This ensures that the file will be placed in the same
directory as your assembly file when you compile it. Figure shows a class
library with the appropriate .extmap.xml file.
The
easiest way to create an .extmap.xml file is to take a sample (like the one
shown next), and modify it for your assembly. In the following listing, the
details you need to change are in bold:
<?xml
version="1.0"?>
<manifest
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<assembly>
<name>CacheableAssembly</name>
<version>1.0.0.0</version>
<publickeytoken>e6a351dca87c1032</publickeytoken>
<relpath>CacheableAssembly.dll</relpath>
<extension
downloadUri="CacheableAssembly.zip" />
</assembly>
</manifest>
The
name and version details are obvious, and they should match your assembly. The public
key token is the identifying fingerprint of the strong key that was used to
sign your assembly, and you collected it with the sn.exe tool in the previous
section. The relative path (relpath) is the exact file name of the assembly.
Finally, the downloadUri attribute provides the most important piece of
information—it tells the application where to find the packaged, downloadable
assembly.
You
have two options for setting downloadUri. The easiest approach is the one
that’s used in the previous example—simply supply a file name. When you switch
on assembly caching in your application, Visual Studio will take your class
library assembly (in this case, CacheableAssembly.dll), compress it, and place
the compressed file (in this case, CacheableAssembly.zip) alongside the compiled
XAP file. this is the approach that Silverlight’s add-on assemblies use.
Your other option is
to use an absolute URI for the downloadUri:
<extension
downloadUri="http://www.mysite.com/assemblies/v1.0/CacheableAssembly.zip"
/>
In this case,
Visual Studio won’t package up the assembly when you compile the application.
Instead, it expects you to have already placed the assembly at the web location
you’ve specified. This gives you a powerful way to share libraries between
multiple applications. However, the download location must be on the same
domain as the Silverlight application, or it must explicitly allow cross-domain
access. With the .extmap.xml file shown earlier, you’re ready to use assembly
caching. To try it, create an application that uses your class library. Turn on
assembly caching for your application by opening the project properties and
selecting the “Reduce XAP size by using
application
library caching” option. Finally, build your application. If you check out the contents
of your Debug folder, you’ll find the packaged up ZIP file for your assembly
(as shown
in Figure)
ASP.NET
Web Services
Some of the most interesting
Silverlight applications have a hidden backbone of server-side
code. They may call a web server
to retrieve data from a database, perform authentication, store
data in a central repository,
submit a time-consuming task, or perform any number of other
tasks that aren’t possible with
client-side code alone. The common ingredient in all these
examples is that they are based
on web services—libraries of server-side logic that any webcapable
application can access.
In this chapter, you’ll learn how
to create ASP.NET web services and call them from a
Silverlight application. You’ll
learn how to deal with different types of data, handle security, tap
into ASP.NET services, monitor
the client’s network connection, and even build a two-way web
service that calls your
application when it has something to report.
Building Web Services for
Silverlight
Without a doubt, the most
effective way for a Silverlight application to tap into server-side code is through web services. The
basic idea is simple: you include a web service with your ASP.NET website, and your Silverlight
application calls the methods in that web service. Your web services can provide
server-generated content that isn’t available on the client (or would be too computationally expensive to
calculate). Or, your web services can run queries and perform updates against a server-side
database. With a little extra work, it can
even use ASP.NET services such as
authentication, caching, and session state.
Silverlight applications can call
traditional ASP.NET web services (.asmx services) as well as
the WCF services, which are the
newer standard. In the following sections, you’ll learn how to
build, call, and refine a WCF
service.
Creating a Web Service
To create a WCF service in Visual
Studio, right-click your ASP.NET website in the Solution
Explorer, and choose Add New
Item. Choose the Silverlight-enabled WCF Service template,
enter a file name, and click Add.
When you add a new WCF service,
Visual Studio creates two files (see Figure )
The service endpoint: The service
endpoint has the extension .svc and is placed in your root website folder. For
example, if you create a web service named TestService, you get a file named
TestService.svc. When using the web service, the client requests a URL that points
to the .svc file. But the .svc file doesn’t contain any code—it includes one
line of markup that tells ASP.NET where to find the corresponding web service
code.
The service code: The service
code is placed in the App_Code folder of your website (if you’re creating a
projectless website) or in a separate code-behind file (if you’re creating a
web project). For example, if you create a web service named TestService, you
get a code file named TestService.cs in a projectless website or
TestService.svc.cs in a web project. Either way, the contents are the same: a
code file with a class that implements the service interface and provides the
actual code for your web service.
The
code file for your web service begins with two attributes. The ServiceContract
attribute indicates that it defines a service contract—in other words, a set of
methods that you plan to expose to remote callers as part of a service. The
AspNetCompatibilityRequirements attribute indicates that it will have access to
ASP.NET platform features such as session state:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode
=
AspNetCompatibilityRequirementsMode.Allowed)]
public class TestService
{ ... }
To
add a new web service method, you add a new method to the code file and make
sure it’s decorated with the OperationContract attribute. For example, if you
want to add a method that returns the current time on the server, you can
modify the interface like this:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode
=
AspNetCompatibilityRequirementsMode.Allowed)]
public class TestService
{
[OperationContract]
public DateTime
GetServerTime()
{
return DateTime.Now;
}
}
Initially,
a newly created service contains a single DoWork() web service method, which
Visual
Studio adds as an example. You’re free to delete or customize this method.
Adding a Service Reference
You consume a
web service in a Silverlight application in much the same way that you consume
one in a
full-fledged.NET application. The first step is to create a proxy class by
adding a Visual
Studio service
reference.
To add the
service reference, follow these steps:
1. Right-click
your Silverlight project in the Solution Explorer, and choose Add Service
Reference. The
Add Service Reference dialog box appears (see Figure).
2. In the
Address box, enter the URL that points to the web service, and click Go.
However, you probably won’t know the exact URL of your web service, because it
incorporates the randomly chosen port used by Visual Studio’s test web server
(as in http://localhost:4198/ASPWebSite/TestService.svc). You could run your web
application to find out, but an easier approach is to click the Discover
button, which automatically finds all the web services that are in your current
solution.
3. In the
Namespace box, enter the C# namespace that Visual Studio should use for
the automatically generated classes. This namespace is created inside your
project namespace. So if your project is MyClient and you give the web service
the namespace name WebServiceSite, the full namespace is
MyClient.WebServiceSite.
4. Click OK.
Visual Studio creates a proxy class that has the code for calling the web
service. To see the file that contains this code, select the Silverlight
project in the Solution Explorer, click the Show All Files button, expand the
namespace node (which uses the name you picked in step 3), then expand the
Service References node inside, then expand the Reference.svcmap node inside
that, and open the Reference.cs file. When you add a service reference, Visual
Studio creates a proxy class—a class that you can interact with to call
your web service. The proxy class is named after the original web service class
with the word Client added at the end.
For example,
when adding a reference to the TestService shown earlier, Visual Studio creates
a proxy class named TestServiceClient. The proxy class contains methods that
allow you to trigger the appropriate web service calls and all the events that
allow you to receive the results. It takes care of the heavy lifting (creating
the request message, sending it in an HTTP request, getting the response, and
then notifying your code).
You can update
your service reference at any time to taken into account web service changes
(such as new methods or changes to the number of type of method parameters). To
do so, recompile the web application, then right-click the service reference in
the Solution Explorer, and finally choose Update Service Reference.
Silverlight includes a
command-line utility that does the same work as Visual Studio’s service
reference feature. This utility is named slsvcutil.exe (for Silverlight Service
Model Proxy Generation Tool), and you can run it most easily from the Visual
Studio Command Prompt. For example, the following command creates the proxy
code for TestService example shown earlier (assuming the port number matches
the port that the test web server is currently using):
slsvcutil http://localhost:4198/ASPWebSite/TestService.svc?WSDL
The ?WSDL that’s appended to the
service is an web service convention. It tells ASP.NET to provide the Web
Service Description Language (WSDL) document that describes the web service.
This document details the public interface of the web service (its methods and
parameters) but doesn’t expose any private details about its code or inner
workings. The WSDL document has all the information Visual Studio or slsvcutil
needs to generate the proxy code.
Calling the Web Service
To use the proxy
class, start by importing the namespace that you specified for the service reference
in step 3. Assuming that you used the namespace MyWebServer and your project is
named MySilverlightProject, you’d need this statement:
using
MySilverlightProject.MyWebServer;
In Silverlight,
all web service calls must be asynchronous. That means you call a method to start
the call (and send off the request). This method returns immediately. Your code
can then carry on to perform other tasks, or the user can continue to interact
with the application. When the response is received, the proxy class triggers a
corresponding proxy class event, which is named in the form
MethodNameCompleted. You must handle this event to process the results.
Here’s how to call the
TestService.GetServerTime() method shown earlier:
// Create the proxy class.
TestServiceClient proxy = new
TestServiceClient();
// Attach an event handler to the
completed event.
proxy.GetServerTimeCompleted
+= new
EventHandler<GetServerTimeCompletedEventArgs>(GetServerTimeCompleted);
// Start the web
service call.
proxy.GetServerTimeAsync();
To
get the results, you need to handle the completed event and examine the
corresponding EventArgs object. When generating the proxy class, Visual Studio
also creates a different EventArgs class for each method. The only difference
is the Result property, which is typed to match the return value of the method.
For example, the GetServerTime() method works in conjunction with a
GetServerTimeCompletedEventArgs class that provides a DateTime object through
its Result property.
When
accessing the Result property for the first time, you need to use
exception-handling code. That’s because this is the point where an exception
will be thrown if the web service call failed—for example, the server couldn’t
be found, the web service method returned an error, or the connection timed
out. (As an alternative, you could check the Error property of the custom EventArgs
object. For example, if GetServerTimeCompletedEventArgs.Error is null, no error
occurred while processing the request, and it’s safe to get the data from the
Result property.) Here’s an event handler that reads the result (the current
date and time on the server) and displays it in a TextBlock:
private void GetServerTimeCompleted(object
sender,
GetServerTimeCompletedEventArgs
e)
{
try
{
lblTime.Text =
e.Result.ToLongTimeString();
}
catch (Exception
err)
{
lblTime.Text = "Error
contacting web service";
}
}
By default, the proxy
class waits for one minute before giving up if it doesn’t receive a response.
You can configure the timeout length by using code like this before you make
the web service call:
proxy.InnerChannel.OperationTimeout
= TimeSpan.FromSeconds(30);
Configuring the Web Service URL
When
you add a service reference, the automatically generated code includes the web service
URL. As a result, you don’t need to specify the URL when you create an instance
of the proxy class.
But
this raises a potential problem. All web service URLs are fully
qualified—relative paths aren’t allowed. If you’re using the test web server in
Visual Studio, that means you’ll run into trouble if you try to run your
application at a later point, when the test web server has chosen a different
port number. Similarly, you’ll need to update the URL when you deploy your
final application to a production web server. You can solve this problem by
updating the service reference (and thereby regenerating all the proxy code),
but there are two easier options.
Your
first option is to configure Visual Studio to always use a specific port when
running its test web server with your web application. This works only if
you’ve created your web application as a web project (not a projectless
website). In this case, you can configure the test web server for your project
by double-clicking the Properties item in the Solution Explorer. Choose the Web
tab. Then, in the Servers section, select “Specific port” and enter the port number
you’d like to use. (You may as well choose the port number that the test server
is already using for this session.) In the settings shown in Figure, that port
number is 54752.
Now you can modify
the code that creates your proxy class. Instead of simply using this, which
assumes the service is at the same port that it occupied when you added the
reference:
TestServiceClient proxy
= new TestServiceClient();
You can explicitly
set the port with the EndpointAddress class:
// Create the proxy
class.
TestServiceClient proxy
= new TestServiceClient();
// Use the port
that's hard-coded in the project properties.
EndpointAddress address
= new EndpointAddress(
"http://localhost:54752/ASPWebSite/TestService.svc");
// Apply the new
URI.
proxy.Endpoint.Address
= address;
Your second option is
to change the address dynamically in your code so that it’s synchronized with
the port number that the test web server is currently using. To do so, you simply
need to grab the URL of the Silverlight page and find its port number (because
the Silverlight page is hosted on the same web server as the web service). Here’s
the code that does the trick:
// Create a new URL
for the TestService.svc service using the current port number.
EndpointAddress address
= new EndpointAddress("http://localhost:"
+
HtmlPage.Document.DocumentUri.Port
+ "/ASPWebSite/TestService.svc");
// Use the new
address with the proxy object.
TestServiceClient proxy
= new TestServiceClient();
proxy.Endpoint.Address
= address;
You
can use similar code to create a URL based on the current Silverlight page so
that the web service continues to work no matter where you deploy it, as long
as you keep the web service and Silverlight application together in the same
web folder.
_________________________________________________________________________________
Reach us At: - 0120-4029000; 0120-4029024; 0120-4029025,
0120-4029027; 0120-4029029
Mbl: 9953584548




























No comments:
Post a Comment