You’ll see how to pass in initialization parameters, show a custom splash screen, and break free from the confines of the browser to run your Silverlight application in a stand-alone win
Trang 1}
}
You then decide to build an AutoCompleteBox that attempts to match the user’s text with a Product object In preparation for this step, you fill the AutoComplexBox.ItemsSource collection with product objects:
Product[] products = new []{
new Product("Peanut Butter Applicator", "C_PBA-01"),
new Product("Pelvic Strengthener", "C_PVS-309"), };
acbProduct.ItemsSource = products;
If you take no further steps, the AutoCompleteBox will use its standard behavior As the user types, it will call ToString() on each Product object It will then use that text to perform its suggestion filtering Because the Product class overrides the ToString() method to return the product name, the AutoCompleteBox will attempt to match the user’s text with a product name, which is perfectly reasonable
However, if you perform custom filtering you can get a bit more sophisticated For
example, you can check if the user’s text matches the ProductName property or the
ProductCode property and deem the Product object as a match either way Here’s an example
of the custom filtering logic that does the trick:
public bool ProductItemFilter( string text, object item)
{
Product product = (Product)item;
// Call it a match if the typed-in text appears in the product code
// or at the beginning of the product name
Now if the user types the text PBA, it matches the product code C_PBA-01 and see the
matching item Peanut Butter Applicator in the list of suggestions, as shown in Figure 5-15
Trang 2Figure 5-15 A custom search that matches product codes
Dynamic Item Lists
So far, you’ve used the ItemsSource property to fill the AutoCompleteBox with a collection of
suggestions For this to work, you must have the complete list and it must be a manageable size
If you need to pull the information from somewhere else or the list is large enough that it isn’t
practical to load the whole thing at once, you’ll need to take a different approach to filling the
AutoCompleteBox Instead of setting the ItemsSource property when the page is first created,
you’ll need to set it in real time, as the user types
To do so, set the FilterMode property to None, and handle the Populating event The
Populating event fires whenever the AutoCompleteBox is ready to search for results By default,
this happens every time the user presses a key and changes the current text entry You can
make the AutoCompleteBox somewhat more relaxed using the MinimumPrefixLength and
MinimumPopupDelay properties that are discussed at the end of this section
<input:AutoCompleteBox : Name ="acbProducts" FilterMode ="None"
Populating ="acbProducts_Populating" ></ input : AutoCompleteBox >
When the Populating event fires, you have two choices: set the ItemsSource property
immediately or launch an asynchronous process to do it Setting the ItemsSource property
immediately makes sense if you have the list of suggestions on hand or you can generate them
quickly The list of suggestions will then appear in the drop-down list right away
But in many situations, you’ll need a potentially time-consuming step to get the list of
suggestions, such as performing a series of calculations or querying a web service In this
situation, you need to launch an asynchronous process Although you can accomplish this with
the multithreading support that’s described in Chapter 19, you won’t necessarily need to Some
Silverlight features have built-in asynchronous support This is the case with Silverlight’s
implementation of web services, which is hard-wired to use asynchronous calls exclusively
When using an asynchronous operation, you need to explicitly cancel the normal
processing in the Populating event handler, by setting PopulatingEventArgs.Cancel to true You
can then launch the asynchronous operation The following example gets the suggestion list
asynchronously from a web service (You’ll learn much more about coding and consuming web
services in Chapter 15 For now, you can review the example code and the downloadable
project with this chapter.)
private void acbProduct_Populating( object sender, PopulatingEventArgs e)
Trang 3// Signal that the task is being performed asynchronously
e.Cancel = true;
// Create the web service object
ProductAutoCompleteClient service = new ProductAutoCompleteClient();
// Get the products (for example, from a server-side database)
Product[] products = GetProducts();
// Create a collection of matches
List< string > productMatches = new List< string >();
foreach (Product product in products)
private void GetProductMatchesCompleted( object sender,
Trang 4acbProducts.ItemsSource = e.Result;
// Notify the control that the data has arrived
acbProducts.PopulateComplete();
}
When filling the AutoCompleteBox with a time-consuming or asynchronous step,
there are two properties you may want to adjust: MinimumPrefixLength and
MinimumPopupDelay MinimumPrefixLength determines how much text must be typed in
before the AutoCompleteBox gives its suggestions Ordinarily, the AutoCompleteBox offers
suggestions after the first letter is entered If you want it to wait for three letters (the standard
used by many of the Ajax-powered auto-completion text boxes that you’ll find on the Web), set
MinimumPrefixLength to 3 Similarly, you can force the AutoCompleteBox to hold off until a
certain interval of time has passed since the user’s last keystroke using the
MinimumPopulateDelay property This way, you won’t waste time with a flurry of overlapping
calls to a slow web service Of course, this doesn’t necessarily determine how long it takes for
the suggestions to appear–that depends on the wait before initiating the query and then the
time needed to contact the web server and receive a response
Range-Based Controls
Silverlight includes three controls that use the concept of a range These controls take a
numeric value that falls in between a specific minimum and maximum value These controls–
ScrollBar, Slider, and ProgressBar–derive from the RangeBase class (which itself derives from
the Control class) The RangeBase class adds a ValueChanged event, a Tooltip property, and the
range properties shown in Table 5-5
Table 5-5 Properties of the RangeBase Class
Name Description
Value This is the current value of the control (which must fall between the minimum
and maximum) By default, it starts at 0 Contrary to what you might expect,
Value isn’t an integer–it’s a double, so it accepts fractional values You can
react to the ValueChanged event if you want to be notified when the value is
changed
Maximum This is the upper limit (the largest allowed value) The default value is 1
Minimum This is the lower limit (the smallest allowed value) The default value is 0
SmallChange This is the amount the Value property is adjusted up or down for a “small
change.” The meaning of a small change depends on the control (and may not
be used at all) For the ScrollBar and Slider, this is the amount the value
changes when you use the arrow keys For the ScrollBar, you can also use the
arrow buttons at either end of the bar The default SmallChange is 0.1
Trang 5Name Description
LargeChange This is the amount the Value property is adjusted up or down for a “large
change.” The meaning of a large change depends on the control (and may not
be used at all) For the ScrollBar and Slider, this is the amount the value changes when you use the Page Up and Page Down keys or when you click the bar on either side of the thumb (which indicates the current position) The default LargeChange is 1
Ordinarily, there’s no need to use the ScrollBar control directly The higher-level ScrollViewer control, which wraps two ScrollBar controls, is typically much more useful (The ScrollViewer was covered in Chapter 3.) However, the Slider and ProgressBar are more useful
on their own
The Slider
The Slider is a specialized control that’s occasionally useful You might use it to set numeric values in situations where the number itself isn’t particularly significant For example, it makes sense to set the volume in a media player by dragging the thumb in a slider bar from side to side The general position of the thumb indicates the relative loudness (normal, quiet, loud), but the underlying number has no meaning to the user
Here’s an example that creates the horizontal slider shown in Figure 5-16:
<Slider Orientation ="Horizontal" Minimum ="0" Maximum ="10" Width ="100" />
Unlike WPF, the Silverlight slider doesn’t provide any properties for adding tick marks However, as with any control, you can change its appearance while leaving its functionality intact using the control template feature described in Chapter 13
Figure 5-16 A basic slider
The ProgressBar
The ProgressBar indicates the progress of a long-running task Unlike the slider, the
ProgressBar isn’t user interactive Instead, it’s up to your code to periodically increment the
Trang 6Value property By default, the Minimum value of a ProgressBar is 0, and the Maximum value is
100, so the Value corresponds to the percentage of work done You’ll see an example with the
ProgressBar in Chapter 6, with a page that downloads a file from the Web and shows its
progress on the way
One neat trick that you can perform with the ProgressBar is using it to show a
long-running status indicator, even if you don’t know how long the task will take You do this by
setting the IsIndeterminate property to true:
<ProgressBar Height ="18" Width ="200" IsIndeterminate ="True"></ ProgressBar >
When setting IsIndeterminate, you no longer use the Minimum, Maximum, and Value
properties No matter what values these properties have, the ProgressBar will show a hatched
pattern that travels con-tinuously from left to right This pattern indicates that there’s work in
progress, but it doesn’t provide any information about how much progress has been made so
far
Date Controls
Silverlight adds two date controls, neither of which exists in the WPF control library Both are
designed to allow the user to choose a single date
The Calendar control displays a calendar that’s similar to what you see in the Windows
operating system (for example, when you configure the system date) It shows a single month at
a time and allows you to step through from month to month (by clicking the arrow buttons) or
jump to a specific month (by clicking the month header to view an entire year, and then
clicking the month)
The DatePicker requires less space It’s modeled after a simple text box, which holds a
date string in long or short date format However, the DatePicker provides a drop-down arrow
that, when clicked, pops open a full calendar view that’s identical to that shown by the Calendar
control This pop-up is displayed over top of any other content, just like a drop-down combo
box
Figure 5-17 shows the two display modes that the Calendar supports, and the two date
formats that the DatePicker allows
Trang 7Figure 5-17 The Calendar and DatePicker
The Calendar and DatePicker include properties that allow you to determine which dates are shown and which dates are selectable (provided they fall in a contiguous range) Table 5-6 lists the properties you can use
Trang 8Table 5-6 Properties of the Calendar and DatePicker Classes
DisplayDateStart to DateTime.MinValue and DisplayDateEnd to DateTime.MaxValue
BlackoutDates Holds a collection of dates that will be disabled in the calendar and
won’t be selectable If these dates are not in the range of displayed dates, or if one of these dates is already selected, you’ll receive an exception To prevent selection of any date in the past, call the
BlackoutDates.AddDatesInPast() method
SelectedDate Provides the selected date as a DateTime object (or a null value if no
date is selected) It can be set programmatically, by the user clicking the date in the calendar, or by the user typing in a date string (in the DatePicker) In the calendar view, the selected date is marked by a shaded square, which is only visible when the date control has focus
SelectedDates Provides the selected dates as a collection of DateTime objects This
property is supported by the Calendar, and it’s only useful if you’ve changed the SelectionMode property to allow multiple date selection
DisplayDate Determines the date that’s displayed initially in the calendar view (using
a DateTime object) If null, the SelectedDate is shown If DisplayDate and SelectedDate are both null, the current date is used The display date determines the initial month page of the calendar view When the date control has focus, a square outline is displayed around the appropriate day in that month (which is different than the shaded square used for the currently selected date)
FirstDayOfWeek Determines the day of the week that will be displayed at the start of each
calendar row, in the leftmost position
IsTodayHighlighted Determines whether the calendar view uses highlighting to point out
the current date
DisplayMode
(Calendar only)
Determines the initial display month of the calendar If set to Month, the Calendar shows the standard single-month view If set to Year, the Calendar shows the months in the current year (similar to when the user clicks the month header) Once the user clicks a month, the Calendar shows the full calendar view for that month
Trang 9Property Description
(Calendar only) include None (selection is disabled entirely), SingleRange (a contiguous
group of dates can be selected), and MultipleRange (any combination of dates can be selected) In SingleRange or MultipleRange modes, the user can drag to select multiple dates, or click while holding down the Ctrl key You can use the SelectedDates property to get a collection with all the selected dates
IsDropDownOpen
(DatePicker only)
Determines whether the calendar view drop-down is open in the DatePicker You can set this property programmatically to show or hide the calendar
SelectedDateFormat
(DatePicker only)
Determines how the selected date will be displayed in the text part of the DatePicker You can choose Short or Long The actual display format is based on the client computer’s regional settings For example,
if you use Short, the date might be rendered in the yyyy/mm/dd format
or dd/mm/yyyy The long format generally includes the month and day names
The date controls also provide a few different events Most useful is SelectedDateChanged (in the DatePicker) or the very similar SelectedDatesChanged (in the Calendar), which adds support for multiple date selection You can react to these events to reject specific date selections, such as dates that fall on a weekend:
private void Calendar_SelectedDatesChanged ( object sender,
CalendarDateChangedEventArgs e)
{
// Check all the newly added items
foreach (DateTime selectedDate in e.AddedItems)
{
if ((selectedDate.DayOfWeek == DayOfWeek.Saturday) ||
(selectedDate.DayOfWeek == DayOfWeek.Sunday))
{
lblError.Text = "Weekends are not allowed";
// Remove the selected date
The Calendar also adds a DisplayDateChanged event (when the user browses to a new month) The DatePicker adds CalendarOpened and CalendarClosed events (which fire when the calendar drop-down is displayed and closed) and a DateValidationError event (which fires when the user types a value in the text entry portion that can’t be interpreted as a valid date) Ordinarily, invalid values are discarded when the user opens the calendar view, but here’s an option that fills in some text to alert the user of the problem:
Trang 10private void DatePicker_DateValidationError( object sender,
The Last Word
In this chapter, you saw all the fundamental Silverlight elements You considered several
categories:
• The TextBlock, which allows you to display richly formatted text using built-in and
custom fonts
• The Image, which allows you to show JPEG and PNG images
• Content controls that can contain nested elements, including various types of buttons
and the ToolTip
• List controls that contain a collection of items, such as the ListBox, ComboBox, and
TabControl
• Text controls, including the standard TextBox, the PasswordBox, and the
AutoCompleteBox
• Range-based controls that take a numeric value from a range, such as the Slider
• The date controls, which allow the user to select one or more dates from a calendar
display
Although you haven’t had an exhaustive look at every detail of XAML markup, you’ve
learned enough to reap all its benefits Now, your attention can shift to the Silverlight
technology itself, which holds some of the most interesting surprises In the next chapter, you’ll
start out by considering the core of the Silverlight application model: the Application class
Trang 12CHAPTER 6
■ ■ ■
The Application Model
Over the past five chapters, you’ve taken a detailed look at the different visual ingredients you
can put inside a Silverlight page You’ve learned how to use layout containers and common
controls, and how to respond to mouse and keyboard events Now, it’s time to take a closer look
at the Silverlight application model–the scaffolding that shapes how Silverlight applications
are deployed, downloaded, and hosted
You’ll begin by considering the lifecycle of a Silverlight application You’ll examine the
events that fire when your application is created, unloaded, or runs into trouble with an
unhandled exception Next, you’ll pick up a few practical techniques that help you extend your
application beyond Silverlight’s basic behavior You’ll see how to pass in initialization
parameters, show a custom splash screen, and break free from the confines of the browser to
run your Silverlight application in a stand-alone window–even when the client computer can’t
get a network connection
Finally, you’ll explore the many options Silverlight provides for efficiently retrieving
the large files called binary resources, whether they’re images, video, or other assemblies that
your application requires You’ll learn two strategies for dealing with resources: including them
in your application package for easy deployment, and downloading them on demand to
streamline performance
■ What’s New In this chapter, you’ll encounter two new features First, Silverlight 3 adds the ability to
create out-of-browser applications—applications that the user can install locally and run in a stand-alone
window Clients can even use stand-alone applications when they don’t have a connection to the Internet You’ll
learn about this feature in the “Out-of-Browser Applications” section Silverlight 3 also supports assembly
caching, which you first learned about in Chapter 1 You’ll learn how your own assemblies can take advantage of
assembly caching in the “Supporting Assembly Caching” section
The Application Class
In Chapter 1, 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
Trang 13public 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 add properties that store global data You’ll see the App class used this way in examples throughout this book
Application Properties
Along with the static Current property, the Application class also provides several more
members, as described in Table 6-1
Table 6-1 Members of the Application Class
Member Description
Host This property lets you interact with the browser and, through it, the
rest of the HTML content on the web page It’s discussed in Chapter
14
Resources This property provides access to the collection of XAML resources
that are declared in App.xaml, as described in Chapter 2
RootVisual This property provides access to the root visual for your application–
typically, the user control that’s created when your application first starts Once set, the root visual can’t be changed, although you can manipulate the content in the root visual to change what’s displayed
in the page For example, if it’s the Grid control, you can remove one
or more of its current children and insert new controls in their place Chapter 7 demonstrates this technique
Trang 14ApplicationLifetime-Objects
This property holds a collection of application extension services
These are objects that provide additional respond to application events, in much the same way as your event handling code in the Application class The difference is that the code for an application extension service is separated into its own class, which makes it easier
to reuse this code in more than one Silverlight application
GetResourceStream() This static method is used to retrieve resources in code You’ll see
how to use it later in this chapter, in the “Resources” section
LoadComponent() This static method accepts a XAML file and instantiates the
corresponding elements (much as Silverlight does automatically when you create a page class and the constructor calls the InitializeComponent() method)
Along with these properties and methods, the Application object also raises events at
various points in the lifecycle of your application You’ll explore these next
Application Events
In Chapter 1, 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
Trang 153 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 you’ll explore later in this chapter
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"
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:
Trang 16private 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
user-specific or session-user-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
■ Note For more detailed interactions between the HTML and your Silverlight application—for example, to
pass information back and forth while your Silverlight application is running—see Chapter 14
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 <param>
Trang 17initParams 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" />
string view = e.InitParams["viewMode"];
if (view == ""Employee"") this viewMode = ViewMode.Employee;
}
// Create the root page
this RootVisual = new Page();
lblViewMode.Text = "Current view mode: " +
((App)Application.Current).ViewMode.ToString();
Figure 6-1 shows what you’ll see when you run the test page that uses the Customer view mode
Trang 18Figure 6-1 Displaying an initialization parameter
If you have more than one initialization parameter, pass them all in one
comma-delimited string Initialization values should be made up of alphanumeric characters There’s
currently no support for escaping special characters like commas in parameter values:
< param name ="initParams" value ="startPage=Page1,viewMode=Customer" />
Now, the event handler for the Startup event can retrieve the StartPage value and use it
to choose the application’s root page You can load the correct page using a block of conditional
logic that distinguishes between the available choices, or you can write a more general solution
that uses reflection to attempt to create the class with the requested name, as shown here:
UserControl startPage = null ;
// Create an instance of the page
Type type = this GetType();
Assembly assembly = type.Assembly;
// If no parameter was supplied or the class couldn't be created, use a default
if (startPage == null ) startPage = new MenuPage();
this RootVisual = startPage;
Trang 19Application Shutdown
At some point, your Silverlight application ends Most commonly, this occurs when the user surfs to another page in the web browser or closes the browser window It also occurs if the users refreshes the page (effectively abandoning the current instance of the application and launching a new one), if the page runs JavaScript code that removes the Silverlight content region or changes its source, or an unhandled exception derails your code
Just before the application is released from memory, Silverlight gives you the chance to run some code by responding to the Application.Exit event This event is commonly used to store user-specific information locally in isolated storage (see Chapter 18), so it’s available the next time the user runs your application
The Exit event doesn’t provide any additional information in its event arguments
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 than 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,
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:
Trang 20MainPage 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 (which indicates that the application is running in the Visual
Studio debug environment) If there’s no debugger, the code handles the error (rendering it
harmless) and uses the HTML interoperability features you’ll learn about in Chapter 14 to raise
a JavaScript error in its place Here’s the slightly simplified code that shows how the process
// 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
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
Trang 21Figure 6-2 A JavaScript error that represents an unhandled Silverlight exception
When you finish developing your application, you need to tweak the automatically generated error-handling code That’s because it isn’t acceptable to indiscriminately ignore all errors–doing so allows bugs to flourish and cause other usability problems or data errors further down the road Instead, consider selectively ignoring errors that correspond to known error conditions and signaling the problem to the user
■ Caution It’s easy to forget that you need to tweak the Application.UnhandledException event handler, because it only springs into action when you run your Silverlight application without a debugger When you’re testing your application in Visual Studio, you don’t see this behavior—instead, any unhandled exception ends the application immediately
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 6-3)
Trang 22Figure 6-3 The built-in Silverlight splash screen
If you don’t like the stock splash screen, you can easily create your own (see Figure
6-4) 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
Trang 23Figure 6-4 A custom splash screen
The XAML file for your splash screen can’t be a part of your Silverlight XAP file That’s because the splash screen needs to be shown while the XAP file is still in the process of being downloaded For that reason, the splash screen XAML must be a separate file that’s placed alongside your XAP file at the same web location
■ Note Testing a custom splash screen requires some work Ordinarily, you don’t see the splash screen
during testing because the application is sent to the browser too quickly To slow down your application enough
to see the splash screen, you need to first ensure that you’re using an ASP.NET test website, which ensures that your Silverlight application is hosted by Visual Studio test web server (as described in Chapter 1) Then, you need to add multiple large resource files to your Silverlight project—say, a handful of MP3 files—and set the build action of each one to Resource so it’s added to the XAP file Another trick is to temporarily remove the line
of code in the Application_Startup() method that sets the root visual for your application This way, after your application has been completely downloaded, it won’t display anything Instead, the splash screen will remain visible, displaying a progress percentage of 100%
To create the example shown in Figure 6-4, begin by creating a new Silverlight project with an ASP.NET test website, as described in Chapter 1 Then, add a new XAML file to your
Trang 24ASP.NET website (not the Silverlight project) To do so, select the ASP.NET website in the
Solution Explorer, and choose Website ➤ Add New Item Choose the Silverlight group and
select the Silverlight JScript page template Then enter a name and click Add This XAML file
will hold the markup for your splash screen
When you add a new XAML file, Visual Studio creates a basic XAML skeleton that
defines a Canvas That’s because Visual Studio assumes you’re building a Silverlight 1.0
application, which supports a much smaller set of elements and doesn’t include any of the
more advanced layout containers But you can use any of the core Silverlight elements–that is,
elements that are in the built-in assemblies and don’t require a separate download You can’t
use elements that are defined in the add-on System.Windows.Controls.dll assembly, or those in
any other assembly that needs to be packaged in the XAP and downloaded by the client
■ Tip The easiest way to build a simple splash screen is to create it in your Silverlight project and then copy
the markup into the splash screen file on your website This way, you can take advantage of the Visual Studio
design surface and XAML IntelliSense, which won’t be available if you write the markup directly in your ASP.NET
website
Here’s the XAML for the splash screen shown in Figure 6-4 It includes a Grid with a
TextBlock and two Rectangle elements (Rectangle is a shape-drawing element you’ll learn
about in Chapter 8.) The first rectangle paints the background of the progress bar, and the
second paints the foreground The two Rectangle objects are placed together in a single-celled
grid so that one rectangle is superimposed over the other:
<Grid xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns : ="http://schemas.microsoft.com/winfx/2006/xaml">
< StackPanel VerticalAlignment ="Center">
< Grid >
< Rectangle : Name ="progressBarBackground" Fill ="White" Stroke ="Black"
StrokeThickness ="1" Height ="30" Width ="200"></ Rectangle >
< Rectangle : Name ="progressBar" Fill ="Yellow" Height ="28" Width ="0">
</ Rectangle >
</ Grid >
< TextBlock : Name ="progressText" HorizontalAlignment ="Center"
Text ="0% downloaded "></ TextBlock >
</ StackPanel >
</ Grid >
Next, you need to add a JavaScript function to your HTML entry page or ASP.NET test
page (If you plan to use both, place the JavaScript function in a separate file and then link to it
in both files, using the source attribute of the script block.) The JavaScript code can look up
named elements on the page using the sender.findName() method and manipulate their
properties It can also determine the current progress using the eventArgs.progress property In
this example, the event-handling code updates the text and widens the progress bar based on
the current progress percentage:
< script type ="text/javascript">
function onSourceDownloadProgressChanged(sender, eventArgs)
Trang 25■ Note The splash-screen example that’s included with the downloadable code uses a slightly more
advanced technique that draws on a transform, a concept you’ll explore in Chapter 9 This approach allows you
to create a progress-bar effect without hard-coding the maximum width, so the progress bar is sized to fit the current browser window
To use this splash screen, you need to add the splashscreensource parameter to identify your XAML splash screen and the onsourcedownloadprogresschanged parameter to hook up your JavaScript event handler If you want to react when the download is finished, you can hook up a different JavaScript event handler using the onsourcedownloadcomplete parameter:
<object data ="data:application/x-silverlight," type ="application/x-silverlight-2" width ="100%" height ="100%">
< param name ="source" value ="ClientBin/SplashScreen.xap"/>
< param name ="onerror" value ="onSilverlightError" />
< param name ="background" value ="white" />
<param name="splashscreensource" value="SplashScreen.xaml" />
can duplicate many of these effects with an ordinary Silverlight splash screen, like the one
described here However, some are extremely difficult Most would be far easier to achieve after
you’ve downloaded your application, such as code-heavy animations
If you want more flexibility to create an eye-catching splash screen, you need to use a completely different technique First, make your application as small as possible Move its functionality to class-library assemblies, and place large resources (like graphics and videos) in separate files or in separate class-library assemblies Now that your application is stripped down to a hollow shell, it can be downloaded quickly After it’s downloaded, your application can show its fancy preloader and start the real work–programmatically downloading the resources and assemblies it needs to function
Designing an application this way takes more work, but you’ll get all the information you need to perform dynamic downloads in the following sections Pay particular attention to the last section in this chapter, "Downloading Assemblies on Demand."
Trang 26Out-of-Browser Applications
As you already know, the code for every Silverlight application is contained in a XAP file The
browser plug-in downloads this file from the web server and executes it on the client After this
point, there’s no requirement for the web server to get involved again–all the code runs on the
local computer
This design raises an interesting possibility Although Silverlight applications depend
on the Silverlight browser plug-in, there’s no technical reason that they need to be embedded in
a live web page In fact, as long as there’s a reliable way to run the Silverlight plug-in outside of
a browser, it’s also possible to run a Silverlight application on its own Flash developers have
had this ability for some time, and now Silverlight 3 adds a superficially similar but differently
implemented feature for running applications outside of the browser Figure 6-5 shows the
EightBall application from Chapter 2, running as an out-of-browser application
Figure 6-5 The EightBall application outside of the browser
The first detail you should understand about Silverlight out-of-browser applications is
that despite their name, they don’t run without the browser Instead, out-of-browser
applications conceal the browser’s presence When you run one, a specialized Silverlight tool
named sllauncher.exe (which you can find in a directory like c:\Program Files\Microsoft
Silverlight\3.0.40723.0 on a Windows computer) creates a stand-alone window that hosts a
browser control inside This browser window is stripped down to little more than a bare frame
and includes none of the standard browser user interface (which means no toolbar, favorites,
navigation buttons, and so on) Unfortunately, Silverlight gives you no ability to control the
initial size and placement, or the design of the sllauncher.exe window
■ Note Out-of-browser applications work on all supported Silverlight platforms, including Windows and Mac
computers
Trang 27Given that out-of-browser applications are really just slick illusions (and given your limited control over the stand-alone window, they’re not even that slick), why would you use them? There are several good reasons:
• To get a desktop presence An out-of-browser application must be “installed” through a
lightweight process that downloads the XAP file (as usual) and adds desktop and Start menu shortcuts If you want to give your users the ability to launch a Silverlight application this way, rather than forcing them to load up a browser and navigate to the appropriate URL, an out-of-browser application makes sense
• To allow the application to run when offline Ordinarily, Silverlight applications are
accessed through a web page, and that web page is on a public or private network As a result, clients can’t run the application unless they have a connection But after going through the install process for an out-of-browser application, a copy is cached locally and permanently (unless the user explicitly removes the application)
• To support intermittent connectivity This is similar to the previous point but represents
an even more common scenario Many clients–particularly those who use laptops and access the Internet through a wireless connection–have periods of connectivity interrupted with periodic disconnections Using an out-of-browser application (and the network detection features described in Chapter 15), you can create an application that deals with both scenarios When it’s connected, it can call web services to retrieve updated data and perform server actions When disconnected, it remains self-sufficient and allows the user to keep working
The architects of Silverlight expect that the third scenario will be the most common In other words, out-of-browser applications are a way to create an application that gracefully deals with inconsistent connectivity on the client end
■ Note Although out-of-browser applications have the same feature set as in-browser applications, there is
one difference: their initial allotment of file space As you’ll learn in Chapter 18, every Silverlight application gets its own carefully walled-off area of disk space where it can create and store files Ordinary in-browser
applications get a mere 1 MB of disk space (although they can request more by prompting the user) But browser applications start with a significantly increased quota of 25 MB, which means that in many cases they won’t need to ask the user for additional space To learn more about isolated storage, refer to Chapter 18
out-of-THE LIMITATIONS OF OUT-OF-BROWSER APPLICATIONS
It’s also important to understand what the out-of-browser application feature isn’t intended to accomplish Notably, it’s not a competitor for desktop applications, and it doesn’t add any new features that your application can exploit Out-of-browser applications are limited by exactly the same security sandbox as ordinary browser-based applications They have no way to perform
Trang 28desktop interactions (like drag-and-drop), no file access outside of isolated storage (see Chapter
18), no support for local databases, and no ability to interact with other applications
For all these reasons, Silverlight out-of-browser applications aren’t a competitor to
technologies like Adobe AIR, which allows developers to create desktop-like applications using
web programming technologies like Flash This isn’t an oversight—Microsoft has specifically
created Silverlight out-of-browser applications to support the limited range of scenarios just
described If you want a more capable desktop platform, you’ll be well advised to step up to
Silverlight’s amazingly capable big brother: WPF Although WPF is tied to the Windows platform,
it offers a wealth of rich client features that are lacking in browser-based technologies like
Silverlight and Flash
Enabling Out-of-Browser Support
In order to run a Silverlight application outside of the browser window, the user must first
install it locally But before that’s possible, you must specifically allow this feature To do so,
follow these steps:
1 Double-click the Properties item in the Solution Explorer to show the application
configuration options
2 Click the Silverlight tab
3 Select the “Enable running application out of the browser” setting
4 Optionally, click the Out-of-Browser Settings button to show a window where you can
set additional options (see Figure 6-6)
Trang 29Figure 6-6 Configuring out-of-browser settings
The additional options that you can set in the Out-of-Browser Settings window include the following:
• Window Title: Name used in the title bar of the window when the application is running
outside of the browser
• Width and Height: The initial size of the window for the out-of-browser application The
user can resize the window after the application launches If you don’t supply width and height values, Silverlight creates a window that’s 800 pixels wide and 600 pixels tall
• Shortcut name: Name used in the installation window and in any shortcuts that are
created
• Download description: Descriptive text that describes the application It appears in a
tooltip when you hover over the application shortcut
• Icons: Allows you to customize the images used for the installation window and the
shortcuts, as described a bit later in the “Customizing Icons” section
Trang 30• Enable GPU Acceleration: Determines whether the out-of-browser will support the
video-card caching that boosts performance in some scenarios Selecting this check box
simply gives you the option to use hardware acceleration–it’s still up to your elements
to opt in when appropriate, as described in Chapter 10
The values you enter are placed in a file named OutOfBrowserSettings.xml and saved
with your project
Once you’ve performed these steps, your application gains the ability to be installed
locally and then launched outside of the browser However, it doesn’t need to be installed–the
user can continue running it as a standard Silverlight application in the browser
Installing an Out-of-Browser Application
There are two ways to install an application that has out-of-browser capability The first option
is for the user to perform this step explicitly To do so, the user must request the entry page for
the application, right-click the Silverlight content region, and choose “Install
[ApplicationShortName] on this computer,” as shown in Figure 6-7
Figure 6-7 Explicitly installing an out-of-browser application
The other option is to start the installation process programmatically by calling the
Application.Install() method You must call this method in an event handler that responds to a
user-initiated action (like clicking a button) The Install() method returns true if the user
accepts the install prompt and continues or false if the user declines to install the application
The Install() method has one potential problem: it throws an exception if the
application is already installed on the local computer, even if the user is currently running the
application in the browser To avoid this error, you should check the Application.InstallState
value before calling Install() Here’s the complete process, launced in response to a button click:
private void cmdInstall_Click( object sender, RoutedEventArgs e)
{
// Make sure that the application is not already installed
Trang 31// Attempt to install it
bool installAccepted = Application.Current.Install();
ShowInstallMenuItem ="False"
When an application is installed, either through the user’s choice or the Install() method, several things happen First, an installation window appears (see Figure 6-8) that asks the user for confirmation
Figure 6-8 Installing the EightBall application
The installation window performs several services:
• It provides the name of the application and indicates the domain where it’s hosted (or shows file:// for an application that’s executed locally, without an ASP.NET test website)
Trang 32• It provides a More Information link which, if clicked, launches another browser window
and navigates to a page on the Silverlight website that describes the out-of-browser
feature
• It allows the user to choose whether the installation should create a Start menu shortcut
(which is checked by default) and a desktop shortcut (which isn’t) If the user clears both
check boxes, the OK button becomes disabled and the install can’t continue It’s also
worth noting that the Start menu shortcut appears in the first level of the Start menu
(not in a subgroup)
■ Note The install window looks slightly different on a Mac, to follow the conventions of that platform For
example, it doesn’t include check boxes for creating shortcuts Instead, Mac users are expected to drag the
installed application bundle into the location of their choice after the install completes, much as they would do
when installing any other application
The most important feature of the installation window is that it explains what’s about
to happen in clear, nonthreatening terms It doesn’t require an administrator account or
privilege escalation on Windows Vista, and it isn’t followed or preceded by any additional
security warnings Compare this to the prompts you get when installing a standard NET
application through ClickOnce, and you’ll see that the experience is much friendlier with a
Silverlight out-of-browser application As a result, the user is much more likely to go through
with the operation and install the application, rather than be scared off by obscurely worded
warnings
The installation process places the application in a randomly generated folder in the
current user’s profile When the installation process finishes, it launches the newly installed
application in a stand-alone window But the existing browser window remains open, which
means there are now two instances of the application running You can deal with this situation
by handling the InstallStateChanged event, as described in the “Tracking Application State”
section
■ Note Out-of-browser applications are installed for the current account only Much as one user’s web
bookmarks aren’t available to others, one user’s locally installed Silverlight applications (and the related desktop
or Start menu shortcuts) aren’t shown to other users of the same computer
Customizing Icons
The default image in the installation window is far better than an intimidating picture like a
warning icon But you can get an even better result by substituting a custom picture in its place
Silverlight lets you supply small image files to customize several details:
• Use a 16×16 image for the application icon in the title bar and in the Start menu
Trang 33• Use a 32×32 image for the application icon on the desktop (and in other large icon views)
• Use a 48×48 image for the application icon in tile mode
• Use a 128×128 image for the installation window
To customize these details, begin by adding the images to your project Each image file must be a PNG file with the correct dimensions For better organization, put the images in a project subfolder, like AppIcons Then, select each one in the Solution Explorer, and set the Build Action to Content (not Resource) so the images are packaged in the XAP as separate files Finally, return to the Out-of-Browser Settings window shown in Figure 6-6 to identify the icons you want to use
Figure 6-9 shows the installation window with a customized image
Figure 6-9 Customizing the EightBall installation
■ Note The image is the only detail you can change in the installation window You can’t alter the options it
provides or the descriptive text it uses
Tracking Application State
As you’ve seen, out-of-browser applications are ordinary Silverlight applications that have a bit
of additional information in the application manifest This gives users the option of installing
them locally, but it doesn’t prevent them from running in the browser This flexibility can be helpful, but it many situations you’ll want to pay attention to the application’s execution state–in other words, whether it’s running in the browser or in a stand-alone window You may want to provide less functionality in the browser or even prevent the user from running the application
The tool that allows you to implement this design is the Application
IsRunningOutOfBrowser property It returns true if the application was launched as a alone application or false if it’s running in the browser To differentiate your application in these two states, you need to check this property and adjust the user interface accordingly
stand-For example, if you want to create an application that supports offline use only, you can use code like this in the Startup event handler:
Trang 34private void Application_Startup( object sender, StartupEventArgs e)
{
if (Application.Current.IsRunningOutOfBrowser)
{
// Show the full user interface
this RootVisual = new MainPage();
}
else
{
// Show a window with an installation message and an Install button
this RootVisual = new InstallPage();
}
}
■ Tip It’s also important to check the IsRunningOutOfBrowser property before attempting to use any
features that aren’t supported in out-of-browser applications—namely, the browser interaction features described
in Chapter 14
There’s one possible quirk with this code It tests the IsRunningOutOfBrowser
property, and uses that to decide whether to launch the install process This makes sense,
because if the application is currently running in the browser (meaning
IsRunningOutOfBrowser is true), the application is obviously installed However, the reverse
isn’t necessarily correct If the application isn’t running in the browser (meaning
IsRunningOutOfBrowser is false), it may still be installed The only way to know for sure is to
check the Application.InstallState property, which takes one of the values from the InstallState
enumeration (as described in Table 6-2)
Table 6-2 Values of the InstallState Enumeration
InstallFailed The out-of-browser install process failed
Installed The application is installed as an out-of-browser application This doesn’t
necessarily mean it’s currently running outside of the browser–to determine this fact, you need to check the IsRunningOutOfBrowser property
The InstallPage takes this detail into account When it loads, it uses the following code
Trang 35been installed, it enables an Install button Otherwise, it disables the Install button and shows a message asking the user to run the application from the previously installed shortcut
lblMessage.Text = "This application is already installed " +
"You cannot use the browser to run it " +
"Instead, use the shortcut on your computer.";
Once the application has been successfully installed, it makes sense to inform the user However, you can’t assume that the application has been successfully installed just because the Install() method returns true This simply indicates that the user has clicked the OK button in the installation window to start the install The actual install process happens asynchronously
As the install progresses, Silverlight adjusts the Applicaton.InstallState property and triggers the Application.InstallStateChanged event to notify you When InstallStateChanged fires and InstallState is Installed, your application has just finished being installed as an out-of-browser application At this point, you should notify the user The following example does exactly that using some extra code in the App class
First, you need to attach the event handler in the App class constructor:
this InstallStateChanged += this Application_InstallStateChanged;
Then, you can add the code that reacts as the application is being installed, and notifies the InstallPage
private void Application_InstallStateChanged( object sender, EventArgs e)
{
InstallPage page = this RootVisual as InstallPage;
if (page != null )
{
// Tell the root visual to show a message by calling a method
// in InstallPage that updates the display