1
2
MEAP Edition Manning Early Access Program
Copyright 2010 Manning Publications
For more information on this and ...
18 downloads
1015 Views
9MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
1
2
MEAP Edition Manning Early Access Program
Copyright 2010 Manning Publications
For more information on this and other Manning titles go to www.manning.com
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
3
Table of Contents
•
1 Hello, Silverlight
•
2 Silverlight Controls
•
3 Data Services in Silverlight 4
•
4 Learning Xaml Through Blend
•
5 Creating a Fish Eye Menu
•
6 WCF RIA Services to the Rescue
•
7 Out of Browser Mode
•
8 Game Development in Silverlight
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
4
1 Hello, Silverlight Silverlight is a case of the right technology at the right time. This is a time of convergence. Companies want to be able to leverage the experience of their developers when developing Rich Internet Applications (RIAs). Developers want to write applications using the development tools and languages that they are comfortable with. Desktop application developers who previously may have felt that web development was out of reach can feel at home developing Silverlight applications. For Windows client developers writing a web application has been kind of like writing in a dialect that is not completely familiar to them. The stateless model of web applications and the session management model is something that Microsoft tried to hide away when they created ASP.NET, but it’s still not quite like writing a desktop application. If you want to do anything interesting with the user interface it usually requires you to write Javascript on the client side and many more hours of development than it would take to do the same thing in a desktop application.
I’M USED TO WRITING WEB APPLICATIONS, I WOULDN’T KNOW HOW TO WRITE A DESKTOP APP.
What you learn in this book should also give you a great jump start on writing Windows applications in Windows Presentation Foundation (WPF) since Silverlight is based on WPF. What’s in Silverlight is a subset of what’s available in WPF but what’s there usually works the same. In WPF and Silverlight, display elements are defined in an XML based language called XAML. Powerful data binding techniques allow data to be displayed with a minimum of display-specific code. Silverlight combines some of the best features of desktop application development with the ease of deployment and cross platform reach of web based applications. Silverlight contains the Silverlight Common Language Runtime (CLR) which is a smaller version of the .NET Framework. The Silverlight CLR runs on the PC and Mac, is only 5 megabytes to download, and installs in seconds. This means that to create Silverlight applications developers can use any .NET based language, including C# and ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
5 VB.NET. Let’s take a look at the current state of application development and why Silverlight is an appealing option.
1.1 A very brief history lesson
Many companies have been moving away from Windows applications in favor of web applications because of some inherent problems with Windows applications. Deployment and updating of the application is difficult (although this is a lot easier using ClickOnce). For most modern Windows applications, the prerequisites that need to be installed on the machine can be prohibitive. If you write a mainstream application using the .NET Framework, and it’s not already installed on the target machine, it can mean a 20+ megabyte download and a half hour to install. This isn’t the ideal first impression for your users. Then let’s say you want to provide some of the same functionality in a browser. You’ll end up having to write a web version anyway, and it will most likely be a different code base because of the differences in the two platforms. Traditional web development isn’t without its own issues. For a bit of a historical perspective, let’s take a moment to consider Rembrandt van Rijn, the most important artist in Dutch history.
WHAT DOES REMBRANDT HAVE TO DO WITH WEB DEVELOPMENT?
If Rembrandt were alive today he might find himself at home in traditional web development. Rembrandt painted to pay the bills, but his passion was for etchings. Rembrandt would create his etchings starting with a metal plate covered with wax and would methodically remove the wax to create the image that he desired. He was able to take this medium that was previously mainly used for printing and did things with it that were never conceived of before. He elevated the medium to a new level. Never before had someone created etchings with the level of detail and precision that Rembrandt had. One of Rembrandt’s etchings is shown here: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
6
Figure 1.1 The amazing detail of Rembrandt’s etchings. Adverse Fortune, 1633 What Rembrandt did for etchings the top web developers of today have done for HTML. HTML was originally developed as a language to share research documents. HTML was not originally meant to be used to create the web applications of today, and if someone from back in the time of the early Web was to see a modern web application they wouldn’t believe that it was created using a descendant of the original HTML. There have been some amazing sites developed using HTML, CSS, and JavaScript. The developers of these sites are like the Rembrandts of the web and deserve our respect. By using technologies that were designed from the ground up to provide interactive experiences, including multimedia, animations, and vector graphics, you don’t necessarily need a Rembrandt, but if you have one they can take a RIA and push it to its limits and beyond. Now let’s take a look at some of the features of RIAs and how they help to provide a better user experience.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
7
1.2 Anatomy of a RIA
The term “RIA” was originally coined by Macromedia in 2002 in reference to applications built using their Flash technology. Applications that are categorized as RIAs typically run inside the browser while delivering an experience similar to traditional desktop application. The code to handle the user interface runs on the client and data is retrieved as needed from the web server. RIAs normally do not have full access to local resources but can save data in sandboxed file storage on the client.
WHY NOT JUST USE CSS AND HTML?
RIAs were born out of the desire to increase responsiveness of web applications and to provide a user interface that is either difficult to impossible to provide using traditional web technologies. With HTML5 the line has blurred a bit and there are things you can do now that used to only be possible in a plug-in. As HTML evolves, so does Silverlight, and it’s a safe bet that there will be features available in Silverlight that you can’t do in HTML for the foreseeable future. Additionally if you are comfortable with .NET languages there’s a very good chance you prefer to use those instead of Javascript. Let’s take a look at a few more features common to RIAs, the first of these is vector graphics.
Vector graphics Computer graphics typically fall into two major categories. The most common format is raster graphics, also known as bitmap graphics. This group includes photos, icons, and other files where each pixel is represented in the stored file. The other major graphics category is vector graphics. In vector graphics, the data is stored as a collection of lines and curves with supporting data such as color, thickness, what color to fill shapes with. What vector graphics are very good at is representing shapes and text. Anything that can be described as a series of lines and curves to outline a shape can easily be stored as vectors. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
8
One of the major benefits of vector graphics is that they can scale to any size without sacrificing quality. If you were to scale a raster graphic by the same amount, the level of detail would not increase, and the edges would look jagged.
Figure 1.2 The impact of scaling on vector and raster graphics. The first image is a vector graphic scaled by 500 percent, while the second is the same graphic converted bitmap and then scaled by the same amount.
I LOVE THE JAGGIES, VERY RETRO.
This ability to easily resize vector graphics is why most fonts are stored in this format. Take any font in a word processing program and blow it up to 100 point text and it will still have smooth edges. Along with scaling, vector graphics can also be manipulated in other ways such as stretching, skewing and rotating without any loss of detail. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
9
Breaking away from the boxes The basis of HTML and Cascading Style Sheets (CSS) is what’s called the box model. Web designers have used various methods to make their pages look less boxy, often using some cleverly constructed bitmap images. Let’s say, however, that we want a video to play stretched and skewed to give it a three dimensional effect and we want to round the corners to make it look like it’s playing on an old television. This is something that is difficult if not impossible to achieve using traditional web technologies.
I WROTE A WEB SITE WITH ROUNDED CORNERS ONCE, IT WASN’T EASY.
Things like rounded corners are trivial in Silverlight. Also as designers attempt to make better use of page real estate they are turning to techniques where they can overlay information and controls on top of each other. A good example of this is a text box where before any text is entered, a hint is displayed over the text box explaining the information that is expected. Once the first character is entered this hint disappears. This is a much better use of space than having a separate block of text to the side which would display this same information. Another popular technique in desktop applications is to display objects as semi-transparent and when the mouse is positioned over these objects they can fade into view. This is something is trivial in an RIA application. These types of effects are done using animations.
LIKE CARTOONS?
Animations No, not really like cartoons. An animation in Silverlight is simply the changing of the value of a property or properties over a period of time. The various RIA frameworks name these animations different things, Flash, for example, calls them timelines, and Silverlight calls them storyboards. Some basic animations have been done for years in traditional web pages, typically using JavaScript and timers to modify the value of a document property over a given time. With the advent of sophisticated AJAX libraries some of this animation has become easier, but there are still browser differences to deal with and animations still typically have to be written in code. With Silverlight and other RIA technologies these animations can be specified in the design of the layout of the page and simply kicked off when needed. These animations can be triggered on a mouse entering or leaving a control, a keyboard input or mouse click, or other external inputs.
Client side storage Web applications can’t have free access to the local file system otherwise malicious sites could access sensitive data. Much of the effort in web security over the past few years has been to protect the casual ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
10 user from unseen threats. Normally the only way a web application can store information on the user’s computer is through the use of cookies. These cookies can store small amounts of information, typically less than 4 kilobytes. Let’s say you had a web application where a user could enter a blog post. This post could be hundreds of words or more and is entered via a web form. Now what if one of your users is almost done with the post and they lose their internet connection, or the browser crashes, or they accidentally close the page. All of that information is lost since the data is not sent to the server until they hit the submit button.
I REALLY HATE IT WHEN THAT HAPPENS.
To address this problem, RIAs typically have access to the local file system to store data while protecting the user’s sensitive data through a technique known as sandboxing or protected storage. An application can access data in a folder which only that application knows about. The application cannot access any data in the rest of the file system. Silverlight’s implementation of this is called Isolated Storage. Now that we have seen some of the important features that differentiate RIAs from other web technologies let’s take a look at the tools you can use to build a Silverlight application. That is, of course, if you would like to use something more sophisticated than Notepad.
1.3 The tools of the trade
Silverlight and WPF also share common tools for creating applications. They say that a craftsman is only as good as his tools. Fortunately, when building Silverlight applications, the tools are the some of the best available.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
11
Microsoft Visual Studio 2010 Visual Studio is the main development platform for Microsoft technologies. If you’ve never used Visual Studio before be prepared to fall in love with it. With Visual Studio 2010 Microsoft has rewritten the user interface in WPF and has taken the product to the next level. If you don’t have access to the full versions of Visual Studio 2010 you can develop Silverlight applications using the free Visual Web Developer 2010 Express Edition. The Express edition is a full development environment which is more than adequate for most development projects. For convenience, throughout this book when we mention Visual Studio we also mean Visual Web Developer Express since everything we will talk about can be done in either tool. To develop Silverlight applications you will also need to download and install the Microsoft Silverlight Tools for Visual Studio 2010 which includes the project templates and supporting files for creating
Silverlight
applications
with
Visual
Studio.
You
can
get
everything
you
need
at
silverlight.net/getstarted.
Microsoft Expression Blend Microsoft Expression Blend is a revolutionary design tool. It was built from the ground up using WPF technology to create an environment that designers are familiar with. It behaves the way designers expect it to. What makes it unique is that it understands the Visual Studio project format, and allows designers to work on the same exact files and projects as the developers.
Figure 1.3 Microsoft Expression Blend is targeted at how graphics designers work.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
12 In the past, designers would create the application’s look and feel in a tool like Photoshop or Illustrator and then hand it over the wall to the developers, who would do their best to make the application look like what the designer had envisioned. In a best case scenario, the developer would slice up the graphics and come up with a reasonable approximation of what the designer had in mind. Worst case, the developers would create a site and then get the designers involved at the last minute to “pretty it up”.
WE CALL THAT PUTTING LIPSTICK ON A PIG.
Yes, we have our own terms for this that can’t be discussed here. With the tight integration between Visual Studio and Blend, with designers and developers working off of the same project, teams can develop applications in any way that works best for them. The can do a design first approach, or prototype first, or design and develop concurrently. And even if most of the application is done before the designer gets involved, all is not lost, since as long as the designer follows the same naming conventions and some basic techniques, the whole front end could be swapped out easily. Expression Blend provides a visual design surface allowing designers to combine vector graphic primitives, gradient fills, and other media to create user interfaces. It also provides robust support for creating animation timelines. Anything that can be done with Blend can be done with other tools, or even in a text editor. As we’ll see in Chapter 4, Blend can be a great learning tool. By studying the output that it produces, you can learn new tricks and techniques for your own applications.
CAN WE GET GOING AND WRITE A SILVERLIGHT APPLICATION ALREADY?
Sure, no problem, let’s get started by creating a new Silverlight application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
13
1.4 The world’s simplest Silverlight application
Open Visual Studio and select File->New->Project, and in the C# section, select Silverlight. You should see a project type of “Silverlight Application”.
Figure 1.4 The Silverlight section of the Visual Studio 2010 New Project dialog Select this and specify a project name of HelloSilverlight and click OK. This kicks off the New Silverlight Application wizard. Now we’re given some choices. We can either create a separate web project to host the Silverlight application, or we can generate an HTML test page in the Silverlight
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
14 project to host our Silverlight control. We recommend always creating a new web site to host the application since you can run into problems accessing web resources when testing.
Figure 1.5 The New Silverlight Application wizard. You can choose the Silverlight version to target. For all of our samples in this book we will be building Silverlight 4 applications. Now if you click OK, Visual Studio will create the Silverlight application for you. Let’s take a look at some of the files that are generated as part of a Silverlight project.
Table 1.1 The files created by the C# New Silverlight Application Wizard File
Description
MainPage.xaml
Contains the forms layout for the Page control. The Page control is the root of the Silverlight application.
MainPage.xaml.cs
Code behind file for the Page control.
App.xaml
Global resources go here.
App.xaml.cs
Contains code that runs when Silverlight is loaded, including loading the MainPage control.
There are also a couple of other files containing assembly information and other plumbing but we don’t need to worry ourselves with those. Go ahead and run the project and see what happens. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
15
IT LOOKS LIKE AN EMPTY BROWSER WINDOW, IMPRESSIVE.
Actually it isn’t, it’s just that the Silverlight control has a white background and so does the web page. You can confirm this by right clicking in the browser window and you’ll get the Silverlight context menu. Let’s take a closer look at the MainPage.xaml file and see what we can do to make it visible. The MainPage.XAML file is the root visual of your Silverlight application. It is roughly equivalent to a default.aspx page in ASP.NET or a Form1.cs in Windows forms programming. Any graphical element that you add to the control will be a child or other descendant of this root. To view the markup for MainPage.xaml, go to the Solution Explorer tab, and double click on the Page.XAML file.
HEY THAT LOOKS LIKE XML!
That’s because it is XML. Silverlight uses an XML-based language called XAML (pronounced Zammel) to lay out its controls. Since all XAML is valid XML it’s easier to create tools that can produce and consume XAML. Those xmlns attributes are namespace entries and are important because they contain information about the XAML format. We will use other namespace entries later to access controls that are not in the core Silverlight runtime.
XML Basics If you’ve never worked with XML before, there are plenty of references online since it seems XML is used almost everywhere these days. XML consists of elements and attributes. Elements have a start tag and an end tag and can optionally contain one or more child elements. Here is a UserControl element with a child Grid element.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
16 If an element has no child elements, you can optionally write it as just a start tag, like this:
XML attributes are made up of a name and a value and are specified in the start tag of an element.
This Grid element has an x:Name and a Background attribute. Attributes can be specified in any order, but you can’t have two attributes with the same name on the same element.
You won’t see any mixing of code and XAML in the same file like you will in some other RIA frameworks. A partial class is generated from the XAML definitions so that any code behind can access layout elements. Keeping code and layout separate helps us to realize the benefits stated earlier about having designers and developers work off of the same project.
GOOD, I DON’T WANT DESIGNERS TO HAVE TO TOUCH MY CODE. As long as the designer and the developer agree on some basic naming conventions the designer could completely rewrite the user interface, even using different graphical element types, as long as the names are consistent. Now back to our first Silverlight application. In order to see the Silverlight control, let’s change the Background attribute or property to Blue:
ATTRIBUTE? PROPERTY? WHICH ONE IS IT?
Good question. It’s actually both. Background is a property on the Grid object, but in the XAML above it is represented as an XML attribute. Most properties that can be represented as a string or a number can be specified as an attribute. Complex properties are represented as child XML elements, and we’ll see some of those later in the book. You’ll also see the terms element and object often used interchangeably. In Visual Studio you can either edit the XAML by hand or you can select the Grid element and use the color picker on the Background property in the Properties pane to set the color.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
17
Figure 1.6 The color picker in the Properties pane lets you define a custom color or you can pick from a list of predefined colors. Go ahead and run the application again. You should now see the entire page is filled with blue. This is because the test page that is created for you defaults to 100% for the width and height of the Silverlight plug-in. Now let’s add some text. You can add text to a Silverlight application with the
TextBlock element. The TextBlock element’s attributes are modeled after the style attributes in CSS and HTML. In general, Silverlight controls follow this convention, reducing the amount of new information that needs to be absorbed when getting used to Silverlight. Select the TextBlock control in the Toolbox and drag it to the design surface. Let’s set the Text property to “Hello, Silverlight!” and give it a Foreground of White and a font size of 30. Your TextBlock definition will look something like this: With these changes if you run the application it will look something like this:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
18
Figure 1.7 The obligatory Hello World application. So that’s all there is to it, you’ve written your first Silverlight application! But what exactly did this create? In the next section, you’ll learn the basics of this file format.
1.5 Deploying your application
To make it easier to deploy and download your Silverlight application, all of the code, layout, and assets are packaged into a single file. Silverlight knows how to read this file and access the parts it needs. This is commonly referred to as a XAP file (pronounced Zap), which refers to the file extension used for these files. Looking at our Silverlight application project, where is the XAP file? First let’s select the Show All Files option for the project. This menu option is shown in figure 1.8.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
19
Figure 1.8 Show All Files option in Visual Studio Now you should see a Bin folder in the Solution Explorer window. In that folder, you’ll see a Debug folder which contains a file called HelloSilverlight.xap. This is the file that gets downloaded and run when someone visits a page with your Silverlight control on it. What exactly is in this file? A XAP file is actually a ZIP file with a well defined internal file structure.
LIKE THE NEW OFFICE FILE FORMATS?
Yes, exactly. Office 2007 introduced some new document file types with file extensions end with an x, like .docx, .xlsx, and .pptx. These are ZIP files as well. It’s a nice format, since ZIP files have been around forever and are easy to create and decode. It also provides compression along with the packaging. XAML is pretty verbose but since it contains many of the same tags repeated throughout the document it compresses exceptionally well. You can actually take a look at the contents of this file. In Windows Explorer, navigate to the location of the HelloSilverlight.xap file and rename it to HelloSiliverlight.zip. You can then view the contents in Windows Explorer or your favorite ZIP extractor program. Figure 1.9 shows the result of viewing the contents in Windows Explorer.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
20
Figure 1.9 The contents of the HelloSilverlight.xap file. If you include other content such as images or assembly references you would see them here too. The AppManifest.XAML contains information about which file contains the root application class and can also contain version and other related information. The HelloSilverlight.dll contains the application code and also contains MainPage.XAML as an embedded resource.
HOW CAN WE LET OTHER PEOPLE SEE THIS AWESOME APPLICATION? You can serve up Silverlight applications from pretty much any web server including web servers on Linux such as Apache. There are some Silverlight features like WCF RIA Services that we’ll see in Chapter 6 which require the Microsoft Internet Information Services (IIS) web server that you can take advantage of but you’re not required to. Since Silverlight has strong support for calling web services and making HTTP requests you can retrieve the data for your application from anywhere as well. One thing you will need is a MIME type definition on your server. MIME types tell your web server how to treat different file extensions and this information is passed down to the browser so that it knows how to display the file. The MIME type you need defined is for the XAP file extension. IIS will typically have this defined by default but you’ll probably need to define it if you’re running Apache or another web server.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
21
Figure 1.10 Adding the XAP MIME type to IIS. The web project that is generated when the Silverlight application project is created contains two test pages. One is an ASP.NET version with a .aspx extension and the other is a plain old HTML page. To embed the Silverlight application in your own page, simply copy the object element from either of these files and paste it where you want it. You’ll want to change the source parameter to the location of your XAP file. The hyperlink inside this object element will display a button to install Silverlight if the user doesn’t have Silverlight installed.
Figure 1.11 The default install experience for Silverlight. You can customize this however you want. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
22 You may want to change this HTML to provide an install experience that’s integrated with your site’s theme or give the user a screenshot of what the application will look like when it’s running. It’s all about convincing the user that installing Silverlight will be worth the effort.
1.6 Summary
RIAs such as Silverlight are convergent technologies and a stepping stone toward being able to write once and run anywhere. As RIAs evolve, the line between desktop applications and web application will continue to blur. Silverlight is a compelling technology for developers experienced with the .NET framework since they can use the languages and tools that they are used to. It is also attractive to developers that are currently using competing technologies since the skills they learn while becoming proficient in Silverlight will serve them well for developing other applications using Microsoft technologies. Aiding in the adoption of Silverlight is a high performance runtime which cannot be matched by the competing technologies and some of the best tools in the business to make Silverlight developers extremely productive. Developers and designers can work off of the same projects and code base, allowing for a new level of collaboration. Add to this the ability to program your application with your language of choice, and Silverlight stands apart from all other alternatives in its ability to create fast, robust, and compelling web applications quickly and easily. Deploying a Silverlight application is easy since the entire application is packaged into a XAP file which can be served up from any web server, and the sample pages created as part of the test web project contain code that can be pasted into any web page that you want to host Silverlight content in. Throughout the rest of the book, we’ll explore the key features of Silverlight and we’ll create some real world samples along the way. The next chapter covers the fundamentals of getting started with controls in Silverlight which can be leveraged to create useful applications quickly.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
23
2 Silverlight Controls Chapter 1 covered the basics of Silverlight, and this chapter starts digging deeper into programming by focusing on the Controls available to you, the developer. Not all end-users realize there is a difference between running a desktop application or a web-based application, so the capability of providing a seamless look and feel across a suite of tools is a very compelling design model.
2.1 Pre-Silverlight 3 controls
Silverlight provides web developers a rich set of controls. This in turn creates a better experience for the end-user. This chapter will discuss the available control set, and demonstrate using just a few of them. By the end of this chapter, the core control set will become familiar and that knowledge will make the other controls easier to learn.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
24
2.2 Silverlight 4 control basics If you open a Visual Studio 2010 project and select the View->Toolbox menu, you will see the Silverlight Controls available, also shown in figure 2.1.
Figure 2.1 The default Silverlight 4 control toolbox provides many controls, as compared with none in Silverlight 1, and only a small handful in Silverlight 2. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
25
WOW ARE WE GOING TO COVER ALL THESE CONTROLS? Silverlight 4 clearly contains many controls for use in Silverlight applications, and with the latest release of Visual Studio 2010, the design surface for Silverlight is fully functional.
Because of the large number of controls available and the complexity of many, only a handful of the controls are going to be discussed in this chapter, but the techniques and approaches to modification are similar in all the controls. The controls to be studied in this chapter are the most basic and most commonly used controls. In the order of their coverage, the controls are: Buttons, Checkboxes, RadioButtons, Grid, TabControl, Border,
StackPanel, ToggleButton, Slider, TextBlock,
TextBox, and
RichTextArea.
The most familiar control is also one of the more basic controls, the Button. Buttons will be discussed first.
Buttons A Button is a clickable piece of artwork that provides the end-user a method to initiate some sort of action. When a control is dragged into the XAML, its local properties are not set, so the ‘default’ control is displayed. The Button control, for instance, will be 75 pixels wide by 23 pixels high and contain the text “Button”.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
26 OK, SO HOW DO I MAKE GREEN TEXT?
YOU WANT GREEN TEXT, YOU HAVE TO SET A PROPERTY!
Setting Button properties
When a control is dragged onto the design surface, arrows are displayed as shown in Figure 2.2 to show the relative spacing of the control to surrounding elements.
Figure 2.2 Control spacing indicators in the design surface assist in correct positioning. Clicking the upper arrow will cause it to become an arrow below the control. Clicking the left arrow will cause it to become an arrow to the right of the control. The distance denoted by the arrow indicators is the Margin property of the control. The Margins and all other properties of the control are set in the Properties Window of Visual Studio 2010. To view the properties, select the View menu, then Properties Window. The desired property values for the control are set directly in the Properties Window, and as the properties are set, the control will reflect those changes.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
27 For this first example, the width is set to 100, the height to 30, and the button Content to ‘Hello’ as shown in Figure 2.3.
Figure 2.3 Basic button control with the Content property set to ‘Hello’.
The properties available inside a control change from control to control. There are some common properties, but the Properties Window will always show what is available for the selected control. Possible examples of properties that can easily change the visual representation of controls would be the foreground color or font. With Silverlight, the designer can truly match up controls with the overall layout of the page without having to be annoyed by ‘default’ or ‘standard’ controls not matching the surrounding artwork. This gives the designer or designer/developer the capability of matching up the corporate look-and-feel with the control set, making the web experience not only pleasant, but a cohesive whole with any other corporate materials. This lends an air of credibility to the site and the organization. A Silverlight button will raise a Click event when reacting to the user clicking the mouse. A normal Windows desktop application button would raise an event when the mouse is released, but in Silverlight, that action can be customized using the ClickMode property. There are three choices for ClickMode: Press, Release, and Hover. Release, as noted above, is the Windows standard click mode for a button, and Press is the standard click mode for a link. If a button is used in place of a link, the Press ClickMode might make sense.
HOW DOES CLICKING THE BUTTON CAUSE AN ACTION?
Wiring up control events A button “raising an event” was mentioned in the previous section. Writing the code to handle the event, and connecting the code to the actual event itself is generally called ‘wiring up’ the event. Wiring up button click handlers is very easily accomplished in Silverlight 4. The design surface in Visual Studio 2010 will give each Button a new name such as Button1 then Button2. Assigning real names in the properties window is a good idea to make the code easier to read. Double-clicking the button in the design surface, or double-clicking the space for the click-handler name in the Properties Window events will assign a default name and navigate to the handler in the code-behind file. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
28 Navigate to Event Handler The following listing shows the default event handler added to the C# code-behind file for a button named myButton. private void myButton_Click(object sender, RoutedEventArgs e) { } To test the handler, add code as shown below to simply change the button text when clicked. private void MyButton_Click(object sender, RoutedEventArgs e) { myButton.Content = "Clicked"; } When the Button is clicked, the code behind event is raised and the Content Property is modified to give the Button the text “Clicked” as shown in Figure 2.4. In a real application, this would not be a good thing to do without having a reset somewhere to restore the button to the original state, because once clicked, the button would retain the Content “Clicked” until the page was reset.
If the code behind is accessed prior to the XAML file being saved, the code behind will not have Intellisense for newly created controls. This is easily repaired by simply saving the XAML file.
Figure 2.4 Basic button control after code-behind modification performed by the click handler The next controls to be investigated are CheckBoxes and RadioButtons. These are standard controls used both in desktop and web applications, and are also included in the suite of Silverlight 4 controls.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
29
CheckBoxes and RadioButtons Dragging a CheckBox or RadioButton to the design surface is identical to the previously discussed experience with Buttons. Silverlight CheckBoxes and RadioButtons provide a UI experience for the user that is the same as any experienced user would expect in a Windows or non-Silverlight Web application.
CheckBoxes As with a Windows or Web application, CheckBoxes may be logically grouped, but the grouping has less meaning for CheckBoxes than for RadioButtons. CheckBoxes typically work independently of each other and offer the user choices that are not mutually exclusive such as “Show Status Bar”, “Show System Menu”, and “Autohide”. Any combination of choices of the three Checkboxes could be valid. Figure 2.5 demonstrates a possible CheckBox layout.
Figure 2.5 CheckBox control layout demonstrating the out-of-the box Silverlight 4 Styled CheckBoxes in a vertical layout. RadioButtons provide a different experience in that only one of any particular set of RadioButtons may be checked at any given time.
RadioButtons All RadioButtons on a Silverlightpage work as a group unless otherwise noted in the properties. In other words, if there is only one set of radio buttons, then there is no extra work to be done to make them work together in a group, i.e. when one is selected, the others are unselected. RadioButtons are grouped by using the GroupName in the Properties Window. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
30 Give all the RadioButtons in the desired group the same GroupName and they will operate as a group independently of all other RadioButtons on the page. Figure 2.6 shows two RadioButtons vertically stacked similarly to the CheckBoxes in Figure 2.5.
Figure 2.6 RadioButton control layout demonstrating the Silverlight 4 out-of-the box styling experience for RadioButtons stacked vertically. This RadioButton combination is familiar to all developers as one of the choices in setting up the Indent Formatting for the IDE editor in Visual Studio. Only one of the two choices Tabs or Spaces may be selected, so RadioButtons is the logical control choice. The CheckBox and RadioButton control examples given are those usually found on setup or customization pages in an application. Silverlight 4 provides a TabControl to make the logical grouping of controls a better user experience.
TabControl The next control to be introduced is the TabControl . This control provides a familiar interface for the end-user.
The Silverlight 4 TabControl is very simple to use. Figure 2.7 demonstrates the very beginning of a tabbed application built for this chapter. Tabs allow multiple pages of data to be placed on a single page ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
31 using a book-like tab effect that allows the end-user to select alternate ‘pages’ by simple selecting a different tab.
Figure 2.7 TabControl showing 1 TabItem, as initially built by Visual Studio 2010 The layout of the TabControl is straightforward. When the TabControl is dragged from the Toolbox to the design surface, a default TabItem is added as shown in Figure 2.7. The placement of the tabs on the control are set by using the TabStripPlacement property. Choices are Left, Top, Right, and Bottom. Clicking the tab itself provides access to its properties as well, and the text is changed using the Header property. Adding new tabs is as simple as either modifying the TabControl Items collection as shown in Figure 2.8, or right-clicking an existing tab and choosing Add Tab from the context menu.
Figure 2.8 TabControl Items Collection for the default tab, tabitem1 ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
32
Before going any farther with the layout of controls on a page, the layout controls must be discussed. The layout controls provide finer placement and help the developer to provide a better user experience.
2.3 Getting organized with panels
In all except the simplest applications, it’s important to be able to arrange your visual elements in a way that makes sense for the information that is being displayed. You also typically need to be able to resize gracefully based on the available space. In Silverlight, this is done through the concept of layout panels.
LAYOUT PANELS ROCK!
Layout panels are simply controls that have a list of children, but each type of layout panel manages these children differently. They are elegant in their simplicity, and very powerful. Each of these panels can be coaxed into doing most tasks, but by picking the right one you can save yourself a lot of time and effort. The layout panels discussed in this section are the Grid, StackPanel, and Canvas.
Getting to know the Grid All Silverlight pages must have some sort of layout control onto which objects and controls are placed. The default layout control in Silverlight 4 is the Grid. A new page in a Silverlight application is really a UserControl. This provides the facility of taking that page or control grouping and using it in another circumstance. The UserControl needs a layout control to allow the grouping of Silverlight objects, and the default control is the Grid. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
33 The Grid control is not obvious in the design surface, and controls may be laid out scattered across it. To have better control over the layout, an understanding of the Grid and how to use it is beneficial.The Grid panel defaults to one row and one column, and that’s how we’ve been using it so far, but this can be modified to any number of rows and columns based on your needs. This is done through the RowDefinitions and ColumnDefinitions properties. At first glance, the blank Grid simply shows a rectangle which denotes the border of the control. Left-clicking inside the grid will highlight the left and top borders of the Grid as demonstrated in Figure 2.9.
Figure 2.9 Grid control with borders highlighted by clicking inside the body of the control.
Grid rows and columns may now be defined simply by left-clicking the border. As rows and columns are defined, they may be set to be fixed, proportional, or set by content. Those choices may be made with icons that appear when the row or column is hovered over by the mouse as shown in Figure 2.10.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
34
Figure 2.10 Grid control with rows and columns defined, and column size icons showing above the rightmost column Alternatively, clicking the icon in the upper left-hand corner of the Grid will display the Grid’s properties in the Properties W indow. The row and column definitions are contained in the RowDefinition and ColumnDefinitions collections as shown in Figure 2.11.
Figure 2.11 Grid RowDefinition collection from the properties window
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
35 New rows and columns may be added and deleted through the Collection dialog if the user chooses to do so.
So how do we use these properties in MainPage.XAML?
Let’s say we want to have two columns, one for a list of elements, and the other for the details associated with those elements. We’ll just use borders for placeholders to illustrate how the Grid works. These borders would be replaced by or populated with other controls if it was a real application. Click inside the default page that Visual Studio 2010 displays for MainPage.xaml, and you will see the Grid borders as in Figure 2.9. Click in the blue border along the top to set a column in the grid. Once you have set a column, a left-and-right arrow appears allowing you to move the column indicator. For this example, move the indicator to 150 pixels, and select the ‘fixed’ icon indicator as shown in Figure 2.12.
Figure 2.12 Grid column set to 150 pixels by clicking and moving the column indicator. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
36
Any elements in the left column that do not specify their width will be resized to a width of 150. The second column is going to be set to have a width of “*” (pronounced Star) which tells it to take all of the remaining space. This is selected by hovering the mouse over the blue border on the top for the second column as shown in Figure 2.13.
Figure 2.13 Grid column set to * by clicking the * icon. If this page is now compiled and run, nothing would display because there are no controls in the grid.
What if we want to be able to see the columns?
To make the columns visible for this example, drag a Border control into the left column, then drag the corners out to size it to fill the column. Right-click inside the Border control and select Properties to open the Properties Window, then set BorderThickness to 5, the BorderBrush to Red, Height and Width to Auto, and HorizontalAlignment and VerticalAlignment to Stretch, producing the result in the design window as shown in Figure 2.14.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
37
Figure 2.14 Grid left-hand column filled with a Border control having a Red border width of 5, and fully sizeable width and height.
Fill the right-hand column with a Green Border control with the same settings, compile and run to view the output as shown in Figure 2.15.
Figure 2.15 Grid with two columns, both containing only a border control.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
38 Because of the properties set on the Border controls, the browser window is sized down to the size of the Silverlight Grid. Try resizing the browser window and seeing how the grid layout responds. Since the first column is a fixed width it stays the same size while the other column resizes automatically. This makes it easy to create resizable applications and you don’t even have to write any C# or VB code to make it happen.
CAN I SPREAD THINGS OUT A LITTLE BIT? LIKE MARGINS IN CSS? Yes there is a Margin property, and it is very similar to what exists in web pages. Margins provide a buffer area around elements and are an easy way to space things out a bit. If you want a margin of 5 pixels on all sides of an element you can specify a Margin of “5,5,5,5” or you can just use a shorthand of “5”. If only one value is specified, this value is applied to the left, top, right, and bottom margins. Figure 2.16 demonstrates the two borders each with a margin of 5.
Figure 2.16 Margins are an easy way to provide some spacing around your elements.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
39 Margins: Similar to HTML and CSS, with a subtle difference The Margin property in Silverlight can be a bit confusing at first to developers coming from a web development background because the ordering is different. In CSS, if you specify different margins for each side of the element, the order of specifying these margins is Top Right Bottom Left, or clockwise starting with the top. The margin values in Silverlight are specified as Left, Top, Right, Bottom or X1,Y1 for the top left corner, and then X2,Y2 for the bottom right corner.
Now what if we want a split view on the right?
Since layout panels can have other layout panels as children, one option would be to put another Grid inside that cell, and define two rows in it, but we can do it all with one Grid. First let’s add a couple of row definitions to the Grid control using the RowDefinitions Collection in the Properties Window. Set the Height of the upper-most Row to *, and the Height of the lower Row to 2*. Any row or column that has a width or height of “*” takes a percentage of the remaining space. By specifying a Height of “2*” which the other has a Height of “*”, the second row will be twice as tall as the first row. These values can be decimal values as well, so you can say “.8*” and “.2*” for 80 percent and 20 percent. Because we set the Row definitions after filling the original cells, our left Border is spanning the two rows, and the right Border is in the upper right cell. Now let’s add another border to go in the lower right.Populate the new Border properties as before, but make it Blue. The result is shown in Figure 2.17, and as the browser is resized, both right-hand Border controls resize both in width and height.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
40
Figure 2.17 A Grid with two rows and columns, and a Border spanning two rows There is one more type that can be specified for Width or Height, and that’s “auto”. Auto means set the width or height of the row or column to the width or height of the largest element in that cell.
WHAT’S THE DEAL WITH HAVING TO PREFIX CERTAIN PROPERTIES LIKE GRID.ROW WITH THE WORD ‘GRID’? These are called attached properties and they’re pretty sweet. Attached properties are a key concept in layout panels. One of the most common questions from developers new to XAML is “where are the Left and Top properties on elements”?
DA, WHERE ARE LEFT AND TOP?
The answer is that they don’t exist in XAML, but don’t get too upset. It makes sense once you think about it a bit. For the children of a Grid, Left and Top properties aren’t very useful since the controls are automatically placed based on rows and columns. The StackPanel positions elements in a list and elements are displayed in the order they appear in the list, so StackPanel doesn’t have much use for a
Row or Column property. Likewise a Canvas panel uses absolute positioning of elements, so it needs its children to have a Left and a Top property, but doesn’t care about rows and columns. Silverlight and WPF provide a pretty cool solution to this problem. Elements can inherit properties from their parent panel, and these properties are called attached properties. This provides great flexibility, since you could create a completely different type of panel, that needs its children to specify different properties, and these can be provided by the panel to its children. For instance, you could write your
own
SortedStackPanel,
and
it
could
have
an
attached
property
SortedStackPanel.DisplayOrder to control the order in which the children would be displayed.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
of
41 Attached properties or dependency properties? You’ll see attached properties also referred to as dependency properties. Which is correct? Well they both are, attached properties are a type of dependency property so all attached properties are also dependency properties but not the other way around.
Table 2.1 Panels and their attached properties Grid
StackPanel
Canvas
Your custom panel
Grid.Row
None
Canvas.Left
Whatever makes sense
Grid.Column
Canvas.Top
Grid.RowSpan
Canvas.ZIndex
Grid.ColumnSpan Now that you have some idea about how the Grid panel and attached properties work, let’s take a look at our next layout panel, the StackPanel.
Stacking the deck Stack panels are simple but powerful. They let you easily lay elements out one after the other either vertically or horizontally. Note that panels can be nested inside each other, sometimes many levels deep to get the look you want. Let’s replace the first Border with a StackPanel and put a few borders in it as shown in Figure 2.18. This was done by selecting and deleting the left Border and replacing it with a StckPanel. Then a Border control was placed inside the StackPanel. Since the StackPanel defaults to Vertical Orientation, the new Border control went to the top of the StackPanel. The Border Width and Brush were set to 5 and black respectively, then the Border was right-clicked, copy selected, then rightclick pasted 3 times in the StackPanel. Each was given a Margin of 0,5 to set the separation, and Figure 2.18 is the result.
Now if you run the application, you should see a few borders stacked in the left column like in figure 2.18. Since no width was specified for the borders, they grow to the width of the stack panel. If a width was specified the elements would be centered by default.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
42
Figure 2.18 Stack as many elements as you like in this nested StackPanel.
LOOKS LIKE A GREAT START TO A MASTER-DETAIL VIEW. Absolutely, think about Outlook or something similar and how easy it is to create a layout like that in Silverlight. That leaves the last built-in layout panel, the Canvas.
Put it wherever you want with Canvas Sometimes you just need to be able to put something exactly where you want it. You can fool with
Margins in a Grid to do some positioning, but it is still dependent on the size of the cells and other factors. When this is what you need, you should use the Canvas panel.
LIKE IN GAMES?
Yes if you’re writing games, like the one we’ll write in Chapter 7, the Canvas will most likely be your layout panel of choice. The Canvas lets you specify exact positioning for your elements through the
Canvas.Left and Canvas.Top attached properties. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
43 Be careful where you use a Canvas It may be tempting to use a Canvas because you have full control over positioning and it may be more like what you are used to, but this full control had its drawbacks. It means you need to handle all of the positioning and sizing and you don’t get any of the automatic layout benefits of the Grid and StackPanel. It is recommended that you only use a Canvas where you don’t have any choice and using another type of panel won’t work for you.
Let’s replace the Border in the lower right with a Canvas, and give it some child Borders by copying and pasting as in the last example. The output should now look like figure 2.19.
Figure 2.19 A Combining all of the different types of layout panels gives you a lot of flexibility.
HOW CAN WE CONTROL WHAT SHOWS UP IN FRONT?
By default, the element that was specified last will show up on top when elements overlap. If you want a behavior other than this, the Canvas panel has a third attached property, the Canvas.ZIndex. This allows elements on the Canvas to be displayed in a different depth order. The default Z index is 0,
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
44 and the higher the value, the more to the front or “closer” to you the element is displayed. Let’s set the
Canvas.ZIndex of the first Border to 1 and see what happens: The boxes will now be arranged as in figure 2.20.
Figure 2.20 The top border is now in front because it has the highest ZIndex.
Canvas.ZIndex is a little weird Even though ZIndex is an attached property of Canvas, you can actually use it with any panel, such as the Grid, to control the order in which elements are drawn. It’s still specified as Canvas.ZIndex, but Grid will obey this property. This is useful if you have more than one element in the same Grid cell but you want to force one to show up in front of the other.
ARE WE GOING TO SEE SOMETHING I CAN USE?
Of course we are going to demonstrate something usable. The next section will pull together a more common real-world form.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
45
2.4 Panels, CheckBoxes, and sliders oh my! To provide a somewhat realistic TabControl and at the same time demonstrate additional Silverlight controls, the second tab will contain four more controls from the list inSection 2.2: the ToggleButton, Slider, TextBlock, and TextBox. A screenshot of the running application for Tab Two is shown in Figure 2.21.
Figure 2.21Tab Two of the example TabControl containing a complex grouping of other controls
The CheckBoxes and RadioButtons are familiar, but the ToggleButton, Slider, TextBlock, and TextBox are definitely new, and those are to be discussed next.
ToggleButton, Slider, TextBlock, and TextBox The second tab was created using the Items collection on the TabControl. A Grid control was then dragged to the Tab surface. Three rows and three columns were defined and the appropriate controls dragged onto the surface as shown in a design surface screenshot in Figure 2.22.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
46
Figure 2.22Tab Two of the example TabControl displayed from inside Visual Studio 2010’s design view.
Figure 2.22 clearly shows a button in the first cell in Row 0 of the Grid, three CheckBoxes in the middle cell, and three RadioButtons in the right-hand cell. The middle row has a Slider control that spans all three columns. The bottom row only has the center cell populated by a TextBlock and a TextBox. The Button in Figure 2.22 has every outward appearance of being a standard Button control, but it was placed there as a ToggleButton which is the next topic of discussion.
ToggleButtons A ToggleButton looks almost like a normal Button. The difference becomes apparent when the Button is pressed. A pressed ToggleButton holds the visual effect of being pressed giving the impression of clicking once to push it down, clicking it again to pop it back out. A ToggleButton can fire a click event just like a regular Button does, but also at any time, the state of the button may be queried using the IsChecked property of the Button as shown in the listing below. private void toggleButton_Click(object sender, RoutedEventArgs e) { if (toggleButton.IsChecked == true) { toggleButton.Content = "RadioButtons"; } ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
47 else { toggleButton.Content = "CheckBoxes"; } } A ToggleButton provides much of the same functionality of a CheckBox in that it can fire a click event and can be polled for a state. The listing above is actually the Clicked event handler for the ToggleButton of Figure 2.22. IsChecked is used to determine the state of the control, and the Content property of the Button changed appropriately. Unpressed the ToggleButton contains the text “CheckBoxes”, and pressed, it contains the text “RadioButtons”. The Slider position is a double type, ranging from 0 to 10. In the sample application, the Slider position is converted to a String and displayed in the TextBox in Grid Row 3 using the code in the listing shown below. This was purposefully done without using databinding since that will be covered in Chapter 3. In a real-world application, discrete values would be desirable, such as indexing the values as 0, .5, 1, 1.5, etc., rather than a stream of numbers with 14 digits to the right of the decimal point. Displaying the full value of the double does display the granularity to which the control could be set or displayed.
The CheckBoxes and RadioButtons were placed on the tab to demonstrate some of the customization that can easily be done. The color and font weight of the text for each was changed using the Foreground and FontWeight properties in the Properties window. Before leaving controls completely, there is one new in Silverlight 4 that arrived with much fan-fare, and that is the RichAreaControl, the subject of the next section.
2.6 RichTextArea Control Displaying rich text in Silverlight became decidedly simpler in Silverlight 4, with the arrival of the RichTextArea control. Rich text still takes a lot of work, but the visual effect is exactly what your users are expecting. Exactly how much effort is this going to take?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
48 All the controls covered so far have been reasonably easy to layout using the Properties Window. The RichTextArea control is going to break that pattern and force the developer into XAML. Begin by dragging a RichTextArea control onto the design surface and dragging the corners out to set the placement and size. The Properties Window allows some basic parameters to be set such as Horizontal and Vertical alignment and one important one if there is going to be a Hyperlink involved: IsReadOnly. To make a hyperlink functional at run-time, IsReadOnly must be set to “True”. A RichTextArea display is formatted into logical segments using the Paragraph property. A simple Paragraph in XAML is shown below. This is Paragraph 1 From the code above, it is obvious that the formatting properties may be changed for each individual Paragraph. Figure 2.23 demonstrates the output produced by the XAML paragraph.
Figure 2.23 Output from RichTextArea control Proper output formatting becomes less obvious as more and varied information are inserted. For example, the XAML shown below produces the screenshot in Figure 2.24 in the display surface of VS2010. This is a hyperlink: Manning.com
Figure 2.24 Screenshot of VS2010 display surface for RichTextArea Paragraph The Paragraph, Run, and Hyperlink elements appear to be running together. Figure 2.25 demonstrates that the end result looks fine. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
49
Figure 2.25 Screenshot of output produced by RichTextArea Paragraph Figure 2.25 shows the formatting is very nice, with no text overrunning any other text. The XAML for the hyperlink, as shown above, is very straightforward, detailing the URI for the link, and the text typed in a very readable form. Once again, the link is navigable only if the IsReadOnly parameter is set “True”. One last RichTextArea element to show is the InlineUIContainer as demonstrated in the following XAML. In the above example, an Image was chosen to be displayed from an Images folder of the web project. This syntax is not different from displaying an Image in any other location in a Silverlight project. The important concept in this situation is the fact that the image is displayed inside a RichTextArea control in-line with everything else. Figure 2.26 demonstrates the full RichTextArea control built up from the XAML shown in the above examples.
Figure 2.26 Screenshot of full output produced by RichTextArea control example
There are more controls and variations of controls in Silverlight 4 than could be covered in a book much less a chapter. The basics presented here should give the developer enough information to begin and venture out into the other controls.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
50
2.7 Summary Silverlight 4 provides many controls for the designer and developer to use while building applications or Silverlight segments to be contained within other applications. The controls are not only many and varied but also provide a seemingly endless stream of properties. The rich property set allows customization of the application to take place providing a personalized or corporately-branded look-and-feel to the application.
The next chapter is all about data: databinding, Web Services, and Isolated Storage. The applications used to demonstrate those concepts will use many of the elements learned in this chapter.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
51
3 Data Services in Silverlight 4 Chapter 2 provided a good foundation for Controls and layout on the Silverlight page. No matter how good an application looks or how smoothly it operates, the functionality and usefulness of the application always depends upon display of information to the user and acceptance of input from the user. This chapter is going to concentrate on both of those topics. The information passed back and forth from the user to the application is the data associated with the application. A thermostat provides a good data application:
[Dave note: this is from www.mercuryinschools.uwex.edu]
Figure 3.1 Temperature control Thermostat
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
52 In one very familiar physical device, the thermostat of Figure 3.1 demonstrates both an input and output control concept. The ambient room temperature is shown on the device as an analog or digital display. This temperature value changes as the room temperature changes. A thermostat also has an input control by which a desired temperature is set. This may be analog or digital as well. The thermostat input and output are bound to values used by the heating or cooling system. For instance, as the room temperature rises and falls, the temperature reading will display that change. That is what is meant by a control being bound. The control value exactly mirrors the data at all times. The thermostat temperature setting is also bound, it is bound to a value used by the heating or cooling system and used to determine if the system should be running. In this particular case, the binding is between a user-generated value and a data element internal to the heating and cooling system. Taking this input/output concept into a Silverlight 4 application, it becomes fairly simple to recognize where data is not only used but bound. A data-entry form such as a profile page takes user input and utilizes it in the program either by having it be directly stored in the database, or if not retained, the data is immediately used. Output displays of data continually provide web-page users functionality by displaying data based upon external requirements. Examples are: stock quotes, search results, bank balances, or temperature readings for a specific location. The biggest problem developers have with data is the sheer volume of data that must be consumed and displayed by the applications. Silverlight’s databinding provides a way to make that job much simpler and that’s what we’ll be focusing on in this chapter.
3.1 What exactly is DataBinding While it may be easy to relate to the simple thermostat example in the introduction, the actual concept of Binding data might be a mystery in general. A simple TextBlock on a page can hold temperature data. Data may be written to that TextBlock and never changed. Typically this would be the scenario used for labels or page headers. It is also possible that run-time code could change the data in the TextBlock. At the completion of a task, a TextBlock could be refreshed with information informing the user of a completion state, for instance. A TextBlock that contains temperature information would not be very useful. Having data written only once during the application’s life cycle
would not provide a very good experience for the user
unless it was referencing historical data. The temperature value must be written actively to the
TextBlock to provide regular updates to be useful.
How do we automatically write data to the TextBlock?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
53 One way to accomplish this would be to have the code behind updating the TextBlock when the temperature changes. Ignore for the purposes of this example how the code behind would get that information! If the application described were truly a thermostat instead of simply a thermometer, there must be a way for the user to enter text and have the value picked up by the code behind for use in setting the desired temperature point. Once again, with sufficient work in the code behind this can be accomplished by polling for a change in the value of the TextBlock. The scenarios explained may actually be sufficient for applications where the data changes are few and infrequent. In an application where many data values need updating, for example a grid of temperatures throughout a facility. The code behind would get very cumbersome in such a situation, and would spend quite a bit of processing time just updating the user interface.
So can databinding make the code easier?
DataBinding resolves the 'cumbersome code' problem.
Very simply, DataBinding associates a piece of data with a control on a form. In most cases, when the value of the data changes, the control changes in some manner. If the control is a rotary dial with 6 positions, the dial will change position based on the data value. If the control is a grid of temperature values, the temperatures of all grid locations would automatically adjust themselves as the temperatures changed without any timers or polling in the code behind. As with the different ways of updating the data in the examples explained, there are different types of DataBinding for the various requirements.
Binding only once If a data element must be read from a database, or a device, and the value is not going to change for the duration of the application, "OneTime" binding can be used. OneTime binding would make sense for reading a user's name from a database based upon the login credentials, for example. A friendly greeting may be displayed where the user’s name is the element bound. No continued use of the user’s name in the application is necessary, so reading it from the database only one time is not a burden.
Binding only in one direction If a data element may be updated in a database or device, and those updates need to be displayed in the application but the data is never written from the application to the database or device, "OneWay" ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
54 binding may be used. OneWay binding is very appropriate for temperature displays as discussed above. A simple thermometer is a perfect example of OneWay binding. With no means or reason to modify the values, the thermometer only displays the temperature value it is sensing.
Binding in two directions If a data element is not only read and displayed on the user's interface, but also has the capability of being modified by the user and written back to the database, "TwoWay" binding must be used. TwoWay binding is a very familiar binding concept used in all "Update User Information" forms, for example. The information is initially read from the database, but also updated back to the database from the same form. Now that there is a basic understanding of what the types of binding are, an example each of those types will be constructed in the following sections.
3.2 Binding local data Binding a TextBox to a data element was discussed in the previous section. Silverlight doesn’t actually bind directly to the data element itself, it binds to a ‘property’. In other words, the binding is done to a member variable of a class. This does not hamper the ability of a minimal binding example, it just necessitates the need for wrapping the data in a class. The binding can then be done to a member of the class. A simple class can be created to retrieve system information. System information is a good example for One Time binding because the chances of some very basic system information changing during the life of an application are very small. The sample code is a class named MachineInfo that wraps the .NET version running on the local machine and the Silverlight version running on the machine as well.
Building a MachineInfo class Create a simple class in a Silverlight 4 project by right-clicking the project name and selecting Add, then Class, as shown in Figure 3.2.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
55
Figure 3.2 Add a Class to a project in Visual Studio 2010. Selecting Add Class as described in Figure 3.2 brings up the dialog of Figure 3.3 to name the class and pick other properties.
Figure 3.3 Naming the new class and setting properties ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
56 The default C# is taken, and “MachineInfo” is typed in as the name of the new class, and the “Add” button pressed. In the code shown in the listing below, the default namespace of Chapter3 was inserted automatically by the Add Class wizard. Two data entries were created, the get/set code for a Platform member, and a SLVersion date member. The intent is to return the platform of the target machine as well as the Silverlight version. The two data values are loaded in the constructor of the class. As shown in the listing, the platform is simple, a system call will return the proper value. The version of Silverlight loaded on the machine is more difficult to determine. Silverlight 4 provides an IsVersionSupported method, and takes a string parameter that is a version. By calling it with the latest version number, it is possible to determine if the target machine is running the latest version. If the version is the latest, the constructor will enter that value into the SLVersion parameter. If the version is not the latest, SLVersion is set to “Not Latest”.
namespace Chapter3 { public class MachineInfo { public MachineInfo() { Platform = System.Environment.OSVersion.Platform.ToString(); if (Chapter3.App.Current.Host.IsVersionSupported("4.0.41108.0")) { SLVersion = "4.0.41108.0"; } else { SLVersion = "Not Latest"; } } public string Platform { get; set; } public string SLVersion { get; set; } public void Setup() { } } }
The MachineInfo class is a little more code than simply defining two local variables, but once defined, the class can be used very similarly to the local variables, and better than that, Silverlight 3 allows binding to the class members.
So now what? Explain some of this binding stuff already! ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
57
OneTime binding Figure 3.4 shows a label and TextBox for displaying the platform name, and a label and TextBox for displaying the Silverlight version.
Figure 3.4 One Time DataBinding to static class variables. Binding the data for the TextBoxes in the screenshot of Figure 3.2 is going to mean modifying some of the XAML in MainPage.xaml. Once those changes are made, the Properties window of Visual Studio 2010 will provide the remainder of the binding effort.
Binding in XAML This section will detail not only the changes to be made to MainPage.xaml, but all the binding code will be done with no code behind changes. The first addition to MainPage.xaml is shown in the listing below. This namespace string points into the project being built, in the example shown the project is named “Chapter3”. The namespace is inserted in the head section of the UserControl along with the other namespace definitions. xmlns:local="clr-namespace:Chapter3" The next modification is immediately after the header section for the UserControl, and is as shown in the following listing. That completes all the changes that have to be made to MainPage.xaml directly. The binding can now all be accomplished through the Properties window.
Are we done yet?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
58 We’re not quite finished. The binding comes next. Select the upper TextBox, the Platform display control. Right-click the control and select Properties from the context menu. Scroll down the properties to Text and click the icon which brings up a menu as shown in Figure 3.5.
Figure 3.5 Text context menu from the Properties window showing Apply Data Binding
Select Apply Data Binding from the context menu to show the dialog of Figure 3.6.
Figure 3.6 Apply Data Binding Dialog prior to any edits.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
59 Select StaticResource which causes UserControl.Resources to appear in the center. This is the XAML section that was added earlier. Select UserControl.Resources and MachineInfo appears in the right-hand side. MachineInfo is the only class currently in the UserControl.Resource, which is the reason there is only one item showing on the right as demonstrated in Figure 3.7.
Figure 3.7 Apply Data Binding Dialog with Static Resource and UserControl.Resource selected. When MachineInfo is selected, the control scrolls to the left to display as shown in Figure 3.8.
Figure 3.8 Apply Data Binding Dialog with Static Resource, UserControl.Resource, and MachineInfo selected. Following the information in the new right-hand column, select the Path divider as shown in Figure 3.9, and in the first column, select Platform, to bind the TextBox to the Platform variable of the MachineInfo class. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
60
Figure 3.9 Apply Data Binding Dialog with MachineInfo variable Platform selected
What about the Binding mode? Where do we set that?
One final operation needs to be done in the Apply Data Binding dialog, and that is to select the binding option, by clicking the Options divider as shown in Figure 3.10.
Figure 3.10 Apply Data Binding Dialog with Binding Mode expanded
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
61 Possible values for Mode are OneTime, OneWay, or TwoWay as previously discussed in section 3.1, and shown in the Mode dropdown box in Figure 3.10. In this case, select OneTime. Perform the same actions on the lower TextBox, only selecting SLVersion for that one. After the above instructions are followed, both TextBoxes are bound to variables of the MachineInfo class for OneTime binding. Because the MachineInfo class is simply a wrapper for static data, two interesting side effects appear. First, all the MachineInfo information is available after the constructor runs. For that reason, the design surface displays the correct data. The second effect is no code behind changes need to be made, leaving MainPage.cs untouched. So OneTime binding saves me from writing a bunch of code, but what can I do with the data? OneTime binding has been shown to be easy to code, but the usability is limited to data not changing during the life of the application. Much more useful is OneWay binding which allows changes to the data to be displayed in the controls.
OneWay binding In the example of Figure 3.11, the Chg Temp button randomly changes the value of the Temperature member bound to the control, the Chg Time button updates the TempTime member, and Chg Both does both operations at the same time.
Figure 3.11 OneWay DataBinding to changing values based on button pushes To understand how changes in the members of a class appear in the Silverlight 4 controls, a discussion of INotifyPropertyChanged is necessary. This discussion will be important through the rest of this chapter.
INotifyPropertyChanged is the interface that forces the class member value to be updated to the bound control and therefore in some manner modify the display for the user. In the simple examples shown in Figure 3.11, text is displayed, and a TextBox output control suits the purpose. More elaborate examples may use a grid of values or a graphical display of some sort. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
62 To perform the negotiation with INotifyPropertyChanged in the binding, a Thermostat class is defined for the next sections, and is shown in the following listing.
namespace Chapter3 { public class Thermostat : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private int _Temperature = 0; private string _TempTime = ""; public int Temperature { get { return _Temperature; } set { _Temperature = value; UpdateTempProperty(Temperature); } } public string TempTime { get { return _TempTime; } set { _TempTime = value; UpdateTempTimeProperty(TempTime); } } private void UpdateTempProperty(int Temperature) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("Temperature")); } } private void UpdateTempTimeProperty(string TempTime) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("TempTime")); } } } } ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
63 One
noticeable
difference
between
Thermostat
and
MachineInfo
is
the
inheritance
from
INotifyPropertyChanged. That difference is what provides the updates to the bound variables. The setters for the members are different for the same reason, and contain method calls to Update methods of the class.
UpdateTempProperty, for instance, calls PropertyChanged and passes the Temperature property as a PropertyChangedEventArgs variable. PropertyChanged is defined just inside the class as being of type PropertyChangedEventHandler.
T This is all super-confusing!
The important thing to take from the two code examples is the fundamental difference between the two is that the one just above uses INotifyPropertyChanged. The setter change and the Update methods are
the
only
real
differences,
everything
else
is
simply
code
necessary
to
enable
the
INotifyPropertyChanged handler. The binding solution demonstrated in this OneWay solution will be an all-code behind solution. The Binding class is used as shown in the code below for both the Temperature and the TempTime variables and TextBoxes. Binding tempBinding = new Binding("Temperature"); tempBinding.Source = m_Thermostat; tempBinding.Mode = BindingMode.OneWay; tbTemperature.SetBinding(TextBox.TextProperty, tempBinding); Binding temptimeBinding = new Binding("TempTime"); temptimeBinding.Source = m_Thermostat; temptimeBinding.Mode = BindingMode.OneWay; tbTempTime.SetBinding(TextBox.TextProperty, temptimeBinding); Both Binding objects have the Source set to a Thermostat object, and the Mode set to OneWay. The Temperature binding is then set to the Temperature TextBox and the TempTime binding is set to the TempTime TextBox. When the application runs, the initialized values of the Thermostat constructor are displayed as shown in Figure 3.12. Clicking the buttons in the application causes the TextBoxes to change with the data in the Thermostat object, producing output similar to Figure 3.11.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
64
Figure 3.12 OneWay DataBinding screenshot of default values
In the real world, I can set the temperature I want on my thermostat.
That is exactly what we are going to be looking at in the next section, TwoWay binding.
TwoWay binding Figure 3.13 shows an addition to the OneWay example of a single Temperature TextBox. Because it is TwoWay, the lower Temperature TextBox changes as buttons are pressed just as the Upper Temperature TextBox changes. The Difference is that a new value may be entered into the lower Temperature box. As the user tabs out of the control, the upper Temperature display changes to match.
Figure 3.13 TwoWay DataBinding screenshot after a value is entered in the lower Temperature TextBox. The code behind to enable the functionality of Figure 3.13 looks remarkably like that of the OneWay binding with only the Mode change as shown below. Binding temp2Binding = new Binding("Temperature"); temp2Binding.Source = m_Thermostat; temp2Binding.Mode = BindingMode.TwoWay; tb2WayTemp.SetBinding(TextBox.TextProperty, temp2Binding);
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
65 To reiterate, the only change to enable the TwoWay functionality was to set the Mode to TwoWay.
INotifyPropertyChanged handles all the rest of the binding issues.
So now that we’ve gotten all the binding modes covered, what next?
Just displaying data is one thing, displaying it in a format appropriate for the user to understand is better, so next we are looking at converters.
Converting bound data Figure 3.14 demonstrates Date and Time TextBoxes that are formatted in readable form. The formatting could, of course, be done in the source object, or during a query, but that would mean additional code for every instance displayed. As will be demonstrated, a Value Converter can be written once and used across the entire application. A DateTime variable was added to the Thermostat class for this section. Binding a DateTime object to a TextBox would not work because the data formats are incompatible.
Figure 3.14 OneWay databinding of a DateTime object using Value Converters The screenshot of Figure 3.14 shows the date and time formatted out in a very readable format. The output formatting of Figure 3.14 is handled behind the scenes by the use of two Value Converters. A DateConverter class was added to the project as shown in the following listing. namespace Chapter3 { public class DateConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
66 { DateTime dt = (DateTime)value; return dt.ToString("MMM dd, yyyy", culture); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } }
The DateConverter is inherited from IvalueConverter and the class code shown in the listing above is a very minimal converter. IvalueConverter allows conversion both ways, just as data is bound both ways. In the example shown, only the Convert method is used. ConvertBack is defaulted to throw an exception if called. The syntax shown for the public object Convert is the standard syntax used for IValueConverter objects. The value object called out in the Convert method is the new dateTime object of the
thermostat class. value is cast to a local DateTime object, and then that object is formatted into a return string of the form desired for the display. In the case of the listing above, it was chosen to display dates in the format “MMM dd, yyyy”. Only one more piece of information is necessary to make the ValueConverter produce the output of Figure 3.14, and that takes place in the code behind. The next listing shows the code added to perform the binding and value conversion for the Date TextBox. Binding dateBinding = new Binding("dateTime"); dateBinding.Source = m_Thermostat; dateBinding.Mode = BindingMode.OneWay; dateBinding.Converter = new Chapter3.DateConverter(); this.tbDate.SetBinding(TextBox.TextProperty, dateBinding);
This looks familiar
The binding does look familiar to what’s been done previously in this chapter. The addition is the converter property of the Binding being set to an instance of the DateConverter. In Figure 3.14, both a DateConverter class and a TimeConverter class are demonstrated in use. The TimeConverter is very similar to the DateConverter and is shown below. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
67 namespace Chapter3 { public class TimeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { DateTime dt = (DateTime)value; return dt.ToString("HH:mm:ss"); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } It has been demonstrated with the time conversion that the only code behind change necessary for producing custom-displayed Date and Time strings is setting the DataContext. The combination of INotifyPropertyChanged on the Thermometer class and the Converter syntax in the binding efficiently ties those two concepts together allowing the developer to concentrate on other elements of the project. This chapter has demonstrated multiple ways to associate a Silverlight control with a data element. The code required to setup the display and supporting classes to wrap the data have also been discussed. Furthermore, ways to alter the display of the data in a somewhat global manner by the use of Value Converters has been demonstrated. Data display is normally more complex than the simple use of a TextBox as shown so far in this chapter. Data templates provide a unified way to display complex data. Data is also normally retrieved external to the application.
So how do we get external data with Silverlight?
Silverlight 4 provides an excellent manner of accessing external data by the use of Web Services. The next section combines the use of Data templates and Web Services.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
68
3.3 Data templates and Web Services Figure 3.15 shows what appears to the user to be a complex controls layout. The display in Figure 3.15 is crafted using a DataTemplate on a Label control. The Label control was chosen because of the ability to extend the Label’s use with a ContentTemplate.
Figure 3.15 Simple DataTemplate output There is no good coding path for the ContentTemplate and DataTemplate beyond writing XAML in MainPage.xaml. The XAML fragment below is the code necessary to produce the temperature visualization of Figure 3.15. Instead of a Content value, the label contains a ContentTemplate, and in turn the ContentTemplate contains a DataTemplate. The DataTemplate contains a Horizontally oriented StackPanel to encase the two data items displayed. The two data items are an image for a quick visualization of the temperature, and a temperature display in farenheit and celcius. The label also contains a border to give it the appearance of a TextBlock. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
69 The Image source is bound to a new Thermostat class member named tempImage using familiar binding syntax. The following listing contains the additions to the Thermostat class to add an Image parameter. private string _tempImage = ""; public int Temperature { get { return _Temperature; } set { _Temperature = value; if (_Temperature > 100) tempImage = "http://localhost:1934/Images/Hot.png"; else if (_Temperature < 40) tempImage = "http://localhost: 1934/Images/Cold.png"; else tempImage = "http://localhost: 1934/Images/Normal.png"; UpdateTempProperty(Temperature); } } public string tempImage { get { return _tempImage; } set { _tempImage = value; UpdateImageProperty(tempImage); } } private void UpdateImageProperty(string tempImage) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("tempImage")); } }
As shown above, the Temperature method was modified to set the _tempImage parameter based on temperature range of the incoming value. A very simple if/else check is done to determine which of three possible images should be displayed. The localhost:1934 value will probably be different if this code is compiled on the reader’s computer, and therefore have to be changed to include the appropriate development server port number. Three temperature images were added to the Chapter3.Web project in a new Images folder to go along with the tempImage definitions in the code sample above.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
70 The _tempImage property has its own method, tempImage, and an Update method identical to the others to handle the INotifyPropertyChanged code. The following listing contains the last piece of new code for the DataTemplate, and that is the TemperatureConverter class. namespace Chapter3 { public class TemperatureConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { int nFarenheit = System.Convert.ToInt32(value); int nCelcius = ((nFarenheit - 32) / 9) * 5; StringBuilder sb = new StringBuilder(); sb.Append(nFarenheit); sb.Append(" ["); sb.Append(nCelcius); sb.Append(" C]"); return sb.ToString(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } The TemperatureConverter class is inherited from IValueConverter the same as the Time and Date Converters discussed earlier in this chapter. In the case of the TemperatureConverter, the modification made to the output is to display Celcius in addition to Farenheit. A simple mathematical conversion is done formatting the data into a string that is returned.
Note the TemperatureConverter needs to be added to the Resources at the top of the XAML in the same manner that MachineInfo was. This is necessary because all thebinding is done in the XAML.
Repeating that section with this addition, the XAML is as follows.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
71 The Label control therefore contains a DataTemplate displaying an Image and a String. The image is bound to an image string in the Thermostat class that changes based upon the Temperature. The string value in the DataTemplate is bound to the Temperature property as before, but also includes a Converter that formats the text to display Celcius in addition to Farenheit. There is code involved in this solution, but the code is contained within two classes, and no extra XAML beyond what has already been displayed. One line of code behind must be added to MainPage.xaml.cs to wire up the binding used in the XAML. That code is shown below and associates the label content with the Thermostat object. this.lblTemplate.Content = m_Thermostat; The result of all the code changes is the DataTemplate demonstrated in Figure 3.15. The display was made somewhat more difficult in that example by using a DataTemplate. In the next DataTemplate example, the code used for Figure 3.15 makes much simpler work the ListBox output shown in Figure 3.16.
Figure 3.16 DataTemplate replicated in a ListBox
ListBox control and data template
Are we STILL looking at controls and the template?
Yes we are, and now we’re looking at the ListBox. The ListBox control is another control that allows the use of a template. In the case of the ListBox, the XAML element is an ItemTemplate which defines the manner of display for each item in the ListBox as displayed in the XAML fragment below. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
72 Comparing the listing above with that for the Label control used previously, it can be seen that the DataTemplate is identical. The only difference is that the ListBox will use the DataTemplate repetitively for each element in the ListBox. That repetitive use is intrinsic to the ListBox however and takes no code on the part of the developer. The ListBox is bound to a static list of data. In a real-world application, static data is not very useful, but removes the burden of wiring up a web service to a ListBox to make the ListBox and DataBinding code easier to understand yet fully functional. The static list of data and the single line of code behind to bind the list to the ListBox is shown in the listing below. List myDTColumn = new myDTColumn.Add(new Thermostat() { myDTColumn.Add(new Thermostat() { myDTColumn.Add(new Thermostat() { myDTColumn.Add(new Thermostat() {
List(); Temperature = 72 }); Temperature = 20 }); Temperature = 110 }); Temperature = 50 });
this.DTListBox.DataContext = myDTColumn; A list of Thermostat objects was created, each instantiated with a different Temperature value to show the various images. To complete the binding, the DataContext of the ListBox was assigned to the newly created List. When the tab is displayed, the DataTemplate displays the individual rows of the List in the exact same manner that the previously discussed DataTemplate Label is displayed. Therefore, each row is displayed with an appropriate image, plus the temperature in native and converted format.
In the real world we would us a web service to retrieve data from a server instead of a static list
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
73
That’s exactly correct, and wiring up a WebService is what we’re going to do next.
ListBox control, data template, and web service The WebService used in this example is somewhat contrived but demonstrates not only the fundamentals of wiring up a web service to a Silverlight control, but also the fundamentals of building a WCF web service into an application. Before the WebService discussion itself, the XAML for the new ListBox should be discussed. Since the only difference between the previous ListBox population and the next is that the new one is receiving data from a WebService, there is no need to display the XAML for the ListBox. It is identical to the previous XAML listing with the exception of the name changing to DTListBox2. This discussion begins with the Web application in our solution. Right-click the Web application, select Add, then New Item, then select the Silverlight Category. Under the Visual Studio Installed Templates section, select Silverlight-enabled WCF Service as shown in Figure 3.17
Figure 3.17 Add Silverlight-enabled WCF Service wizard
Why TempService, why did we name it that? What about TemperatureService?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
74 Because this service is going to be returning temperature values, the name chosen was TempService.svc. Any name would suffice as long as it was recognizable. Visual Studio will add the service and open it in the editor window. For the purposes of this simple example, the DoWork method was renamed GetTemperatures, which is shown below. Notice the return type change to the Visual Studio supplied code to return an array of integers. No other changes were made to the Visual Studio provided service code. [OperationContract] public int[] GetTemperatures() { int[] temps = new int[] { 10, 20, 40, 60, 80, 100, 120 }; return temps; }
The WCF service is returning an array of integers which represent temperature values. In a real application, this service could be accessing an array of temperature probes on a large machine. Because of the desire to maintain a simple application, the code above is all that is being added to the WCF service, and therefore all that is added to the Web project. Rebuilding the solution at this point is a good idea to ensure the proper coding of the WCF service and helping to avoid errors in the next steps. The Silverlight project needs to be given knowledge of the WCF service and since this service is local to the solution, that is a simple matter. Right-click the Silverlight project, scroll down the context menu and select Add Service Reference. This will open a dialog as shown in Figure 3.18.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
75
Figure 3.18 Add Service Reference wizard Following the directions on the dialog, click the Discover button. That automatically populates the Address and Services boxes with the newly-created TempService. Click the arrow to expand the Service and then click the arrow on the service to find the Operations. In this simple example there is only one service and one operation in that service. Assign a Namespace name to the Service reference as shown in Figure 3.19.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
76
Figure 3.19 Add Service Reference Completed When the Operation and Namespace is set, press OK to complete the addition of the Service Reference. Doing a Rebuild All at this point is a good idea to ensure no problems have been injected into the solution. Examining the Solution Explorer window, the Service Reference shows up as shown in Figure 3.20.
Figure 3.20 Add Service Reference Completed
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
77 Are these two talking to each other, or do they need counseling?
The code below must be added to get the Silverlight 3 project to talk to the WCF service. The Visual Studio IntelliSense will guide the developer through most of this code, but seeing the end result is always useful. var proxy = new TempService.TempServiceClient(); proxy.GetTemperaturesCompleted += new EventHandler(proxy_GetTemper aturesCompleted); proxy.GetTemperaturesAsync(); The proxy instantiation and GetTemperatureAsync() call shown next will be in Page Load or a helper method called by Page Load.
void proxy_GetTemperaturesCompleted(object sender, Chapter3.TempService.GetTemperaturesCompletedEventArgs e) { List myDTColumn = new List(); for (int nIndex = 0; nIndex < e.Result.Count; nIndex++) { myDTColumn.Add(new Thermostat() { Temperature = Convert.ToInt16(e.Result[nIndex]) }); } this.DTListBox2.DataContext = myDTColumn; } The code shown above retrieves the data from the WCF service and populates a List identical to the static data list for the previous ListBox. Setting the DataContext for the new ListBox to the new List completes the path to the xaml and when run, the ListBox will display the temperatures using the DataTemplate exactly as the first ListBox. There are quite a few steps to this process, but once the developer has gone through it, the process is quite straightforward and with all the code that Visual Studio adds in automatically, the process is relatively painless as well. So far in this chapter we have discussed databinding to local data by using a wrapper class and now through the use of a WebService. A discussion of Silverlight and data would not be complete without a discussion of Isolated Storage, and we will finish the chapter with that discussion. 3.4 Examining Isolated StorageAre you talking about storage like the locker I have down the street or what?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
78 Isolated Storage is a little like that in that it provides a way to save information on a user’s machine in an easily-retrievable manner. The IsolatedStorageFile is an instance of a dictionary stored in keyvalue pairs, and is used in the same manner as all other dictionaries in .NET. An important thing to note about Isolated Storage is that the file system of the client machine is not used in any manner for access. There are two dictionaries that may be written to and read from: ApplicationSettings and SiteSettings. ApplicationSettings are qualified by the full path of the application, and each user has unique
ApplicationSettings
for
every
application
run.
SiteSettings
works
similarly,
but
the
IsolatedStorageFile is scoped at the domain level. Any kind of data may be saved in Isolated Storage within the limitations of the storage quota. An application on a domain shares the same quota with all other applications on the domain. The quota is initially set to 100K, and an application may send a request to the user to increase the quota. The developer must be prepared for the possibility of the user denying the request, however. Prudent and well-engineered use of Isolated Storage should avoid quota limitation problems. Figure 3.11 displays contains simple Silverlight application containing buttons and TextBoxes used to demonstrate basic Isolated Storage functionality.
Figure 3.21 Isolated Storage in action The following listing is a partial code example of the code used in Figure 1.11. private IsolatedStorageSettings locAppSettings = IsolatedStorageSettings.ApplicationSettings; locAppSettings.Add(this.tbIsoKey.Text.ToString(), this.tbIsoInput.Text.ToString());
A
private
member
variable,
locAppSettings,
is
defined
as
being
of
type
IsolatedStorageSettings.ApplicationSettings. When the Save button is pressed, the value in the Input Value TextBox is stored into the ApplicationSettings dictionary using the key in the Key TextBox. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
79 In a similar manner, the listing below displays the recall from Isolated Storage code. When the Recall button is pressed, the ApplicationSettings value associated with the key listed in the Key TextBox is retrieved and displayed in the Display Value TextBox. string sKey = this.tbIsoKey.Text.ToString(); if (locAppSettings.Contains(sKey)) { this.tbIsoDisplay.Text = locAppSettings[sKey].ToString(); } else { this.tbIsoDisplay.Text = "No Key"; } A more revealing test can be conducted by saving a Key/Input Value pair, closing the Silverlight application, restarting the Silverlight application, and pressing Recall with the proper Key value in the Key TextBox. The value saved into Isolated Storage in the previous execution is available and correct. Figure 3.21 shows one more function associated with the Key/Value pairs that is demonstrated, and that is Clearing the Isolated Storage settings. The following line of code will clear all Application Settings from Isolated Storage. locAppSettings.Clear(); Continuing to store data into Isolated storage without removal could overrun the 100K initial limit.
Yeah, I had that problem when I tried to put my boat in the storage locker. Most of it fit.
The wisest storage method would be to know ahead of time if there was enough room in Isolated Storage to hold the data currently to be saved. The next section does exactly that.
Examining Isolated Storage Quota One more important part of using Isolated Storage is knowing how much storage is available to the application. Silverlight provides a way to not only find out how much remains but a way to request more from the user. As mentioned earlier, the developer needs to be prepared for the user to deny the request, so determining the amount remaining is very important. Figure 3.22 contains a Silverlight form used to test the query available and request additional Isolated Storage functions.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
80
Figure 3.22 Isolated Storage Quota The listing below shows the code necessary to populate the Quota and Available TextBoxes on the tab of Figure 3.22. tbIsoQuota.Text = IsolatedStorageFile.GetUserStoreForApplication().Quota.ToString(); tbIsoAvailable.Text = IsolatedStorageFile.GetUserStoreForApplication().AvailableFreeSpace.ToString(); The code above details a helper method named refreshStats() that is called from Page Load and also from the Refresh button on the tab. Two very simple method calls to the IsolatedStorageFile class provides the Quota and AvailableFreeSpace. The two values are converted to strings and placed in the appropriate TextBocks. When the Request Increase button is pressed there is code that requests the current Quota, adds it to the Requested amount and makes the request to the user. The user request code is shown next. if (this.tbRequest.Text != "") { using (var store = IsolatedStorageFile.GetUserStoreForApplication()) { Int64 IsoQuota = store.Quota; if (store.IncreaseQuotaTo(IsoQuota + Convert.ToInt64(this.tbRequest.Text.ToString()))) { refreshStats(); } else { refreshStats(); } } } The existing Quota plus the requested amount must be included in the call because the sense of the request message is that the code author is asking for a total Quota. This makes sense because the user may not know how much is already allocated.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
81 When the Request is made, the user is presented with a dialog box similar to that of Figure 3.23. The user may elect to deny the request at which time the developer must have a clear idea of how to continue. The code in the example for this chapter simply refreshes the display in either case.
Figure 3.23 Requesting additional Isolated Storage Quota This was a very difficult chapter because of the concepts. If you’ve followed along faithfully you’ve accomplished quite a bit. You now have a good basic understanding of databinding in general, and WCF WebServices as a bonus. Isolated Storage is the final piece of the data puzzle with Silverlight, and that was the last item covered.
3.5 Summary There are very few applications worth running that do not make use of data in some manner. Silverlight 4 applications are no exception to that fact. Displaying data in Silverlight 4 controls is not a difficult task. The examples in this chapter should get the reader started on a path to displaying data when needed. WCF Services are almost a defacto standard for Silverlight 4 data-driven applications, and the examples of this chapter demonstrate initiating and consuming one. Rounding out the chapter is a section on Isolated Storage. Having a good understanding of the use of Isolated Storage will extend the user’s capability to write fully functional real-world applications in Silverlight 4. The next chapter will introduce you to Expression Blend and provide an alternate and easier way to lay out your Silvelright applications.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
82
4 Learning XAML Through Blend Silverlight’s built in controls make it easy to get started and build some interactive applications, but you would be hard pressed to convince someone that they provide a “rich” experience. They’re great if you’re creating an application where function is more important than fashion, but you’re not going to wow many users. Fortunately you can use some of Silverlight’s powerful graphics library to create your own custom user interface from the ground up or to customize existing controls. The key to customization in Silverlight is to use XAML building blocks to create the look you want. XAML has a bit of a learning curve so we’ll use Microsoft Expression Blend to create the XAML for us and then take a look what it did. Anything Blend generates can be done by creating the XAML yourself once you know how. Let’s start by seeing how we can install Blend and use it to create some basic shapes.
4.1 Blend basics
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
83 Expression Blend is Microsoft’s design tool for Silverlight and WPF. It is targeted at graphics designers and is meant to work like other tools that they are familiar with. This can make it a little foreign to developers but with some practice you can get the hang of it. Unlike Visual Web Developer Express, Microsoft Expression Blend is a retail product and there is not a free version. WHY SHOW US A RETAIL PRODUCT WHEN WE MIGHT NOT HAVE ACCESS TO IT?
Even though you may not have the resources to purchase Blend it can be a great learning tool and there is a 60 day free trial available. You can go to www.microsoft.com/expression/try-it to download the trial version. Make sure the version you download matches the version of the Silverlight SDK you have installed. Once you install Blend, launch Visual Studio or Visual Web Developer Express and create a new Silverlight Application called XamlBasics. Take all of the defaults in the application wizard. In the Solution Explorer, right click on MainPage.xaml. When you have Blend installed, you get a new option on this context menu to help you switch between your development environment and Blend.
Figure 4.1 You can easily switch to Blend by right clicking on an item in the Solution Explorer. Choose the Open in Expression Blend… menu item. This will launch Blend and open the selected item. Since Blend uses the same solution and project structure you can move back and forth between the tools at will and changes made in one will automatically show up in the other. You should now see something like the following:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
84
Figure 4.2 Expression Blend with the design surface in the middle.
WHY DOES IT LOOK SO DARK AND INITIMIDATING?
There’s actually a very good reason for the subtle shades of gray in Blend and in other tools like it. Brighter colors like those in Visual Studio can distract from the design process and cause eye strain. Don’t worry it’s not as intimidating as it looks. Along the left hand side there is a toolbar that we’ll be using a lot. First let’s select the shape tool that looks like a solid rectangle.
Figure 4.3 When you mouse over a tool it tells you what it is and how to get more options.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
85 Now move your mouse over to the design surface and draw a draw a rectangle by clicking and dragging in the white area.
Figure 4.4 As you drag the mouse the width and height are displayed to help you with sizing. Along with the design surface you can also show just the XAML or a split view. These controls are along the right hand side at the top in the image above. Choose the split view icon which will help us see what XAML is created by our actions. This is great for learning about XAML because you can see immediately what changes when you edit elements on the design surface or by editing properties. In the XAML, your Rectangle element will look something like the following: The attributes on the Rectangle element correspond to properties in the Rectangle class. You should also see an Objects and Timeline tab to the left of the design surface. This is a hierarchical representation of the elements in the item you are currently editing. There should be a new Rectangle element that is a child of the LayoutRoot grid. When you select an element in the Objects and Timeline tab it is also selected on the design surface and its properties will be loaded up in the Properties panel. Let’s take a look at how we can edit properties and what the resulting XAML looks like.
Layout and Appearance properties When you select the Properties tab you’ll see the Properties panel for the selected element. This panel will change and display different sections and different properties within those sections based on which element is selected and what type of layout panel contains it. The first section we are interested in is Layout.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
86
Figure 4.5 The Layout section of the Properties panel includes properties for size, alignment, and margins. You will also see any attached properties for this element.
WHAT DO THOSE LITTLE SQUARES ALONG THE RIGHT DO? If you click on one of these squares you’ll see a context menu for that property. If the property hasn’t been changed from its default value it will show up as an unfilled box. If the property is set explicitly it will be white and there are other colors to indicate if it’s being set via data binding or from a resource. The Margin property is set based on how we drew the rectangle but we don’t want these values so click the square reset this property. Then enter a Width of 200 pixels and a Height of 100 pixels.
Pixels as a unit of measure in Silverlight Throughout this book, the term pixels will be used as the units for the width, height, and position of elements. This is not entirely accurate, since elements in Silverlight are inherently scalable. An element with a width of 50 is actually 50 pixels wide if the scale is 100 percent, but if the element is scaled, then it might actually be more or less than 50 pixels wide. It is easiest to refer to these units as pixels instead of some more abstract term.
Now the rectangle will be centered in the grid cell and have the specified size. The XAML now looks as follows:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
87
WHAT IF I WANT TO DRAW WITH A THICKER LINE? If you don’t tell Silverlight how thick to make a Stroke it will make it one pixel wide. You can change this using the StrokeThickness property which can be found in the Appearance section of the
Properties panel. Let’s specify a StrokeThickness of 10. As you would expect, this results in a thicker line for drawing the rectangle:
Figure 4.6 Setting the StrokeThickness can give us a wider border.
THAT’S KIND OF BOXY, HOW ABOUT SOME ROUNDED CORNERS?
In Silverlight rounded corners are a piece of cake. The Rectangle has RadiusX and RadiusY properties which are used for exactly that. By default these values are 0, which gives the rectangle a square corner. In the Appearance section let’s set the RadiusX and RadiusY of our Rectangle to 20 pixels each.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
88
Figure 4.7 It’s very easy to create rounded corners in Silverlight
Since Silverlight uses vector graphics you get a very clean edge and even if you scale this rectangle the corners will look great. There are a few properties you can set to further customize the look of the edges. If you click the little arrow at the bottom of the Appearance section you’ll see some more available properties. By default, the StrokeDashArray is set to “1 0” which means draw with a dash of size 1 and a gap of size 0 then this sequence repeats. This size value is relative to the stroke thickness. Change this to “3” and you’ll see evenly spaced dashes where the gap is the same size as the dash. Alternatively you can set it to “3 3” or “3 3 3” or any number of the same value and get the same result.
Figure 4.8 Setting the dash array to equal values creates a very regular dash pattern. You can use any number of values in this dash array and when it runs out of values when drawing the dashes it starts over with the first entry. Where this gets interesting is when you have different numbers in your dash array. If you have three entries in your dash array like “1 2 3” then the first dash is size 1, the first gap is size 2, the second dash is size 3, and the second gap is size 1 again. It will look something like this.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
89
Figure 4.9 This is a funky dash array that you get with an odd number of values in the array. Probably a bit more useful is if you have an even number in the array greater than 2. Consider an array of “2 1 3 1”.
Figure 4.10 Alternating dash sizes can give a nice look.
I’VE SEEN SOMETHING LIKE THIS IN MY PAINT PROGRAM
That’s right, although a lot of times this dashed line will be animated to make it clear what is selected. This technique is called “marching ants” and you could do this effect easily in Silverlight by animating the StrokeDashOffset property. We’ll cover animations in chapter 5. Now let’s reset the dash array to the default for a solid stroke and see how we can change the color we are drawing with.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
90
4.2 Drawing with brushes
Brushes are a very important part of Silverlight. Simply put, a brush is what you use to draw with. In the Brushes section of the Properties panel select Fill and then use the color picker to choose a new color.
Figure 4.11 The Brushes section of the Properties panel. On a Rectangle you can set the Fill and Stroke colors.
WHAT’S GOING ON WITH THAT #FFC6C6C6 GIBBERISH?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
91 The #FFC6C6C6 is what’s called a hexadecimal (or hex) representation of our color. Up until now we’ve used named color values like White and Black. Each named color value is translated into its hex equivalent before it is used. Colors in Silverlight are a combination of red, green, blue, and an alpha channel with each value ranging from 0 to 255. Colors that are not named are represented as a # sign followed by hex pairs for alpha, red, green, and blue, in
that
order.
This
is
similar
to
the
HTML
representation except in HTML the color doesn’t have an alpha value. For example, a solid non-transparent white would be #FFFFFFFF. The named color Red corresponds to #FFFF0000 and if you replace Red with this value, you will get the same result.
Alpha channel – Allows a color to be partially transparent and the color is blended with the background color. 255 is totally opaque, and 0 is completely transparent.
I CAN CONVERT HEX VALUES IN MY SLEEP. ACTUALLY I REALLY DO, I DREAM IN HEXADECIMAL. If you don’t have the brain of a computer you can use the Calculator program that comes installed with Windows to do your conversions. Just set the view to Scientific (Programmer view in Windows 7) and then you can use the Hex and Dec radio buttons to switch back and forth a color at a time.
Figure 4.12 The Calculator application in hex and decimal modes. A value of 255 in decimal mode is converted to FF in hex mode.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
92 Any property that can accept a brush actually has two XAML formats that you can choose from, a simple and an expanded format. For the expanded format the property is represented as a child XML element instead of an attribute. When you specify a single color for a Stroke or Fill what you’re actually using is a SolidColorBrush. The following two pieces of XAML create identical output.
WHY WOULD I EVER USE THE ONE ON THE RIGHT?
For a solid color you probably would always use the shorter one, and this is what Blend generates when using a single color. There are, however, other brushes that need to specify more information than can be represented as an XML attribute. Let’s take a look at one of these which lets us specify a gradient to draw with.
When one color isn’t enough A gradient is an effect where colors are specified at certain offsets and then the colors gradually shift from one color to the next providing a smooth color transition. Gradients have become very popular in web design over the past few years, and can be used to provide a glassy look, subtle shading, or a 3D effect. Silverlight supports two kinds of gradient brushes. One of these is the LinearGradientBrush where the color changes uniformly along a specified line. This line is called the gradient’s axis. This axis can
be
horizontal,
vertical,
or
somewhere
in
between.
The
other
gradient
brush
is
the
RadialGradientBrush. The color of the RadialGradientBrush varies based on the distance from a center point known as the gradient’s origin. Here is an example of a linear and a radial gradient brush:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
93
Figure 4.13 The same colors and gradient stops, one applied as a linear and one as a radial gradient. Both gradient brushes use gradient stops to control what colors are displayed. These gradient stops contain a Color and an Offset, and the Offset is a decimal value from 0 to 1. You can create a gradient with as few as two gradient stops or many more for some complex gradient effects. The gradients above contain four gradient stops. Let’s fill a rectangle with the LinearGradientBrush. To do this go to the Brushes section and select the gradient brush icon.
Figure 4.14 Selecting the gradient brush icon changes how the Brushes section looks.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
94 In the lower left hand corner you can select whether to use a linear or radial gradient brush and it defaults to linear. Just above this is a graphical representation of the gradient stops and you can click on each of these gradient stops to select its color or click anywhere that there isn’t already a gradient stop to add a new one. The default gradient that is created when you select the gradient brush looks like this: If you select the black gradient stop you can change its color, for example to red. Once you make this change your rectangle will now go from red to white.
Figure 4.15 A rectangle with a vertical linear gradient The direction of a linear gradient depends on the StartPoint and EndPoint. The StartPoint defaults to 0.5,0 and the EndPoint defaults to 0.5,1 giving a vertical gradient.
SO TWO X,Y VALUES TO CREATE A LINE THAT THE GRADIENT WILL FOLLOW. That right and you might want it to be diagonal, or left to right, or some other direction. You can change the start and end points by using the Gradient Tool in the toolbar.
Figure 4.16 The Gradient Tool looks like an arrow with a gradient fill. It lets you change the direction and start and end points of a gradient.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
95 Select the Gradient Tool and then click anywhere inside the rectangle to select the Fill property. You’ll then see an arrow that you can rotate, move, and resize to change your gradient. If you want to have more color transitions you can add more gradient stops. By adding more gradient stops we can get some cool effects. One interesting way to make a gradient is to use the gradient eyedropper tool in the Brushes section. You can draw a line inside or outside of the Blend window and get a gradient representation of the colors under that line. Be careful if you select something with a lot of color variations because you can get a large number of gradient stops. This is a gradient created with the eyedropper: And this is how it looks:
Figure 4.17 A gradient captured with the gradient eyedropper tool. Multiple gradient stops give it a highlighted look.
HOW ABOUT IF A SOLID COLOR OR A GRADIENT DOESN’T REALLY DO WHAT I NEED?
There are other options for brushes and one of these allows you to use an image as a brush. If you want to add an image to your application you can use the Image element and we’ll see some samples of this in Chapter 6. When you use an Image element you get the basic rectangle with the image in it and you can dress it up by putting borders or drop shadows on it. Let’s say you want to do something more than that and you want to apply the image as the fill for a shape. This can be done using the
ImageBrush. The ImageBrush works like the brushes we’ve seen so far but uses an image to draw with. To use an ImageBrush in Blend, select the Tile brush icon.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
96
Figure 4.18 In the tile brush properties you can select the image and how it should stretch to fill the available space. When you select an image that is not already in your project it will be added to your project for you. This is necessary since this image will need to be packaged with your application so that Silverlight has access to it. The corresponding XAML that gets created looks like this:
Image brushes aren’t limited to the Fill property. Most things that can be drawn in Silverlight can be drawn using any of the various brushes that are available. This includes the foreground of text or the stroke surrounding a shape. A couple of other interesting brushes that are beyond the scope of this book are the VideoBrush to display video as a brush and the video actually can keep playing just like it would in a MediaElement and the HtmlBrush which can use the contents of a web page as a brush. The HtmlBrush is only available in out of browser mode and there is more information about working with HTML in Chapter 7.
ENOUGH WITH THE RECTANGLES. HOW ABOUT A ROUND WEB 2.0 STYLE GLASSY BUTTON WITH A DROP SHADOW AND A HIGHLIGHT. HOW ABOUT THAT?
Drawing a glassy ball Do you mean something like this?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
97
YEAH THAT LOOKS OK I GUESS.
Figure 4.19 A glassy Web 2.0 style round ball First since we would like the ball to be resizable let’s put it in its own grid. Delete the rectangle and then select the Grid element in the toolbar and draw it. It doesn’t matter how big you make it because we’ll override the default settings. Reset the margins and set the width and height to 200. Now if you right click on the shape tool (the one that looks like a rectangle) you can choose to draw an ellipse instead. Notice that Blend tries to be helpful and uses the same stroke and fill properties that we used for the last shape. Reset the stroke value and the margins. For the fill we’ll want the following: You can enter these one by one in Blend or just choose the default gradient fill and then edit the XAML directly. Sometimes it’s just easier to get your hands dirty and dig into the raw XAML. Notice that the syntax is identical to the LinearGradientBrush except for the element name. Your ellipse should now be lighter in the center with a dark edge.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
98
Figure 4.20 The circle has multiple gradient stops to give the illusion of roundness Now let’s add the shadow. This will give the ball more of a 3D look. The easiest way to do this is by using an effect. In the Appearance section you can click on the new effect button:
Figure 4.21 Choosing a new effect in the Properties panel. This brings up a dialog you can use to choose the effect. There are two built in effects, drop shadow and blur, or you can create your own. The one we need here is the DropShadowEffect. Choose this and set the BlurRadius to 20. This controls how much to blur the shadow so that it can be seen behind the ellipse. This creates the following XAML: Now you should see a drop shadow behind the ball. You can play with the other properties such as
Direction and ShadowDepth to see how these affect the shadow. Remember that you can always undo your changes so don’t be afraid to play around and see what happens. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
99
Figure 4.22 Our glassy ball with a shadow
Now to finish the ball off, we need an ellipse for the bright highlight at the top of the ball. This uses a linear gradient from a partially transparent white to a transparent white. The tricky part is to position the highlight properly on the ball but also allowing it to scale properly. For this we’ll enlist the help our parent grid. By giving it three rows and three columns and taking advantage of star sizing we can dynamically scale the ball to any size we need. Here are the row and column definitions: Again once you create the rows and columns by clicking on the edges of the grid you may want to manually enter the exact height and width values instead of trying to get the exact right values. As long as you get close you should be fine and you can always adjust it until it looks right. Now draw another ellipse in the center cell. Reset the margins and choose a linear gradient fill. For the first gradient stop enter a color of #99FFFFFF which is a semi-transparent white and for the second gradient stop make it
#00FFFFFF which is a completely transparent white. By making these fill colors partially transparent some of the blue from the ball shows through and by fading from partially transparent to completely transparent it looks like the top of the ball is getting hit by a light. That completes the ball, and it should now look like this:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
100
OOH SHINY.
Figure 4.23 Not bad for a couple of ellipses and a drop shadow. The resulting XAML is very small and when packaged in the compressed XAP file it will be compressed even smaller. If you wanted to create a similar effect with HTML and CSS, you likely would have to export it to an image. This image would be larger than the corresponding XAML and with the XAML you also get the benefit of being able to scale it to whatever size you need without losing quality. This example should drive home the power of gradient brushes in XAML and XAML in general. Now let’s take a look at some slightly more advanced graphics techniques in Blend and learn about new XAML elements along the way.
4.3 Drawing complex shapes
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
101 Sometimes you need more than a rectangle or ellipse to get the look you want. Let’s say for example you want a play button for a video player and you need to draw a triangle. There isn’t a triangle element so we need to make our own. Start by drawing a rectangle. Make it the approximate size you want your triangle to be. Now in the Object menu, choose Path and Convert to Path. The rectangle will look exactly as it did before but if you look at the XAML you’ll see something like this:
I UNDERSTAND MOST OF THAT, CAN YOU EXPLAIN THE DATA PROPERTY?
The Data attribute of the Path element is a concise way of defining the shape. Alternatively there is an expanded format that uses a child XML element but it’s pretty complicated. This shorthand is actually not too hard when you get the hang of it and table 4.1 has a list of some of the more common commands that are valid in the Data attribute.
Table 4.1 Some Path Data commands Command
Description
Hx
Draw a horizontal line from the previous position to the x value specified.
Vy
Draw a vertical line from the previous position to the y value specified.
M x,y
Move the current point to the x,y specified, don’t draw a line from the previous point.
L x,y
Same as M, but draw a line from the previous point.
C x1,y1 x2,y2 x3,y3
Draw a cubic Bezier curve between the previous point and x3,y3 using the control points x1,y1 and x2,y4.
Z
Close the shape and draw a line to the starting point.
Now to make this into a triangle we’ll select the pen tool in the toolbar. Select the Path element and then using the pen tool click about halfway down the right side of the rectangle.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
102
Figure 4.24 A rectangular path with an extra vertex. This new vertex will be the third point of the triangle.
You’ll get another little box (called a vertex) like the ones in the corners. Now if you click on both corners on the right hand side with the pen tool selected those vertices will be removed.
Figure 4.25 Removing the other 2 vertices creates a triangle.
THAT WASN’T TOO BAD, EVEN I CAN DO IT
This is a trick that graphics designers have been using for ages to make triangles. Now let’s take a look at creating a complex shape. In Chapter 8 we create a game and there is a chicken graphic we need. Here is the chicken:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
103
Figure 4.26 A little teaser for Chapter 8 where we’ll create a game with this guy. The body portion of the chicken is a single Path object, and it needs to be so that we can apply a gradient fill to it and apply a stroke. This shape is actually easier than it may look and starts out with three ellipses.
Figure 4.27 Three ellipses give us an approximation of the chicken’s body. For the two wings, we can convert them to paths and flatten the inside parts of the wings. Finally, select each of the ellipses while holding down the Ctrl key and then from the Object menu select Combine and then Unite. You can also play with the other combine modes and see what they do. Uniting these three shapes gives you the following:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
104
Figure 4.28 The combined shape for the chicken’s body. This can then have a fill and a path applied for the desired effect. These are just a couple of the useful techniques you can use to draw in Blend.
THIS IS KIND OF FUN, IS THERE ANYWHERE I CAN LEARN MORE?
Project Rosetta at visitmix.com/labs/rosetta is a great site to learn more Blend techniques. Also many of the Adobe Illustrator of Photoshop tutorials can give you ideas for things to try in Blend. Now let’s take a look at how to customize the look of existing controls.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
105
4.4 Control styling
So far we’ve learned how to set properties on a single element. Many times in an application you want to give a consistent feel to a page or across pages for the entire application. This is especially true of buttons and other input controls. It would look a little strange if the OK button was a rectangle and the cancel button was round and they used different fonts and font sizes. You could copy and paste the property settings from one element to the other but then if you wanted to change how all of them looked you would have to go to each separately and change it. If you’re familiar with HTML this is one of the big benefits of CSS where you can define a group of properties once and use it repeatedly.
COOL, XAML HAS CSS?
No, not quite, but it has something similar. In XAML this is done by creating styles. A style is a collection of properties which you can apply to multiple elements. It reduces the need to repeat property definitions and can lead to smaller XAML and you can give your application a consistent feel. Let’s start by drawing a button.
Figure 4.29 The plain Jane button with no styling. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
106 In Blend, you can create a style by going to the Object menu and selecting Edit Style. Since we don’t have an existing style choose Edit a Copy… and the properties that have been changed from the defaults are grouped into a style. You will now see a dialog to select other options related to creating this style resource.
Figure 4.30 There are many choices when creating a style. If you select to define the style in the current document then it will be added to the resources for the current document and will not be available to other user controls or pages in your application. If you define it at the application level the style is created in App.xaml and you can use it anywhere in your application. Let’s choose to define it in the application and name it ButtonStyle1. You will now be in the style editor where you can set properties which will be applied to this style. Set some properties, like border brush, border thickness, italics, and foreground. This is our button style after modifying those properties:
Figure 4.31 Our button after setting some colors and other properties in its style.
THAT’S PRETTY UGLY.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
107 You’re right, but we wanted to make sure it stood out. Now let’s draw another button. You’ll see it looks just like the default button. For named styles you need to specify the style in the XAML of each of the elements you want to apply the style to. If you go back to the Object menu and select Edit Style you can apply the ButtonStyle1 style to this button too. For every button using this style, you’ll see a
Style attribute like this: The App.xaml contains a style definition with the properties we have defined. The x:Key attribute corresponds to what we specify for the static resource on the button. It’s tedious to specify this on every button so there is another option. Select one of the buttons and go back to the Object and Edit Style menu, choose Edit a Copy… and this time select Apply to All and define at the application level. This creates a style in App.xaml just like the last time but this time there is no x:Key attribute. This tells Silverlight to use this as an implicit style. Now if you go back to
MainPage.xaml and draw a new button it will have the same style and if you edit the style it will change all buttons unless you override the properties or style for that specific button.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
108
Figure 4.32 Three buttons all using the same implicit style.
NOW YOU HAVE THREE UGLY BUTTONS.
Creating styles isn’t limited to just buttons. You can create styles for any of the controls or other visual elements and assign these on a per-element basis or for all elements of that type in a single page or for the entire application. Let’s take a look back at what we’ve learned in this chapter.
4.6 Summary
XAML is a feature rich language based on XML which provides an easy way to separate the layout of the visuals for an application from the procedural code. Notice that we didn’t write a single line of procedural code in this entire chapter, it was all XAML, and Blend helped us to create it. Microsoft ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
109 Expression Blend is a tool targeted at graphics designers but with a little bit of work developers can get some good value out of it as well. If nothing else it is a great way to learn about XAML and about the various elements and controls that are available. For most things you draw in Silverlight you can choose what brushes you use to draw them. These brushes can be a solid color, a linear or radial gradient, an image, or even a video or HTML. By combining some simple shapes with brushes you can create some impressive visuals like our glassy ball. Sometimes the basic shapes aren’t enough to get the look you want. In these cases you can use some time tested techniques of graphics artists to create more complex shapes by converting basic shapes into path objects and then modifying or combining them. Now that we have covered the basics of XAML the next chapter will put what we have learned to use and introduce some new techniques including animations as we step through the creation of an animated fish eye menu control.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
110
5 Creating a Fish Eye Menu In this chapter we will take the concepts covered up to this point and put them into practice. We’ll also and learn some new techniques along the way. The sample project is a simple Fish-Eye menu, made popular on the Mac and common on web sites these days.
Figure 5.1 This is our goal for the end of this chapter. The menu animates when the mouse hovers over an item.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
111 If you have a menu, you likely have pages too where each menu click brings you to a different page. You could write code to show and hide each of these pages, but Silverlight provides controls and wizards to make this much easier. If you’re creating a multi-page Silverlight application, the Silverlight Navigation Application project type can give you a good head start. User controls help you to create reusable elements that are easier to maintain than having everything in one big XAML file. In this sample, the menu will be a user control. Animations are an important part of making an application visually interesting, and to give a better user experience by providing visual cues in response to user interaction. Silverlight also provides powerful capabilities to scale, rotate, and otherwise transform any visual element including complex elements such as layout panels. Using these transforms in combination with animations can provide impressive results. So let’s get started.
5.1 Navigation applications
Up to this point we have used the Silverlight Application project type to create our initial solution. There is another useful option in the Visual Studio New Project dialog called a Silverlight Navigation Application. This project type is identical to the Silverlight Application type except there are some more files and content generated to help you get started. You could add the navigation controls and other elements manually to an existing project if you wanted, but the project wizard can save a lot of time. Our goal for this chapter is to create a fish eye menu that lets us navigate to multiple pages, so let’s create a new Silverlight Navigation Application called MySite. We will build on this application in the next chapter by implementing the photos page.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
112
Figure 5.2 Creating a new Silverlight Navigation Application project. The application wizard then looks just like the one for the Silverlight Application wizard and you have the same options. Take the default values which automatically create a web application project to host the application. We’ll need that web application in the next chapter to host some web services for us. Ok so now let’s see what the Silverlight Navigation Application wizard generated for us. If you run the application you’ll see that it’s more interesting than what the Silverlight Application wizard generates.
IT’S NOT TOO HARD TO GET MORE INTERESTING THAN A BLANK WHITE SCREEN.
Figure 5.3 The default navigation application provides two content pages and navigation.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
113 MainPage.xaml has a few components in it that should be familiar to you, as well as a new control which handles the navigation. This control is the Frame control. The Frame control acts like a content place holder in a master page in ASP.NET. Based on the browser URL that is specified different content is displayed. If you click on the “about” link, you’ll see that the about page is displayed, and the URL in the browser’s address bar will change to something like this: http://localhost:16248/MySiteTestPage.aspx#/About The Frame element maps the anchor to the appropriate page.
WHAT’S AN ANCHOR?
The anchor is the part of the url after the # sign. It is commonly used in HTML to allow a user to bookmark a particular area of a page. The UriMapper property of the Frame object maps these anchors to a XAML file. In the XAML above, the first UriMapping maps an empty anchor to /Views/Home.xaml to take care of the default home page and the second tries to find a matching XAML file in the Views folder based on what’s in the anchor. This allows you to bookmark a specific page in your Silverlight application and it also lets you use the browser’s back and forward buttons. You can also enable deep linking by creating more advanced UriMapping entries to do things such as go to a specific blog post or product based on the URL specified. If you look in the Views folder of the project, you’ll see Home.xaml and About.xaml. These contain the content for the home and about pages respectively. You’ll also see ErrorWindow.xaml which is used to display error messages when an invalid anchor is specified. For example if you change the anchor to #/foo you’ll see an error window.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
114
Figure 5.4 Error when invalid anchor is specified. The application is grayed out and the window animates into view automatically. The error window is implemented using a ChildWindow control. Since most browsers block pop-up windows this is a good alternative to when you need a modal dialog. This child window is actually displayed inside the Silverlight control so it won’t be blocked by the browser.
HOW ABOUT ENABLING THE BROWSER’S BACK AND FORWARD BUTTONS? You actually don’t have to do anything special to make this work. Since the anchor tag is changing on the URL the browser saves this as a different page and the back and forward buttons just work. When you navigate back and forth this isn’t actually reloading the page, it is just changing the content of the frame based on the anchor. This means much faster navigation than if a page refresh was needed. You can also bookmark a page like the about page and return to that page specifically. The pages that we want in the application are blog, photos, videos, and the about page. So let’s delete Home.xaml for the project and add a new item of type Page to the Views folder. Name this new page Blog.xaml.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
115
Figure 5.5 The Add New Item dialog allows you to add new Silverlight Pages to your application. If you take a look at Blog.xaml you’ll see that it looks very similar to the MainPage.xaml of a standard Silverlight application except that it has a Page as its root element. While we’re at it, add two more pages to the Views folder, Photos.xaml and Videos.xaml.
About.xaml already exists so we don’t need to create that one. In order to make it easier to tell what page we’re on, add a TextBlock to each page with the page name in it. This is just a placeholder until you add some real content there. Here is how it will look for the Blog.xaml page:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
116 Since we’ll be implementing our own menu we can remove the links menu from MainPage.xaml. Delete the Border named LinksBorder and all of its content. There is also some corresponding code in
MainPage.xaml.cs
which
highlights
the
link
text
for
the
current
page
in
the
ContentFrame_Navigated method. Remove the contents of this method as well.
WHY HAVE THE WIZARD CREATE THAT STUFF IF WE ARE JUST GOING TO DELETE IT? It’s easier to delete code than add it, and in many cases the the code is close enough to what you need that you can just do some simple modifications. While you’re in MainPage.xaml you can also change the application name text to whatever you want it to be, we’ll call it “My Silverlight Site”. Finally, since the default page is now Blog.xaml and not Home.xaml, change all references to Home in
MainPage.xaml to Blog. Now if you run the application, you should be able to navigate to each of your pages by specifying the correct anchor tag. The frame contents and the browser’s title bar should change based on which page you’re on. Now it’s probably not the best user experience to have the user type the correct URL in the address bar, so let’s start building our fish eye menu. To make the menu reusable and to keep extra clutter out of the MainPage.xaml we can implement the menu as a user control.
5.2 User controls
When creating Silverlight applications we could stick all markup and code in the MainPage class but this typically isn’t good form. It’s better to let your MainPage class handle application level logic but do most of your layout in controls. Silverlight user controls are based on WPF user controls, and follow the same syntax. If you have done ASP.NET or Windows Forms programming, you have probably used user controls in the past.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
117 Controls vs. User Controls The Control class is the base class for the Button, the TextBox, and many other Silverlight controls. User controls don’t provide all of the power of a Control, such as control templates and some other advanced features, but they are easier to implement and are more than adequate for many common scenarios when developing a Silverlight application. If you find that you need to convert your user control into a full control later, it’s not that hard so don’t feel like you’re stuck with your initial decision if you decide to go with a user control.
Let’s create a user control for our menu. A user control consists of a XAML file and a corresponding code behind file. In C#, this code file will have a .xaml.cs extension and for VB.NET it’s .xaml.vb. To add a new user control, select to add a new item to the root of your Silverlight project (not the web project) of type Silverlight User Control and name it. We’ll call ours FishEyeMenu.xaml.
The
FishEyeMenu.xaml file should look like this:
HEY ISN’T MAINPAGE.XAML A USER CONTROL?
You’re right, MainPage.xaml is a user control. Code is generated in App.xaml.cs to set
MainPage.xaml as the root visual of the application but there isn’t anything else special about it. You’ll see that the XAML for our new control looks very similar to the default MainPage.xaml file, except the
x:Class tag is set to MySite.FishEyeMenu instead of MySite.MainPage. The x:Class tag is a hint to the compiler to generate a partial class file for this control and what to name the class in the generated file. Remove the background color from the Grid because we won’t be needing it.
HOW DO WE ADD AN INSTANCE OF THIS USER CONTROL TO OUR APPLICATION?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
118 If you open MainPage.xaml and look in the Toolbox under MySite Controls you should see our new control.
Figure 5.6 As we add user controls to the project they show up automatically in the Toolbox. Select the FishEyeMenu control and draw it on the design surface. Reset the width, height, alignment, margins, etc. It is probably easier just to go into the XAML and remove all of its properties. Set the VerticalAlignment property to Bottom because our menu will be at the bottom of the page. When you added this control it also added the proper namespace definition to the XAML so that the parent control knows where to find it. xmlns:my="clr-namespace:MySite" This says that any element prefixed by my: will be found in the MySite namespace which is the root namespace of our application. This namespace declaration is similar to an ASP.NET Register tag. The other namespace declarations in MainPage.xaml are for core XAML features as well as the navigation and URI mapping elements. If you run the application at this point you won’t see anything yet because the FishEyeMenu user control is currently empty. Let’s give it some content. Silverlight provides a very useful control when you want to create a list of something, and that control is the ItemsControl. An ItemsControl simply displays a collection of data. It is similar to a DataList or Repeater in ASP.NET and contains properties that control how that data is displayed. Let’s add an ItemsControl element to the
LayoutRoot of our FishEyeMenu.xaml.
Now that we have our ItemsControl we need to display some data in it. This data will come from data binding to a list of items, let’s see how we can make this happen.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
119
5.3 Models and Views
As we learned in Chapter 3, data binding is essential to harnessing the full power of Silverlight. Historically, data binding has meant taking database tables or views and binding them directly to a control. In this more modern take on data binding, we are binding our view controls to a model object. The model exposes data to the view as public properties. Data binding consumes these properties and uses the values stored in these properties to display information. By using data binding the “glue” code that is needed between the model and view is reduced, or ideally eliminated.
HMM MODELS WITHOUT GLUE, WE ALWAYS USED CEMENT.
When using models and views, the model contains the data and any business logic. The key to a proper model is that it has no concept of how the data is being displayed. It is the view’s job to take this data and merge it into the display. There should be no business logic in the view, and any code in the view should be kept to a minimum and only be display related. If done correctly, the view should be able to be completely swapped out with a different view without changing the model at all.
MVVM, MVC, MVP, OMG The alphabet soup associated with model-view design patterns can be dizzying. Design patterns that include models and views have been around for a long time, but have been gaining a lot of ground recently because many successful modern web sites are built using a Model View Controller (MVC) design and it’s been found that development methodologies like Test Driven Development (TDD) work better with this type of architecture. Getting into a discussion of model and view based architectures is well beyond the scope of this book, but if you want to do more research, the most popular design pattern right now for Silverlight development is the Model View ViewModel (MVVM) pattern. In MVVM the model data is used to
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
120 populate a view model which massages this data to get it into a form that’s easier for the view to consume using data binding. We’ll just be covering the concepts at a high level but it should help you have a basic knowledge for when you dig deeper on your own. The most important concept is to separate your view logic from your business logic, so as long as you do this you’re well on your way.
First we’re going to need a model class. Add a new item to the MySite project of type Class and call it MenuModel. This class would be considered a view model in the MVVM pattern. In order to display a list of items the view model should contain a list of objects. Each of these objects maps to a single item in the view. The objects on our model will be of type MenuNode so create another class called
MenuNode. Since MenuModel will be holding a list of MenuNode objects, it makes sense to have it inherit from List. Anything that implements IEnumerable will work just as well, so choose the collection type that best suits your needs. using System.Collections.Generic; namespace MySite { public class MenuModel : List { } } The MenuNode class is the model for the menu items, and there is some data we need for each menu node. These are a caption, a command, and an icon. In order to data bind to a value in Silverlight, it needs to be a public property. So let’s add these properties to the MenuNode class. public class MenuNode { public string Caption { get; set; } public string Command { get; set; } public string Icon { get; set; } public MenuNode(string pageName) { Caption = pageName; Command = "/" + pageName; Icon = "images/" + pageName + ".png"; } }
WHAT’S GOING ON WITH THAT { GET; SET; } STUFF?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
121 If you’re not familiar with this relatively new syntax (introduced with C# 3.0), properties can have default get and set methods which set a private field under the covers but you no longer have to write this code yourself. We also added a constructor that accepts the page name and initializes the other properties. Now in the MenuModel constructor, let’s create a few objects so that we have something to bind to. public MenuModel() { this.Add(new MenuNode("Blog")); this.Add(new MenuNode("Photos")); this.Add(new MenuNode("Videos")); this.Add(new MenuNode("About")); } A MenuModel object will be the data context for our fish eye menu. Let’s set the data context in the root of the FishEyeMenu.xaml. First we’ll create a resource instance of the model. This goes just after the opening tag. You need to do this by hand editing the XAML, there’s no way currently to do it in Visual Studio. You’ll also need the my: namespace definition: xmlns:my="clr-namespace:MySite" Now to set the data context. Select the LayoutRoot Grid control and edit its DataContext property in the Properties window. Select Apply Resource… and choose MenuViewModel from the popup window.
Figure 5.7 When setting the DataContext on an element you can choose the resource to use. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
122 The XAML that is generated by setting this property looks like this: Now let’s see how we can bind this model to our view.
Binding to the ItemsControl The most common usage of an ItemsControl is to bind some data to it and customize how the items appear by using templates. First let’s tell the ItemsControl to expect its data to come from data binding. In the context menu for the ItemsSource property, select Apply Data Binding… and choose a Source of DataContext – MenuModel and a Path of . This means that the ItemsSource will be data bound to the root of the MenuModel which is a list of MenuNode objects. The XAML for this binding looks like this: Now in your FishEyeMenu control you’ll see four pieces of text.
Figure 5.7 This is the default format for non-visual data bound elements.
THOSE DON’T LOOK LIKE OUR DATA.
You’re right, the only thing that matches our data is that there are four items in our collection and there are four elements here. For data bound objects that don’t inherit from FrameworkElement the default behavior is to display the result of the ToString method. This is why you see the not very useful results above.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
123 DataContext of items in a data bound ItemsControl This may or may not be obvious, but it’s important to point out since it has some broad reaching implications that can help you to create better data bound applications. The data context of each of the items in an ItemsControl is the corresponding list element in the list that is bound to it. In our case, the data context of an item in the ItemsControl is a single MenuNode from the
MenuModel’s list. To get some more meaningful results we can override the ToString method of the MenuNode class like this: public override string ToString() { return Caption; } With this change the fish eye menu now looks like this:
Figure 5.8 Overriding ToString makes the data easier to visualize.
5.4 Control templates
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
124 Now that we have wired up our data binding properly we can go ahead and make it actually look like the fish eye menu. First we’ll make it behave like a menu and then we’ll make it look the way we want. We can do this by using templates. Let’s switch over to Blend because it has better support for working with templates. Right click on FishEyeMenu.xaml and select the Open in Expression Blend… option from the context menu. The first thing we will need to do change how the items are arranged. There is a template on the ItemsControl we can use to change the layout panel that is used to display the items. By default items are displayed in a vertical stack panel. For the fish eye menu, menu items should be displayed centered along the bottom of the window. This can be done with a horizontal stack panel with the proper alignments. To specify a different layout panel for the items control to use, right click on the ItemsControl and select Edit Additional Templates->Edit Layout of Items (ItemsPanel)->Create Empty… In the dialog select to define it in this document and choose ItemsControl: in the dropdown list. You will now be editing the items panel template. On the StackPanel, choose an orientation of
Horizontal, vertical alignment Bottom, a horizontal alignment Center, and a bottom margin of 20. This results in the following XAML. After these changes the application will look like this.
Figure 5.9 The menu items arranged across the bottom using a custom ItemsPanel template. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
125 Next we can start making the menu items into actual navigation buttons. We can specify how the items look by creating an item template. Go back to the context menu where we chose the ItemsPanel template. This time choose the ItemTemplate menu item and then Create Empty… and in the dialog choose to create the template in the ItemsControl.
WHERE IS THE BEST PLACE TO CREATE MY TEMPLATES? You can choose to create these templates anywhere you want, and it really depends on whether you’re going to use them again in other parts of your application. Since these templates will only be used in this particular ItemsControl we can create them there. If you create the template in the user control any other ItemsControl in this user control could use it. If you create it at the application level it gets put in the App.xaml and you could use it anywhere in the application. We now have a new
DataTemplate resource and an item template definition in the ItemsControl: We can now give the items in the ItemsControl a custom look and feel. We want the menu items to act like buttons. The HyperlinkButton is a good choice since we can use its NavigateUri property to switch pages. Add a HyperlinkButton to the Grid in the item template and reset all of its properties. We want to bind the Content property to the Caption property of the MenuNode. We can do this by choosing Data Binding… from the Content context menu. In the dialog, use a custom path of Caption. For the NavigateUri property, data bind to a custom path of Command. The XAML for the
HyperlinkButton now looks like this: Now if you run the application the buttons should actually work and navigate to the different pages. This is good progress, but it still doesn’t look like a fish eye menu. To do this, we need to “skin” the
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
126 HyperlinkButton. Skinning is when you completely change how a control looks but it still retains its behaviors and properties. To make things more manageable and not keep putting template inside other templates let’s separate out the HyperlinkButton into its own UserControl. This is easy to do in Blend. Right click on the HyperlinkButton, select Make Into UserControl…, and name it FishEyeMenuItem. Separating this out actually causes a bit of a problem but it is also an opportunity to demonstrate a valuable technique.
Design time data contexts When you are designing the FishEyeMenuItem control it no longer knows what its data context is so you don’t see anything while you’re editing. We could define a data context in this user control but then it won’t be able to inherit its data context from the ItemsControl. What we really need is a design time data context. Since this user control will be bound to a MenuNode let’s update the MenuNode class to generate some sample data. We can do this by creating a default constructor. public MenuNode() { if (System.ComponentModel.DesignerProperties.IsInDesignTool) { Caption = "Blog"; Command = "/Blog"; Icon = "images/blog.png"; } } The IsInDesignTool flag will be true in the Visual Studio designer and in Blend. When the
MenuNode constructor detects that the object is being created in design mode we will generate some sample data. In the FishEyeMenuItem.xaml we can create a data context specifically for design mode as follows: You’ll also need the my: namespace declaration like in the FishEyeMenu control. xmlns:my="clr-namespace:MySite"
WHY IS IT D:DATACONTEXT AND NOT JUST DATACONTEXT? The d: namespace tells Silverlight to ignore this DataContext property when compiling the application so it won’t interfere with getting its data from the ItemsControl later and it will only be
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
127 used in design mode. Now the content of the HyperlinkButton should say Blog like in our sample data.
DONE WITH CODE For the rest of this chapter, we’ll work on changing this menu to look how we want it to. This is also the point at which you could hand over the menu to a designer to customize the look and feel. It is important to note that only XAML will change for the rest of this chapter, no code. This highlights the power of implementing an application using models and views.
Control skinning To
skin a control you give
it a template
supplying
your
new
visual elements. The
new
HyperlinkButton will have a text caption and an icon. First let’s add the icons to the application. Here are the icons we will use, but you can create your own. These images were created by Diane Leeper and you can see more of her work at dianeleeper.com.
Figure 5.10 Four images to use as icons in the menu. To keep things organized in the project, create a folder named images and add your icons to by right clicking on the folder and selecting Add->Existing Item. The image file names should match the names that were specified in MenuModel object. Let’s go back to the HyperlinkButton’s context menu one more time. Select Edit Template>Create Empty… and take the defaults. Now we can start creating our visuals for the control skin. Let’s give the LayoutRoot grid a width of 82 and a height of 90. Also create two rows, the first of height Auto and the second of height *. After these changes the XAML for the grid looks like this: Now we can add some content to this grid. Add a TextBlock to the first row and an Image to the second row and set their properties as follows: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
128 Notice the data bindings. The Text property of the text block is bound to the Caption property and the Source of the image is bound to the Icon property. The template will now look like this:
Figure 5.9 Our button template on the Blend design surface. Run the application again and see how it behaves.
Figure 5.10 We now have four menu items with an image and text at the bottom of the page.
I CAN STILL CLICK ON THE MENU ITEMS AND GO TO THE RIGHT PAGE! That’s right, these are still HyperlinkButtons even though they look completely different and since this skinning didn’t affect the data binding for the NavigateUri property they still behave exactly the same. We want these icons to look like they’re floating over a surface, so let’s add a simple rectangle for this. Just before the ItemsControl in FishEyeMenu.xaml, add the following: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
129
If you run the application again, the menu will now have some “ground” behind it.
Figure 5.11 The rectangle gives the icons a sense of depth. The menu items are looking pretty good, but this is a fish eye menu after all, so there needs to be some movement. This is done through storyboard animations.
5.5 Storyboard animations
In Silverlight, animations are simply a way of changing a property’s value from one value to another over a period of time. These animations are grouped into Storyboards, and these Storyboards can contain one or more animations. By combining multiple animations in a single storyboard, some complex effects can be easily achieved. Let’s say we wanted to move an element on a Canvas from one position to another. We would need two animations, one for the Canvas.Left property, and one for the
Canvas.Top property. These two animations would be grouped into a single Storyboard, and we would call the Storyboard’s Begin method to start the animation.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
130 Animations are not limited to only positioning, and most properties can be animated. pretty much anything that contains a double, a Color, a Point, even an object or an enum like Visibility. This allows you to do things like fade effects, color highlighting, or animating the points of a Polyline element. For our fish eye menu, let’s first make the menu item grow when we hover over it.
Render transforms Render transforms, as the name implies, change how something is rendered or drawn. Render transforms can be applied to any element including layout panels or other elements that have children. The render transform is applied to the element itself and all children of that element. They are especially useful for animations and will help us shrink and grow the menu items. Here are the most common render transforms:
Figure 5.15 Four render transforms and how they affect a sample rectangle The render transform that we are interested in for this animation is the ScaleTransform.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
131 IS THE SCALE VALUE A PERCENTAGE LIKE 0 TO 100?
Not exactly but you’re close. A scale of 1 will display the element at its original size. If we start with a scale of .75 and end with a scale of 1, it will make the menu item appear to grow. So first, let’s add a
ScaleTransform to our Grid in the HyperlinkButton’s control template.
BLEND AND TRANSITIONS, STATES, AND ANIMATIONS Up to this point we have used Blend as a learning tool. Blend has great support for animations and states and you can learn a lot from playing around with them in Blend but it also generates some pretty verbose output which would just get in the way of learning for the rest of this chapter and instead we will be working in the raw XAML.
We want the grid in our control template to start at 75 percent of its actual size and we can do this by setting the ScaleX and ScaleY properties of its ScaleTransform to .75. The RenderTransformOrigin property tells Silverlight what to use as its center point when doing transforms. By setting it to 0.5,1 the bottom center will be the center point. This will make the menu items grow upwards from the bottom. Now if you run the application you’ll see some smaller menu items.
Figure 5.17 Scaling of the menu items relative to the bottom center. Now
we
need
to
create
the
animation
for
the
ScaleX and ScaleY property of the
ScaleTransform. Controls have a special way of making it easier to run animations when transitioning between states known as the Visual State Manager or VSM. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
132
Using the Visual State Manager Controls in Silverlight typically have a number of states which allow you to provide visual cues to the user when things happen, for example when the mouse hovers over the control or when the mouse is pressed and released. The Visual State Manager makes it easier to create these visual cues when a control’s state changes. When skinning a control you have the opportunity of providing animations for any state that a control has but you’re not required to provide all of them. A HyperlinkButton control has six states. These are Normal, Pressed, MouseOver, Disabled, Focused, and Unfocused. The states that we are interested in for our skinned hyperlink buttons are the Normal and MouseOver states.
I DON’T THINK I UNDERSTAND, HOW DO VISUAL STATES WORK?
With visual states, you specify what animations need to happen when a control is moving to another state. For example when you move the cursor over a button the MouseOver state’s animations are run and these animations may color the button to indicate that the mouse cursor is over the button. To use VSM in XAML, we first need to add a namespace declaration which is, by convention, named vsm. Add the following to FishEyeMenu.xaml: xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" Now let’s add the XAML for the MouseOver state. Add the following inside the Grid element in the
HyperlinkButton’s ControlTemplate. #1 ScaleX is of type double #2 Target element x:Name is Scale #3 Target property is ScaleX
#1 #2 #3 #4 #5
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
133 #4 Animate for .3 seconds #5 Animate to a value of 1
Cueballs in code and text Since the ScaleX and ScaleY properties are of type double, we use a DoubleAnimation to animate their values (#1). We need to provide the name of the element we are animating; in this case we previously gave the ScaleTransform a name of Scale (#2). The properties that we are animating are the ScaleX and ScaleY properties (#3), and we want the animation to last for .3 seconds (#4). Finally when the animation is complete ScaleX and ScaleY will both have a value of 1 (#5). The HyperlinkButton control knows to look for this visual state and if it exists will automatically execute it when the cursor is hovering over it. Run the program again and as you move the cursor over a menu item it should grow.
SO WHEN WE TRANSITION TO THE NORMAL STATE IT GETS SMALLER AGAIN? That’s right, for the Normal state we need to provide animations that shrink the button down again. To do this we just need to add another VisualState. The only differences are the name of the visual state and the To property of the animations. Now the items will grow and shrink when the mouse moves over them.
Figure 5.18 The menu items now respond when you hover over them and move off. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
134 Along with animating the scale of the items, there are a couple of other animations we can add to improve the visual effect. First of all, as the items shrink and grow, they should “push” the other items to make room.
SHOULDN’T THE SCALED DOWN MENU ITEMS TAKE UP LESS SPACE AUTOMATICALLY?
You might think this would be the case. Applying a transform to an element doesn’t change its width and height when the layout panel measures and arranges its elements, so in order to achieve the pushing effect we can animate the width of the grid in the control template. First let’s change the width of the grid so that it starts at the smaller width since the initial state of the hyperlink button is the
Normal or smaller state. Then in the MouseOver state’s Storyboard, add the following animation: Similarly for the Normal state, add the reverse animation: Another thing we want to do is fade in the caption when the cursor is hovering over an item. We can do this with the TextBlock’s Opacity property. By default we want it to have a value of 0, or completely transparent. The opacity needs to animate between 0 and 1 so one more animation in each of the states will do the trick. Here is the animation for the MouseOver state: And again the reverse for the Normal state: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
135 Now if you run the application, you should see a nice mouse over effect for the menu items, and when you click on an item you can see that it still acts like a hyperlink button.
Figure 5.19 Menu with MouseOver and Normal state animations Pretty cool huh? Not bad for a bit of XAML without any changes to the underlying code. With a little more XAML we can give the animations a more interesting look. This can be done through something known as animation easing.
Applying easing to animations It’s great that it’s so easy to animate a property from one value to another but how you get there that makes a huge difference. Even if it’s a subtle transition where the value changes slowly then speeds up in the middle and slows down again at the end the user’s mind registers this subconsciously as a much more pleasing effect than a linear motion.
LIKE THE TWEENER LIBRARY IN FLASH?
That’s right but in Silverlight you don’t need a separate library since these transitions are built in and are called easing function. An easing function can be applied to any animation and there are a few easing functions to choose from. They can really take your animations to the next level and improve the user experience. The easing function that we’re interested in is one where the animation goes past the target value and bounces back and we’ll apply this to the scale animations on the MouseOver state.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
136 The only thing that has changed is that we’re now setting the EasingFunction property of the
DoubleAnimation to an ElasticEase function. The EasingMode property can be set on any easing function and specifies whether to apply the easing function to the start of the animation, end of the animation, or both. EaseOut means to only apply it to the end of the animation. Many of the easing functions have additional properties to modify their behavior, such as number of bounces, size of the bounces, or anything else that can vary how the function behaves. Since we want a single bounce, the Oscillations property is set to 1, and the Springiness property controls how much to overshoot and bounce back. If you run the program again you’ll see how this little change has made a significant difference in the feel of the menu. There’s one last thing we need to do to polish off the menu, and that’s to add a reflection to the menu items.
The menu items are not vampires A popular Web 2.0 technique these days is to give elements a reflection.
SO FOR A REFLECTION WE NEED TO DRAW A MIRROR IMAGE OF SOMETHING, HOW DO WE DO THAT? This is actually pretty easy in Silverlight using a combination of some of the techniques we’ve used so far along with a new one. First add another copy of the element, in our case this is the image, and set its scale to a negative number. Since we’re reflecting vertically, the ScaleY is the one we’ll want to set. The menu item will now have two Image elements in it, this is the new one: The reflection image definition is very similar to the original image except it has a ScaleTransform and a RenderTransformOrigin defined. Since its Source is also bound to the Icon property, it gets the same value as the other image.
Figure 5.20 Menu items with reflection You’ll notice as you hover over a menu item that its reflection scales as well. This is because the scale transform is on the grid that contains both images. The scale on the reflection is combined with the scale on the grid to show a reflected scaled image. Reflections are typically displayed with a gradient fade from partially transparent to completely transparent. This effect can be created by using an opacity mask. With an opacity mask the alpha value of the color of the mask at any given pixel is used to determine how much transparency to use when displaying that pixel. All color information in the opacity mask is ignored. Opacity masks can use any brush to provide the alpha values but in our case it makes sense to use a linear gradient. The first GradientStop is at an offset of 1, which would typically be the bottom, but since the reflection is drawn flipped it is actually the top. The alpha value of 80 hex (128 decimal) corresponds to ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
138 50 percent opacity. The second gradient stop at .5 has an alpha value of 0 which is completely transparent. So half of the image will be displayed with opacity ranging from 50 percent to zero and the rest of the image will be completely transparent.
Figure 5.16 The fish eye menu with fading reflections That’s it. We’re done with the fish eye menu and it looks really nice. Now let’s look back at what we have learned.
5.6 Summary
Any application of significant size is probably going to have multiple applications that you need to transition between. The Silverlight Navigation Application wizard creates a project that contains navigation controls and some basic structure for creating this type of application. These navigation controls allow you to bookmark a page in the application and also allows the forward and back browser buttons to behave properly when navigating inside the Silverlight application. No matter how cool your Silverlight application looks it’s not going to be that interesting unless it can display data dynamically. We covered some of the reasons for separating the data layer from the presentation layer and explored how to implement a model and view design pattern. Using data binding is the key to models and views in Silverlight. By using data binding, as long as the data structure and ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
139 properties of the model remains consistent, either side can change its internal structure and logic without impacting the other. We learned how an ItemsControl can display a list of data and how to use templates in the ItemsControl to describe how the data will be displayed. Finally, we saw how we can completely change the look and feel of a control using skinning and the Visual State Manager and how animations can provide a great user experience. Since a skinned control still has the same properties, methods, and events the code associated with the control doesn’t need to change even if it looks completely different. Go back through the code behind files and notice how little code we really had to write to create this menu. Besides the code in the model classes we didn’t add any code to the application. This is a key point for best practices in Silverlight. The less code you write, the less you have to test and maintain. In the next chapter we’ll see how we can implement the photos page of this application which displays a photo gallery and allows you to upload photos to the web server.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
140
6 WCF RIA Services to the Rescue In previous chapters we’ve learned about data binding and communicating with the server via web services and these are very powerful techniques you can use to build sophisticated applications. If all you’re doing is retrieving data for display only, it may be easy enough to create a few web service methods or an HTTP handler that returns the data as XML to suit your needs. But let’s say you also want to be able to modify the data or create new data and push these changes back up to the server. Now you’ll need three to four times as many methods on your web service to do everything. If you enjoy writing a bunch of web service methods then this chapter isn’t for you.
HOW ABOUT VALIDATION? I ALWAYS HAVE TO WRITE THAT TWICE FOR CLIENT AND SERVER.
WCF RIA Services also helps with this. You specify your validation rules and custom validation logic once and have access to it in both places. These are some of the issues that RIA Services was created to solve. The sample code for this chapter will build out the Photos page of the application that we started in chapter 5.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
141
Figure 6.1 The completed photo gallery built using RIA Services for communication. The photos page will show a list of images and when you click on an image you’ll see a larger version of it. There is also the ability to upload photos and store them as files on the server. An entire book could be written on WCF RIA Services and this chapter is not meant to be a complete guide but it should give you a good idea about whether to explore it further.
6.1 RIA Services Essentials
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
142 WCF RIA Services is a free offering from Microsoft that sits on top of Silverlight and ASP.NET and makes your multi-tier application feel like a traditional client-server application. The term multi-tier refers to the database tier, any object tier that makes it easier to access the database, a business logic tier on the server and another on the client, and a display logic tier. In more complex applications there may be even more tiers in the mix. The goal of WCF RIA Services is to allow your Silverlight code to call methods and deal with data as if it is local to the Silverlight application without worrying about all that glue code to pass the data over web services and marshal it on both sides. You can download WCF RIA Services (we’ll call them RIA Services for short) via the Get Started page at silverlight.net/getstarted. Be sure to choose the correct version based on the version of Silverlight that you are developing for. Once RIA Services are installed, there are a couple of ways to enable it for your project. The first is to choose to enable in the Silverlight Application wizard.
Figure 6.2 When you have RIA Services installed, you get a checkbox where you can turn it on in the application wizard.
NOTE: This image will change with the next release of VS2010 and should be updated before printing. BUT WE HAVE AN EXISTING PROJECT, DO I HAVE TO CREATE A NEW ONE AND START OVER?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
143 Well you certainly could, and add all of your existing project files, but you can also enable RIA Services through the Silverlight application’s property pages. Scroll to the bottom of the Silverlight tab and if you have RIA Services installed you should see a dropdown list related to RIA Services.
Figure 6.3 To enable RIA Services you need to choose an ASP.NET project to link to. This allows the client side code to be generated.
NOTE: This image will change with the next release of VS2010 and should be updated before printing Choose the MySite.Web project to link to. This web project will contain our server side code that we want to communicate with. You will also get some new assembly references added to your Silverlight project for you that RIA Services needs.
WHERE WILL THE DATA COME FROM?
Let’s start by adding a database to our web project. To keep things simple we’ll create a SQL Express database by choosing to add a new item to the web project of type SQL Database.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
144
Figure 6.4 Adding a SQL Server Express database to the web project. For this sample let’s use the name Database1.mdf. When you add the database, you will be prompted to put the database file in the App.Data folder. Say yes to this. Once the database is created, create a new table. Give it a primary key called Id of type int, and three nvarchar(255) columns called Title, Url, and ThumbUrl. Make sure that the Id column has its Identity Specification set to Yes.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
145
Figure 6.5 This table will hold our photo data.
HAVING AN INTEGER PRIMARY KEY HELPS THE ENTITY FRAMEWORK GENERATE OBJECTS.
Save this table and name it Photos. Now we need a data access layer (this is one of those tiers we were talking about earlier). The ones currently supported by RIA Services are the Entity Framework and LINQ to SQL. For this sample we’ll use the Entity Framework so add a new item to the web project of type ADO.NET Entity Data Model. You can take the default name of Model1.edmx. In the Entity Data Model wizard, choose to generate your model from a database, and for the database choose
Database1.mdf. For everything else you can take the defaults until you get to the Choose Your Database Objects page. On this page, select the Photos table.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
146
Figure 6.6 Choose to include the Photo table in the model, and make sure that the pluralizae or singularize checkbox is selected.
WHAT DOES THE PLURALIZE OR SINGULARIZE CHECKBOX DO?
By selecting this checkbox, objects in the Photos table will be of type Photo. If you don’t select this, the objects will be of type Photos which can be confusing because each object contains information for a single photo. Go ahead and click Finish, and the entity model will be generated for you. You should then see a graphical representation of your entity model, in this case it is a single entity called Photo.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
147
Figure 6.7 The Photo entity is generated from our table definition and the property names match the column names.
SO FAR THIS IS WHAT YOU WOULD DO TO USE THE ENTITY FRAMEWORK IN YOUR ASP.NET APPLICATION.
That’s right, up until this point there has been nothing RIA Services specific and if you’ve used the Entity Framework it should look very familiar. To get started with the RIA Services piece, first build the solution so that RIA Services can find the entity model. Then we need to generate a domain service off of our data layer.
Domain services Domain services are the key to the magic of RIA Services. By creating a domain service in the web project, the corresponding code is generated on the client side to access the domain service as if it is a local data store. No web service configuration, connection strings, or other plumbing, this is all provided for you. Let’s add a new Domain Service Class to our web project and name it PhotoService.cs.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
148
Figure 6.8 Adding a new Domain Service Class to the web project. You’ll then get a dialog to choose which data layer to associate the domain service with. The entity model should be listed as one of the choices and when selected you should see the Photo entity displayed. Select its check box and also make sure to select to enable editing.
Figure 6.9 New Domain Service wizard, it’s nice to have all of these wizards to do things for us.
WHAT HAPPENS IF I DON’T CHOOSE TO CHOOSE TO ENABLE EDITING? ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
149 If you don’t enable editing, you’ll only get query methods for your objects. You can choose this if your Silverlight application doesn’t need to modify the data. If you don’t want the users to edit the data then it is more secure to not expose these methods. The PhotoService class is generated with the following methods: [EnableClientAccess()] public class PhotoService : LinqToEntitiesDomainService { public IQueryable GetPhotos(); public void InsertPhoto(Photo photo); public void UpdatePhoto(Photo currentPhoto); public void DeletePhoto(Photo photo); } A proxy class with these same methods is generated in the Silverlight application so that you can call these methods directly, but the easiest way to interface with this class is to use it in conjunction with a domain data source.
Domain data sources We want to display our photo data on the photos page of our application so open Photos.xaml and remove the TextBlock placeholder we put there since we’ll be putting actual data here now. In Chapter 5 we defined a data source as a resource in our XAML and used that as our data context. For RIA Services you do something similar, but it is a special RIA Services domain data source that knows how to call the appropriate methods on the domain service to retrieve the data. You can get some help with this by going to the Data menu and selecting Show Data Sources. If your data source doesn’t show up in this view, build the application so that the client side proxies get generated. With the
Photos.xaml page in the design pane the data sources pane should look something like this:
Figure 6.10 The Data Sources pane has an entry for our domain service. We can customize what control to use to display the list and each data element. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
150 For now we’ll go with the defaults for control types. The default control to use to display the list is a
DataGrid. The DataGrid is a very useful control because it can dynamically generate columns based on your data and you can quickly create editable or read only pages.
SOUND LIKE AN EASY WAY TO CREATE AN ADMIN PAGE. That’s right, if you want a quick way to edit table data it can be as easy as dragging your data source to the page. Select the Photo node in the Data Sources pane and drag it to the design surface. You should automatically get a DataGrid created for you. Reset its width, height, alignment, and margins so that it fills the page. The photos page should now look like this:
Figure 6.11 The DataGrid picks up the column names and bindings from the data source definition. If you look in the XAML you’ll see that here were a couple of things added for us. The first is a domain data source definition: This definition had its DomainContext property set to PhotoContext which is the class generated on the client to communicate with our domain data service. The other thing that was added was the
DataGrid itself: Notice that the ItemsSource of the DataGrid is set to our domain data source. The DataGrid columns are also generated for us based on the properties of the Photo object.
I RAN THE PROGRAM AND DIDN’T SEE ANY DATA.
That’s because we don’t have any data yet. Let’s create some test data. First we need an image to display. In the MySite.Web project, create a new folder called Photos and add an image to it called
placeholder.jpg. It can be whatever image you want, we’ll use this one:
Figure 6.12 Placeholder.jpg is a sample image we can use for testing until we upload some real photos. Then we need to add a row to the Photo table. Make the title placeholder.jpg and set the URL and thumbnail URL to /photos/placeholder.jpg. Now if you run the application you’ll see one entry in the data grid.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
152
Figure 6.13 The DataGrid displaying our one row of sample data from the database.
WOW WE HAVE LIVE DATA AND HAVEN’T WRITTEN A SINGLE LINE OF CODE YET. That’s right, we’re now communication with the database and RIA Services has done most of the work for us. Instead of displaying our data in rows and columns we want our photos to be displayed in a thumbnail view that wraps as it hits the end of the page. Let’s see how we can customize the view for our photo data.
6.2 Customizing the photo viewer
The DataGrid isn’t the best choice for this so let’s remove the DataGrid and replace it with a ListBox. You can use the same ItemsSource definition that the DataGrid had. Let’s also add an item template that displays the thumbnail. Now if you run this you will see a single item in our list but the thumbnail isn’t displayed. This is because we have to do a little extra work with the thumbnail URL. To display the thumbnail from our web server we need to use an absolute or full URL instead of the relative URL we’re storing in the database. We could put an absolute URL in the database but then if we wanted to move the application or deploy it to the server we would have to update all of the rows in the database.
I REMEMBER THIS. WE CAN USE A VALUE CONVERTER. That’s right, if the data isn’t exactly the way you want it to be for data binding you can modify it with a value converter. This value converter will take a relative path and append the host information to the front of the URL to make it a full URL. Create a new class called FullUrlConverter in the root of the Silverlight project. This is the code for the class: using System; using System.Windows; using System.Windows.Data; namespace MySite { public class FullUrlConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string input = value.ToString(); if (input.Length > 0 && input[0] != '/') { input = "/" + input; } Uri sourceUri = Application.Current.Host.Source; string fullUrl = string.Format("http://{0}:{1}{2}", sourceUri.Host, sourceUri.Port, input); return fullUrl; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
154 } } } We don’t need to implement the ConvertBack method since we won’t be using this converter with two way data binding. Now to use this value converter, select the Image element and in the properties pane choose to apply data binding on the Source property. We are already bound to ThumbUrl so we can leave that as-is, but under the Converter section select MySite and FullUrlConverter. Then since we don’t have a resource defined for it yet select Create New… and take the defaults.
Figure 6.14 Selecting a value converter for our data binding from the properties pane. In the XAML a new resource is added to the page: And this new resource is used as the value converter for the data binding. Source="{Binding Path=ThumbUrl, Converter={StaticResource FullUrlConverter1}}" And now if you run the application you should see the placeholder image.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
155
Figure 6.15 With the addition of the value converter we now see our thumbnail image.
HOW ABOUT IF WE WANT TO UPLOAD OUR OWN PHOTOS?
If you remember the domain service class that was generated, it had a method to insert a photo entry into the database. Since only the URLs to the photos and thumbnails are stored in the database and the actual images are stored in a folder on the server this method only gets us so far. We need a way to send the actual image data to the server as well. The easiest way to fix this is to add a new method to the domain service.
Customizing the domain service The domain service that’s generated through the New Domain Service wizard is only a starting point. You can customize it to add data validation or more methods that you want to make available to your Silverlight application. In this case we want to be able to upload the binary data for a photo, generate a thumbnail version of it, and save the original photo and its thumbnail to a folder on the web server. The contents of this method are not overly important in the context of RIA Services and Silverlight since it is standard ASP.NET server side code, but is included here for completeness. Add the following method to the PhotoService class in the PhotoAlbum.Web project: public void UploadPhotoData(byte[] data) { Guid guid = Guid.NewGuid(); string guidString = guid.ToString(); string photoPath = "/photos/" + guidString + ".jpg"; string thumbPath = "/photos/" + guidString + "_thumb.jpg"; Bitmap bmp = new Bitmap(new MemoryStream(data)); bmp.Save(HostingEnvironment.MapPath(photoPath), ImageFormat.Jpeg); Size sz = bmp.Size; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
156 double scale = sz.Width / 100d; if (sz.Height > sz.Width) { scale = sz.Height / 100d; } sz.Width = (int)(sz.Width / scale); sz.Height = (int)(sz.Height / scale); Bitmap bmpThumb = new Bitmap(bmp, sz); bmpThumb.Save(HostingEnvironment.MapPath(thumbPath), ImageFormat.Jpeg); Photo p = new Photo(); p.Url = photoPath; p.ThumbUrl = thumbPath; InsertPhoto(p); this.Submit(this.ChangeSet); } You’ll also need the following using statements: using using using using
System.IO; System.Drawing; System.Drawing.Imaging; System.Web.Hosting;
The important part of the above method from a RIA Services standpoint is that the SaveChanges method of the object context needs to be called at the end to commit the changes to the database. The rest of the code generates some file names and resizes the image to make a thumbnail and saves the images as JPG files. Once you rebuild the solution this method will now be ready to use on the client.
IT’S REALLY NICE THAT IT’S ALL AUTOMATIC.
One issue you may run into is that there is a limit on the size of a web request to ASP.NET. This defaults to four megabytes. The image data is encoded to pass it to the server which makes the request larger so you should increase the maximum request size, let’s make it 20 megabytes (you may need to adjust this based on the size of your photos). This is done in the web project’s web.config file in the
system.web element. Add an httpRuntime element system.web as follows: If you want to upload large images to your server a better option would be to use one of the many free open source Silverlight file upload controls that can be found on CodePlex. Now that the upload method has been added to the domain service we need to implement logic on the client side to use it.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
157
6.3 Uploading Files
The first thing we need to do is have the user specify a photo or photos that they want to upload. Up until this point we’ve accessed data from the XAP file, isolated storage, and from the web but haven’t tried to get data from the user’s local computer. There are some security implications to this, if a program running in the browser could just read or write any files it wanted to on the hard drive then it could read sensitive data without the user’s knowledge or install a virus. Because of these concerns, any access to the local file system must be done through user interaction where the user specifies the files to give access to.
The open file dialog One way to access files for input is to use the OpenFileDialog. This is very similar to the
OpenFileDialog in Windows Forms programming.
There is a corresponding SaveFileDialog for
writing files. Let’s add a button to Photos.xaml to allow us to upload a photo. Let’s also define two rows and columns in the grid: Give the ListBox a RowSpan of 2 so that it takes up the entire left side. Here is the click event handler for the button: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
158
private void uploadButton_Click(object sender, RoutedEventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Image Files (*.jpg, *.jpeg, *.png)|*.jpg;*.jpeg;*.png"; bool? ret = dlg.ShowDialog(); if (ret != null && ret.Value == true) { FileInfo file = dlg.File; using (Stream fileStream = file.OpenRead()) { UploadPhoto(fileStream); } } }
THAT FILTER PROPERTY LOOKS STRANGE.
Yes, it does. Filter strings like this have been used since the early Visual Basic days or before and haven’t changed since. The text before the vertical bar (called a pipe) is what gets displayed in the file dialog for the accepted types and the list after the pipe specifies what files can be accepted. If the
OpenFileDialog.ShowDialog method returns true, a file was selected so the UploadPhoto method should be called. Create the UploadPhoto method as follows: void UploadPhoto(Stream stream) { PhotoContext context = photoDomainDataSource.DomainContext as PhotoContext; byte[] data = new byte[stream.Length]; stream.Read(data, 0, (int)stream.Length); InvokeOperation response = context.UploadPhotoData(data); response.Completed += new EventHandler(response_Completed); } void response_Completed(object sender, EventArgs e) { if (photoDomainDataSource.IsBusy == false) { photoDomainDataSource.Load(); } } The UploadPhotoData method on the context object is asynchronous so we need a handler for the method’s Completed event. When the upload is completed, we refresh the photo album’s data which displays the new photo. Now if you run the application you should be able to upload a photo.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
159
Figure 6.16 Upload button and two photos in the album Another way to get data from the file system into the application is through drag and drop. Let’s use the area below the button as a drop target. First add a border and some text: Then to enable drag and drop you can set the AllowDrop property of the visual element to True. We’ll set this on the Border element. Also on the Border we need to handle the Drop event. private void Border_Drop(object sender, DragEventArgs e) { FileInfo[] files = (FileInfo[])e.Data.GetData(System.Windows.DataFormats.FileDrop); FileInfo file = files[0]; if (file.Extension == ".png" || file.Extension == ".jpg") { UploadPhoto(files[0].OpenRead()); } } Now if you drag a photo from anywhere on your computer, for example from Windows Explorer, and drop it in the green area, the photo will automatically be uploaded to the server.
IS DRAG AND DROP LIMITED TO IMAGE DATA?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
160 No, you can drag any file type and drop it into an area that accepts dropped data. You will need to have logic in your application to deal with this data but it can be pretty much anything. Since it takes a bit of time to upload the photo we should really have some way of indicating that the application is busy. There are a few ways to do this, including changing the mouse cursor and disabling elements but let’s do something a bit more visually impressive and use a child window.
Child windows As we saw in the last chapter with the error window, a child window is a Silverlight control that is displayed over the Silverlight application and the contents of the application are grayed out. This is perfect for us to give a visual indication that the photo upload is in progress. First let’s add a new Silverlight Child Window to the root of the MySite project and name it BusyUploadingWindow.
Figure 6.17 Adding a new Silverlight Child Window to the project The default child window that is generated has an OK and Cancel button and a Close button in the corner, we don’t need any of that for this window since we don’t want to let the user interact with it. Change the BusyUploadingWindow.xaml as follows: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
161 By setting the progress bar to indeterminate, it will just show a horizontally scrolling repeating pattern.
HOW DO WE SHOW THIS BUSY WINDOW?
The busy window can’t just be added to the XAML since it’s not allowed to have a parent element, so we need to add it in code. First in the Photos class, add a field for the child window: BusyUploadingWindow busyWindow = new BusyUploadingWindow(); Then at the start of the UploadPhoto method add a call to busyWindow.Show() to show the child window. void UploadPhoto(Stream stream) { busyWindow.Show(); And
when
the
upload
is
complete,
we
need
to
close
this
window
so
modify
the
response_Completed method as follows: void response_Completed(object sender, EventArgs e) { busyWindow.Close(); if (PhotoDataSource.IsBusy == false) { PhotoDataSource.Load(); } } Run the application again. Now you should see a progress bar pop up in a window when the photo is being uploaded. As an added bonus the child window automatically does a nice expanding animation when it’s displayed.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
162
Figure 6.18 The busy window is displayed when a photo is being uploaded. When the request is complete it is closed. Another use for child windows in this application would be to display details about a photo. Let’s create another child window in the project’s root called PhotoDetails.xaml. Displaying a details view for an object is a very common task and there is a control available to help with this. It’s called the
DataForm.
6.4 The Silverlight Toolkit
The DataForm control is actually part of another free offering from Microsoft, the Silverlight Toolkit. The Silverlight Toolkit arose out of a need for Microsoft to be able to provide extra functionality for Silverlight without being tied to the release cycle of the Silverlight plug-in. It allows users to provide feedback on early releases of controls and other functionality. It contains some very useful layout ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
163 controls and a free chart control that can easily create graphical charts and supports data binding. There is also the ability to apply a theme to your application by skinning all of your controls using one of more than a dozen provided themes or your own custom theme. The Silverlight Toolkit is available at silverlight.codeplex.com. Let’s add a DataForm to the PhotoDetails control. If the Silverlight Toolkit is installed you should see the DataForm control in the Toolbox and you can drag it onto the design surface. Reset all of the size, alignment, and margin properties like it seems we always have to do and then set the following properties: This data form will automatically generate fields for each of the properties for the object specified in the CurrentItem property. Since the current item is set to {Binding}, its value will come from the data context of the data form. Now we need some logic to display this window. Let’s add a
MouseLeftButtonUp event to the inner Border element in the item template in Photos.xaml. And the corresponding method in the code behind: private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { PhotoDetails details = new PhotoDetails(); details.DataContext = (sender as FrameworkElement).DataContext; details.Show(); }
In the mouse button up event, we set the DataContext of the window to the DataContext of the item that was clicked. Go ahead and run the application again and click on an image to see the details window.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
164
Figure 6.19 The PhotoDetails child window with a DataForm
I SEE TEXT BOXES, CAN WE EDIT THESE VALUES?
Not quite yet. We need to do a little more to push these changes back up through RIA Services. First in PhotoDetails.xaml.cs we need to either commit or cancel the changes based on whether OK or Cancel is clicked. Change the button event handlers as follows: private void OKButton_Click(object sender, RoutedEventArgs e) { dataForm.CommitEdit(); this.DialogResult = true; } private void CancelButton_Click(object sender, RoutedEventArgs e) { dataForm.CancelEdit(); this.DialogResult = false; } Then in Photos.xaml.cs we need to handle the Closed event of the details window. When that window is closed, if OK was clicked we’ll submit the changes via RIA Services. private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
165 { PhotoDetails details = new PhotoDetails(); details.DataContext = (sender as FrameworkElement).DataContext; details.Closed += new EventHandler(details_Closed); details.Show(); } void details_Closed(object sender, EventArgs e) { PhotoDetails details = sender as PhotoDetails; if (details.DialogResult != null && details.DialogResult == true) { photoDomainDataSource.SubmitChanges(); } } Now to confirm this is actually saving the information, change the title on one of your photos, click OK, and then refresh the page. You should see that the title reflects the changed value.
DataForm validation The DataForm control does more than creating labels and edit fields and providing commit logic. It also offers data validation by default. The Id field is an integer and so can only accept a numeric value. If you try to edit the Id field and then try to edit another field, you will see an error message displayed. If you specify more than 255 characters in one of the other fields which is the maximum length of these columns in the database you should see an error as well.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
166
Figure 6.20 Entering a string value in a numeric field displays an error, and we also get and error if we put more than the max characters for a string field.
WHERE IS THE CODE FOR THESE VALIDATIONS?
Data type validations and length validations are provided by default and are generated from the database metadata. You can add your custom validations as well, based on ranges of numbers or items in a list. These more advanced validations are outside of the scope of this chapter, but if you search online for validation in RIA Services you’ll find several articles on the topic. The DataForm can get us up and running quickly by generating the fields for us but many times you’ll want to customize these fields.
Customizing DataForm layout There are a couple of problems with our default data form. First of all, we don’t want users changing the URL or thumbnail URL, only the title. It would also be good to display the image instead of the URL of the image. First let’s change the size of the PhotoDetail.xaml ChildWindow element. Setting the width to 480 and the height to 420 will work well. Then change the DataForm element as follows: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
167 You can set up the value converter for the image just like we did before for the thumbnail but this time we want the Url property. Now only the image and the title text box will be displayed.
Figure 6.21 The DataForm now shows the image and nets us edit only the title. Now that we have this details screen, it would be great if we could bookmark it to display a particular photo. Let’s see how we can do this.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
168
Deep linking One of the benefits of the navigation framework in Silverlight is that it enables you to do deep linking.
WHAT’S DEEP LINKING? Deep linking is when you can provide a URL to specific item at something deeper than the page level. Navigating to a Page URL will only get us to the photo list, not to an individual photo. To provide this functionality we need to add a some more logic. First in MainPage.xaml let’s add the new UriMapping element listed in bold. When you have multiple UriMapping elements, the first one that matches is the one that gets used so the order of these mappings is important. Since the last UriMapping will match any anchor our new entry needs to go before that one or it will never get used. This new mapping will take an anchor of
/Photos/{photoId} and map it to Photos.xaml with a query string of photo={photoId}. So if the URL is http://localhost:40227/MySiteTestPage.aspx#/Photos/3 then Photos.xaml will be loaded passing in a query string of photo=3. If you run the application again and pass in this anchor tag, it will just load the photo list since there is no code in Photos.xaml yet to process the query string. In Photos.xaml.cs let’s create a new field to hold the photo ID specified in the query string, if any. int photoId = -1; Then in the OnNavigatedTo method, we can add code to retrieve the query string and set this field. protected override void OnNavigatedTo(NavigationEventArgs e) { var query = NavigationContext.QueryString; if (query.ContainsKey("photo")) { string photo = query["photo"]; photoId = int.Parse(photo); } else ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
169 { photoId = -1; } } Finally when the data source has completed loading its data the LoadedData event fires. We can put code in the event handler for this event to show the photo detail window. private void photoDomainDataSource_LoadedData(object sender, System.Windows.Controls.LoadedDataEventArgs e) { var photos = from p in #2 (PhotoDataSource.DomainContext #2 as PhotoContext).Photos #2 where p.Id == photoId #2 select p; #2 Photo photo = photos.FirstOrDefault(); #2 if (photo != null) { PhotoDetail detail = new PhotoDetail(); #1 detail.DataContext = photo; #1 detail.Closed += new EventHandler(detail_Closed); #1 detail.Show(); #1 } photoId = -1; #3 }
#1 Show the detail window for matching photo #2 Get the photo matching ID specified #3 Clear the ID
Cueballs in code and text The code to actually show the photo detail window is the same as the code we used earlier to display the photo detail when a photo was clicked on (#1). The rest is a bit of LINQ code to query the list of photos and try to find one that matches the ID specified (#2).
OH THERE’S THAT LINQ AGAIN, I BETTER BRUSH UP ON THAT. If there is no match, we’ll just not show the photo detail window. Finally, to avoid this code from running every time the photo list is refreshed, we set the photo ID to -1 so that it won’t match any photos in the list (#3). Now if you run the application specifying a valid photo id, the photo detail child window will be displayed.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
170
A good wrap One last issue we have is that all of the thumbnails are just stacked vertically and this isn’t the most visually appealing or the best use of space. What we really want is for the thumbnails to fill the space horizontally and then wrap to the next line. This isn’t built into any of the panels that ship with Silverlight’s core runtime, but he Silverlight Toolkit has one. This panel is the WrapPanel. The
WrapPanel is like a StackPanel that wraps when it runs out of space. First let’s create a place for our WrapPanel by specifying and ItmsPanel template on the ListBox:
This control is in the System.Windows.Controls.Toolkit assembly. The easiest way to get a reference to it in your page and in the project is to drag a WrapPanel to the Photos.xaml design surface and then delete it. Then we need to override the ItemsPanel template in the ListBox. Add this inside the ListBox element: Then drag a WrapPanel from the Toolbox to that blank line in the XAML. This will create the appropriate references and namespace declarations along with adding the WrapPanel itself. The
ItemsPanel template should now look like this:
I TRIED THIS AND IT DOESN’T WRAP, I GET A HORIZONTAL SCROLLBAR INSTEAD.
By default the ListBox will show a horizontal scrollbar if the data gets too wide to be displayed. This isn’t what we want, instead the thumbnails should wrap if they can’t all fit on one line. To do this we can set the horizontal scrollbar to disabled. Add this property to the ListBox: ScrollViewer.HorizontalScrollBarVisibility="Disabled" Now if you run the application the thumbnails should wrap to the next line if necessary.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
171
Figure 6.22 The photo album now wraps to fill the usable space of the browser window. There you go, now we have a functional photo album with server side storage. Clearly some work could be done to improve the look and feel and you would want to put some security around the ability to upload and edit photos, but this is all possible and not that hard to do.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
172
6.5 Summary
The biggest thing to take away from this chapter is that there are libraries available outside of the Silverlight core runtime and the Silverlight SDK which can be leveraged in your applications to increase your productivity. Some of these, like WCF RIA Services and the Silverlight Toolkit are provided by Microsoft but also see what’s available on the thriving CodePlex open source community as well as other sites. WCF RIA Services make your multi-tier application feel like a simple client-server application and by doing so the amount of code you need to write and consequently maintain is greatly reduced letting you concentrate on adding business and presentation logic instead of spending all of your time creating and maintaining the plumbing. The Silverlight Toolkit is an open source project provided by Microsoft which has a lot of very useful functionality that we briefly touched on here but got some valuable use out of it. We encourage you to explore the Silverlight Toolkit and RIA Services further to discover how they might assist you in your specific application needs. Now that we have learned about various ways to get our data, style our pages, and bind our data to visual elements let’s see how we can take a Silverlight application and make it run like a desktop application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
173
7 Out of Browser Mode Up until this point, we have just been using Silverlight as a plug-in embedded in the browser. This is its primary use and can enhance a web page or allow you to develop an entire site but there are some things you just can’t do when running inside the browser. You also have the browser toolbars and menus taking up space and you can’t start up your application unless you’re currently online. To address these issues and more, Silverlight provides an out of browser mode for your application where it can run in its own window like any other desktop application.
will it work on my macbook?
Absolutely, this is a major benefit of Silverlight Out of Browser applications when compared to a WPF application. To illustrate some of the extra features available when running outside the browser we’ll create a simple feed reader application. Feeds are most commonly associated with blogs and are a popular way of providing a list of posts available from a blog server. Feeds aren’t limited to blogs, however, and a web site can use feeds to notify users of any new information. You can even use feeds to get a list of posts on Twitter. Silverlight has great support for the two most popular feed formats, namely RSS and Atom. There are some things that we need our feed reader application to do that just can’t be done in the browser, and we’ll highlight these features as we get to them. Let’s start with a plain vanilla Silverlight Application and name it FeedReaderSample. By the end of this chapter we’ll have an application that looks like this:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
174
Figure 7.1 The completed FeedReaderSample application
7.1 Enabling out of browser mode
Everything you need to run a Silverlight out of browser application is installed with the plug-in, so anyone who has Silverlight installed will be able to use your application. There are no additional runtimes needed to make this work. Allowing your application to run as an out of browser application really couldn’t be much easier. All you need to do is go to the Silverlight project’s (not the web project) property page and on the Silverlight tab select the checkbox to “Enable running application out of the browser”. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
175
Figure 7.2 It only takes a click in a checkbox to enable out of browser mode. Let’s add a TextBlock to MainPage.xaml and give it some text like “Hello from outside the browser”. It’s not important what text you put here since we’ll be replacing it a bit later. It just gives us something to tell that our page is being displayed. Go ahead and run the application like we have for all of the other samples. You’ll see something like this:
Figure 7.3 Running our out of browser application.
hey this is still running in the browser, what gives?
Yes you’re right it’s still running in the browser. The only thing we have done so far is say that the application is allowed to run outside of the browser. All out of browser Silverlight apps can also run in ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
176 the browser, and need to be installed to run outside of the browser. One way to install an out of browser application is to right click on it.
Figure 7.4 If you right click on a Silverlight application that has out of browser enabled you’ll see the option to install it. When you choose to install the application you’ll be prompted to confirm that you approve that this application will be installed.
Figure 7.5 Click OK to install the application, you can also create shortcuts to launch it. You need to choose at least one place to create a shortcut. If you didn’t there would be no way to launch the application in out of browser mode. Once you do this you should see the application running in its own window. You can close it and use the shortcut to launch it again. Even if you’re not able to connect to the web server you can launch using this shortcut because a cached copy of the application is stored on your computer. Now close the application and then run it from Visual Studio again. You’ll see that it is still being run inside the browser.
what if I want to debug my application in out of browser mode?
Debugging out of browser apps One way to debug an out of browser application is to launch it via the shortcut and then attach to it with Visual Studio. This isn’t ideal because it has a couple of manual time consuming steps and you also won’t be able to easily debug the startup process. Fortunately there is an easier way. Up until now, we have been testing and debugging our apps by setting the web project as the startup project. By default if we set the Silverlight project as the startup project a test page is generated and the application is run in a generated test page in the browser. This behavior is ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
177 configurable and if you go to the Debug tab of the Silverlight project’s property page you can choose to run the installed application.
Figure 7.6 Select the Installed out-of-browser application option to debug in out of browser mode. Once this option is selected, if the Silverlight project is the startup project, the application will be launched in out of browser mode when you debug it. You can still test and debug inside the browser by setting the web project as the startup project. Since the application will work in and out of the browser you can allow it to run either way if you want to. For our application there are too many things that won’t work inside the browser and it doesn’t make sense to provide the ability to run it there. Let’s take a look at what we can do to prevent it from running inside the browser and instead give the user a way to easily install it.
Creating an install experience We can’t really completely disable the application inside the browser because the only way to get it installed is for it to install itself. What we can do, however, is to provide a different user interface for when it is running inside the browser. First let’s add a new user control to the Silverlight project and call it InstallPage.xaml. Add a TextBlock to the page and name it Instructions. Set the text to tell the user to click the “Install Me” button. Add a Button, name it InstallButton and set its Content property to Install Me. We’ll wire up an event in a little bit to install the application, but first let’s get this page to display when in browser. In the Application.xaml.cs file there is an Application_Startup method. In here, the application is told which user control to use as the root visual. This defaults to MainPage. We can detect whether the application is running in the browser and if so we can show the other page. The changes are in bold. private void Application_Startup(object sender, StartupEventArgs e) { if (this.IsRunningOutOfBrowser) { this.RootVisual = new MainPage(); ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
178 } else { this.RootVisual = new InstallPage(); } } That’s all there is to detecting out of browser mode and if you run the application now you’ll see that in the browser it shows the install page:
sweet user interface, you must be a developer
Figure 7.7 We only want this application to run out of browser so when in browser we can display an install page. Clearly you could dress this up and put some nice HTML around it to provide the install experience you want. You could have the Silverlight application only be a button if you wanted and that would work fine. In this case we want to be able to display something else if the application is already installed so let’s see how we can make that work. First let’s go back to InstallPage.xaml and add a Click event handler to the install button. The code for this will look as follows: private void InstallButton_Click(object sender, RoutedEventArgs e) { Application.Current.Install(); } Now if you click the install button it will have the same effect as right clicking on the application and selecting to install it. One problem is that if it’s already installed this will throw an exception. In the constructor for InstallPage we can add some logic to determine whether the application is already installed and if so we can hide the button and change the text. We also want to detect when the install state changes so that once the application is installed we can hide the button. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
179 public InstallPage() { InitializeComponent(); CheckInstallState(); Application.Current.InstallStateChanged += new EventHandler(Current_InstallStateChanged); } void Current_InstallStateChanged(object sender, EventArgs e) { CheckInstallState(); } void CheckInstallState() { if (Application.Current.InstallState == InstallState.Installed) { Instructions.Text = "Application is already installed. Please run by launching via the application shortcut."; InstallButton.Visibility = Visibility.Collapsed; } } Now we have a much better install experience giving the user clear instructions as to how to install and run the application. If your application can also run in browser you can add an install button or something similar to an existing page. Note that the install process must be initiated in response to a user action such as a mouse click and you can’t call the Install method automatically as in a Loaded event handler or a timer.
how can I tell users there is a new version available? For browser based Silverlight applications the latest version is automatically downloaded and executed the next time they hit the site. When running out of browser we need to explicitly check to see if a new version is available. Let’s add some code to the Application_Startup method in
Application.xaml.cs to do this check. if (this.IsRunningOutOfBrowser) { this.RootVisual = new MainPage(); this.CheckAndDownloadUpdateCompleted += new CheckAndDownloadUpdateCompletedEventHandler(Application_CheckAndDownloadUpdateComple ted); this.CheckAndDownloadUpdateAsync(); } ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
180
no complicated update scripts?
That’s right the latest version is downloaded automatically and applied. The update doesn’t take effect until the next time that the application is run so it’s helpful to tell the user to restart in order to run the updated version. We can do this in the CheckAndDownloadUpdateCompleted event handler, like this: void Application_CheckAndDownloadUpdateCompleted(object sender, CheckAndDownloadUpdateCompletedEventArgs e) { if (e.UpdateAvailable) { MessageBox.Show("There is a new version of this application available. Restart the application to run the latest version."); } } Any change to the code will cause a rebuild and this will be recognized as a new version. You can test this by running the application out of browser to get the latest version out there and then make a change and build but instead of running the application from Visual Studio run it from the shortcut on the desktop or in the start menu. You should see a message box like this:
Figure 7.8 When a new version of our application is available the user will see this message box. Now that the install and update process has been sorted out we can start adding the meat of our application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
181
7.2 Trusted mode
When a Silverlight application is running in the browser it is running in what is called “sandboxed” mode. A sandboxed application can’t do anything to permanently harm the computer and cannot access sensitive information. By default an out of browser Silverlight application is also restricted to this same sandbox. There are times when this is too restrictive and more access is required to system resources and the networking stack than is available in sandboxed mode.
i really miss playing in my sandbox
When running out of browser you have the option to run in trusted mode. Trusted mode gives you greater freedom and lets you give the user an experience closer to what they are used to for an installed desktop application. Some of the features unique to trusted mode are:
Cross domain policy files not required on remote server.
Network authentication on web requests – you can provide credentials on requests made with HttpWebRequest or WebClient.
Direct access to local file system - limited to the “My” folders on Windows and the user’s folder on Mac.
Notification API - pop up notification windows (toasts) on the desktop.
Full keyboard access in full screen mode.
COM interoperability (Windows only) - provides the ability to interact with Microsoft Office and other application that provide a COM interface.
The first one of these is critical to our application. Many web servers that provide feeds do not have cross domain policy files in place and therefore cannot be accessed directly by Silverlight when in ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
182 sandboxed mode. There are workarounds for this such as creating a proxy service on your server and requesting the remote data but then there is a server side dependency for the application to run properly.
how do we enable trusted mode?
Back when we selected the checkbox to enable the application to run outside the browser there was also a button next to it which would let us configure out of browser settings.
Figure 7.9 Along with enabling out of browser mode we can also configure some settings including enabling trusted mode. When you click on the “Out-of-Browser Settings ...” button you’ll see a dialog with a lot of configuration settings for the out of browser application. Some of these settings are given default values as placeholders but you can change them to suit your needs.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
183
These are the things people always forget to fill out, kind of like status reports
Figure 7.10 You can configure many application settings here including application icons, window size, and text for window titles and shortcuts. The setting we’re most concerned with is the checkbox way at the bottom to require elevated trust. If this is selected the application will run in trusted mode when running outside the browser. While we’re in here let’s also set the width to 1024 and the height to 600. When installing a trusted mode application the user will get a different install dialog.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
184
Harm your computer? i don’t like the sound of that.
Figure 7.11 The trusted mode install dialog includes a security warning informing the user that they need to be sure they can trust the application. This warning is similar to warnings you would get when installing any other application from the web. Since we’re writing the application we can probably trust ourselves and install it. If you have selected to run in trusted mode and are running out of browser you will automatically be in trusted mode but you can confirm this with a bit of code: bool trusted = Application.Current.HasElevatedPermissions; This will return true if in trusted mode. If running inside the browser this will always return false. Now let’s put some of the trusted mode capabilities to work and make our application actually do something.
Cross domain web requests Making a cross domain web request in trusted mode is really no different from making one in sandboxed mode, the only difference is that you don’t need a cross domain policy file on the remote server. You can use either HttpWebRequest or WebClient to make the request or use a higher level layer like generating a SOAP proxy. In our case we’ll use WebClient which is the simplest to use. To begin with, we need a WebClient object and a way to ask it to fetch data for us. In MainPage.xaml.cs, let’s add a field to the MainPage class. WebClient client = new WebClient();
hey shouldn’t you be using a model here to separate out your business logic? ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
185
Yes you are right, best practices would be to put the business logic in a model and it would be even better to put the data retrieval and parsing code into a data service class but to simplify this sample we’ll be breaking a few rules. This sample is, after all, about out of browser features and we want to reduce the noise as much as possible. Following best practices we would also normally create a child control instead of stuffing everything into MainPage.xaml. In the MainPage constructor we’ll wire up an event handler for the DownloadStringCompleted event. public MainPage() { InitializeComponent(); client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted); } void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { } We need some input data, namely the URL of the feed we want to retrieve. Let’s allow the user to input this feed URL and give them a submit button. In MainPage.xaml, divide the LayoutRoot grid into two rows and change the background to blue. You can remove the TextBlock placeholder we had in there earlier. Inside this grid in the first row we’ll add another grid with three columns:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
186 Now we have a text box to accept the feed URL and a submit button. It should look something like this:
Figure 7.12 A simple address bar. The user will enter the URL for the feed and click the submit button. Let’s add a Click event to the button. In the Click event we’ll tell the WebClient to fetch the data. private void buttonSubmit_Click(object sender, RoutedEventArgs e) { Uri feedUri = new Uri(urlText.Text, UriKind.Absolute); client.DownloadStringAsync(feedUri, feedUri); }
If you were following mvvm principles you would use the command property instead of the click event handler
It probably looks a little strange that we’re passing in the same argument twice but this will make it easier for us to retrieve the URI of the request so that we can tell if it is the same URI as the previous request. This will come into play later in this chapter. Just to make sure some data is coming back, let’s display a message box in the DownloadStringCompleted event handler. void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { MessageBox.Show(e.Result.Substring(0, 500)); } Now if you type in the URL for any valid feed you should see a message box. We’ll use the Silverlight
Cream
feed
at
http://geekswithblogs.net/wynapsetechnicalmusings/Rss.aspx
for
example.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
this
187
Figure 7.13 The Geeks with Blogs site doesn’t have any cross domain policy files so we wouldn’t have been able to get this feed in sandboxed mode. Now that we have confirmed that we can successfully retrieve data, let’s get it into a form we can use. The RSS feed format is not very complex and it would be easy to parse the XML ourselves but Silverlight provides a library that we can use to make it even easier. An added benefit is that this library will seamlessly process both RSS and Atom feeds. If we did it ourselves we would need to add logic to parse
both
formats.
Add
a
reference
System.ServiceModel.Syndication assembly.
to
the
Silverlight
project
for
the
In MainPage.xaml.cs, add the following using
statements: using using using using
System.ServiceModel.Syndication; System.Xml; System.IO; System.Collections.ObjectModel;
Then to parse the feed all we need to do is get the string into a form that the SyndicationFeed class can consume. void MergeFeedItems(SyndicationFeed feed, bool notify) { } void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{ StringReader rdr = new StringReader(e.Result); ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
188 SyndicationFeed feed = SyndicationFeed.Load(XmlReader.Create(rdr)); MergeFeedItems(feed, false); } In the MergeFeedItems method we want to take the items from the feed and add them to a collection. The items are already in a collection inside the feed object but we want to be able to support polling for new data on a timer and merging any new items in. First we need a field to hold the list of merged items. ObservableCollection itemList = new ObservableCollection(); Then for the MergeFeedItems logic: void InsertFeedItem(SyndicationItem item) { foreach (SyndicationItem existingItem in itemList) { if (item.PublishDate > existingItem.PublishDate) { itemList.Insert(itemList.IndexOf(existingItem), item); return; } } itemList.Add(item); } void MergeFeedItems(SyndicationFeed feed, bool notify) { foreach (SyndicationItem item in feed.Items) { if (item.Id == null) { item.Id = item.Links[0].Uri.ToString(); } bool exists = (from existingItem in itemList where existingItem.Id == item.Id select existingItem).Any(); if (!exists) { InsertFeedItem(item); } } } Basically if the item’s Id property matched one already in the list we ignore it as a duplicate, otherwise we add the item based on the published date descending.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
189
what about that code that checks to see if the id is null?
There are some feeds which do not populate the Id property so to handle these we’ll use the URI of the feed item as the Id. We need something we can use to determine whether this is a new entry and that’s as close to unique as anything we have access to. Now when a feed is processed the itemList collection should be populated with a list of SyndicationItems. Let’s display this list in a ListBox. This will go in the left column of a two column grid inside our LayoutRoot.
we’ve seen xaml like this a few times, move along In the MainPage constructor, set the data context of the ListBox to our list of feed items. feedItemListBox.DataContext = itemList; Now if you run the application and request a feed you should see a list of the feed item titles in the list box.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
190
Figure 7.14 A list of titles for all of the items in our feed. It’s great that we can display the titles but more importantly we would like to display summary information and the blog posts themselves. This information is typically formatted as HTML, so the easiest way to display it would be if we had some way to embed HTML in Silverlight. Fortunately there’s a control for that.
7.3 Embedding HTML
In the browser you can combine Silverlight content and the HTML of the web page and use each for the tasks that best suit them but when running out of browser there isn’t an HTML container around the Silverlight content. To address the need of displaying HTML based content in out of browser mode ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
191 Silverlight provides the WebBrowser control. The WebBrowser control can only be used in out of browser mode and if you try to use it in the browser you’ll see a message that HTML is disabled.
Figure 7.15 This is what you’ll see if you try to use the WebBrowser control when Silverlight is running in the browser.
why do they do this? i want to use it everywhere.
There are some security concerns with embedding HTML inside Silverlight in the browser. The Silverlight team is very cautious about possible security exploits and always errs on the side of caution. Let’s put a WebBrowser control next to the ListBox in MainPage.xaml. One way to display HTML in the web browser control is to provide the HTML as a string. When we first retrieve a new feed let’s build some HTML to show the summaries of the feed items. Uri lastFeedUri = null; void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { Uri feedUri = e.UserState as Uri; SyndicationFeed feed = SyndicationFeed.Load(XmlReader.Create(new StringReader(e.Result))); if (feedUri != lastFeedUri) { lastFeedUri = feedUri; itemList.Clear(); WriteFeedHtml(feed); MergeFeedItems(feed, false); } else { MergeFeedItems(feed, true); } } We can tell that it’s a new feed that is being requested by comparing the Uri from the last request to this one. If it is a new feed we clear the list, create a summary HTML page by calling the ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
192 WriteFeedHtml method, and populate the list. If it’s a request for the same feed as our last request we just merge in the new items and set the notify flag to true. We’ll make use of this notify flag in a bit. Here is the WriteFeedHtml method: void WriteFeedHtml(SyndicationFeed feed) { StringWriter sw = new StringWriter(); sw.Write(""); sw.Write("{1}", feed.Links[0].Uri.ToString(), feed.Title.Text); foreach (SyndicationItem item in feed.Items) { ooh html, now that’s sw.Write("{1}", something I know! item.Links[0].Uri.ToString(), item.Title.Text); if (item.Summary != null) { sw.Write(item.Summary.Text); } sw.Write(""); } sw.Write(""); browser.NavigateToString(sw.ToString()); } Using
the
feed
information
we
construct
some
well
formed
HTML
and
then
call
the
NavigateToString method of the WebBrowser control to render it. Run the application again to see how it looks.
Figure 7.16 The WebBrowser control is now populated with our dynamic HTML generated from the feed information. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
193 The links are live and you can click on them to navigate just like you would in the browser. Another option for rendering HTML in the WebBrowser control is to point it at a URL. When a user clicks on an item in the ListBox we can navigate to the URL for that item. To do this we will wire up the
SelectionChanged event on the ListBox. private void feedItemListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.AddedItems.Count > 0) { SyndicationItem item = e.AddedItems[0] as SyndicationItem; browser.Navigate(item.Links. First().GetAbsoluteUri()); } } Now if you click on an item in the list you should go to the web page for that item.
Figure 7.17 Using the WebBrowser’s Navigate method we can display an existing web page. Now that we have our HTML rendering working let’s see what we can do about notifying the user when new feed data is available.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
194
7.4 Popup notifications
If you’ve used Microsoft Outlook you’ve seen the type of notifications we’re talking about here. They’re the ones that show up over the system tray and tell you that a new email message has arrived. Notifications in Silverlight are only available in trusted mode. The way this works is that Silverlight gives you a rectangle that you can fill with whatever you want and it is displayed for you. Before we can handle notifications we need to have something to notify about, and in this case that means creating a timer to check for new feed items. First we’ll declare a DispatcherTimer field in the MainPage class: System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer(); Then in the MainPage constructor we’ll initialize it and start it up. timer.Interval = TimeSpan.FromSeconds(15); timer.Tick += new EventHandler(timer_Tick); timer.Start();
Fifteen seconds is a pretty aggressive refresh time but it will help us not have to wait forever when testing. You should set this to a more reasonable number like 10 minutes or more once it’s working properly.
Not with my feed, buddy, I blog like an animal Finally, in the Tick event handler we can request data from the last feed URI specified. void timer_Tick(object sender, EventArgs e) { if (lastFeedUri != null && !client.IsBusy) ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
195 { client.DownloadStringAsync(lastFeedUri, lastFeedUri); } } The DownloadStringCompleted event handler already has logic in it to do the right thing when the same feed is requested again. It calls MergeFeedItems with a notify flag set to true. We can change the MergeFeedItems method to do a notification if a new item is added. if (!exists) { InsertFeedItem(item);
if (notify == true) Notify(item); } The Notify method doesn’t exist yet, we’ll get to that in a moment. Let’s create a user control call
ItemNotify.xaml that will be displayed in the notify window. Set its Width to 300 and Height to 100. Define its contents as follows: Now for the Notify method of MainPage. void Notify(SyndicationItem item) { ItemNotify notifyControl = new ItemNotify(); notifyControl.DataContext = item; NotificationWindow window = new NotificationWindow(); window.Height = notifyControl.Height; window.Width = notifyControl.Width; window.Content = notifyControl; window.Show(5000); }
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
196 This code creates an ItemNotify user control, sets its DataContext to the new item for data binding, creates a new NotificationWindow, sets its properties and shows it for five seconds. It can be a little tricky to test this one since feeds generally don’t change very often. One easy way to do it is to point at the RSS feed for your Twitter user and post a new entry with your favorite Twitter client or through the Twitter web site.
What if I don’t have a twitter account?
Twitter accounts are free and easy to sign up for. You don’t have to use it ever again after this if you
don’t
want
to.
The
URL
for
a
user’s
twitter
feed
is
http://twitter.com/statuses/user_timeline/username.rss where username is the user’s Twitter user name. If you get your RSS feed and post a new entry you should see the notification window.
Figure 7.18 New entries on the feed are added to the feed item list and a notification window is displayed. That concludes our out of browser sample. It is a great start on a general purpose feed reader desktop application. You could enhance it by keeping track of which feed entries were read or allow the user to store and access multiple feeds. Now for a look back at what was covered in this chapter.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
197
7.5 Summary
Out of browser mode is a powerful capability of Silverlight applications. With the addition of trusted mode, Silverlight is a great platform for creating light cross-platform desktop applications that are easy to create and deploy. Enabling out of browser support for a Silverlight application is as easy as clicking a checkbox and if you want a custom install experience it’s only a handful of lines of code more. Trusted mode takes out of browser Silverlight applications to the next level. You can access web resources without cross domain policy concerns, access COM objects on Windows, create custom popup notifications and more. Many web resources that aren’t directly available to Silverlight in sandboxed mode can now be leveraged to create powerful applications. Full HTML support through the WebBrowser control allows you to embed any HTML in your out of browser application including dynamically generated HTML and existing web pages. Finally, support for notification windows or “toasts” allow you to have a tighter integration with the desktop and alert the user if new information has arrived. Trusted out of browser Silverlight applications still have some limitations and you don’t have as much access to system resources as you can have with a WPF application but if it does enough for what you need it’s an attractive option and a great way to allow Mac users to use your application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
198
8 Game Development in Silverlight Have you ever wanted to develop your own game but didn’t know how? This chapter will focus on the basics of game development in Silverlight, including many techniques that you’ll be able to take advantage of in your own games. Game development is a bit different from what we’ve done so far. Previously we’ve set up some controls, wired up their events, and provided them with data and let them go and do their thing. In game development you need to be more of a control freak and know exactly where everything is and how it’s going to behave for every frame that’s drawn. This is very different but it can be fun too. We will still be taking advantage of vector graphics in XAML, control templates, displaying images, and many of the other concepts covered up to this point so things aren’t completely foreign and you should have a solid footing at this point to get started.
The Egg Scramble Game By the end of this chapter we will have written a complete working game.
how about quake? i’d like to learn how to write that. Quake is way beyond what we can cover in this chapter. Instead we will create a simple game that illustrates the concepts important to getting started with game development. You’ll control a cute chicken that is trying to catch eggs being dropped by a mischievous dog. You have to catch twenty eggs each level with increasing speed. Ok, so it’s not Quake, but you have to start somewhere.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
199
Figure 8.1 The completed Egg Scramble game. Notice the egg falling to its death, there’s no way the chicken will get there in time. Let’s create a new Silverlight Application called EggScramble and take the defaults for generating a web site to host it.
Why Silverlight for Game Development? Strong vector and bitmap graphics support, text rendering, and animations combined with the power and performance of managed code make Silverlight appealing for game developers. Combine with this the easy deployment model and the ability to reuse game code written for other managed platforms such as WPF or XNA Game Studio. Online games are big business. Games can help drive traffic to your site, advertise your brand, or you can submit them to a game site and share advertising revenue.
To keep the game logic out of the MainPage control, let’s create a user control that we can put this logic in. Create a new user control called Game.xaml and add the Game user control to MainPage.xaml. Remember to add the local namespace declaration. Here is the updated Grid in MainPage.xaml:
And here is the Game.xaml after some modifications to hold our game elements: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
200 Notice that the width and height are being set on the user control element. It’s easier to code the game to a specific size and handle scaling it later if necessary. The Canvas element is where we’ll put our actual game elements.
we’ve usually used grids as the container before, why a canvas now? Since we’ll be doing all of the positioning we don’t need a grid or a stack panel to assist with layout and these extra features can get in the way and impact performance. Now that our game surface is set up, let’s see how we can start adding some game elements to it.
8.1 Game basics
There are some common tasks in game development that are present in almost every game. These include displaying and moving graphics, handling input, and implementing timers. Let’s start with the sprite, one of the most fundamental concepts in game development.
Sprites Pretty much any game is going to need some things to move around. In game development, these things are called sprites. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
201 oh good i’m thirsty
Not that kind of sprite. Sprites started out as something that lived in the graphics hardware and made it possible to quickly paint an object on a background. These hardware sprites are not in use today but the term has stuck around and now is used to refer to any game element that moves. In Silverlight, our sprites will correspond to a control. This control will have some extra properties and behaviors such as keeping track of its position, being able to update that position based on its velocity, and checking to see whether it collides with another sprite. To allow our sprites to take on different shapes and sizes, we’ll base them on a templated control. A templated control makes it easier to inherit the behaviors of a base class while applying templates for the visual elements, similarly to how we skinned a HyperlinkButton control in chapter 5. In the
EggScramble project, choose to add a new item and you’ll see the Add New Item dialog.
Figure 8.2 Notice the Silverlight Templated Control template. It helps us create custom controls that use control templates. Select the Silverlight Templated Control item type and name it Sprite.cs. A templated control doesn’t have an associated XAML file like a user control. The visual elements for the control are read from a template file. If you take a look at what was created, you’ll see that the control inherits from
Control and the DefaultStyleKey is set in the constructor. This default style key is how we specify what control template to use.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
202 public class Sprite : Control { public Sprite() { this.DefaultStyleKey = typeof(Sprite); } } So where does the XAML come from? It comes from the Generic.xaml file in the Themes folder. If you open this file, you’ll see that a template has been generated for the Sprite control. You can insert whatever XAML you want inside the ControlTemplate tag and this is what will be used by the control. Let’s change how the Sprite looks by replacing the contents of the
ControlTemplate element.
black square, should be red square We can now add this sprite to the game surface. In Game.xaml, add a Sprite element:
In the designer view you should now see a black sprite in the upper left corner as in Figure 8.3.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
203
Figure 8.3 A black sprite in the top left corner of the game surface
looks like pong, can i play? We’re not writing a pong game, this square is just a placeholder that we could use until we create our game graphics. In our game we’ll have sprites that look like a chicken, a dog, and eggs. We can create these different looking sprites by creating new controls that inherit from the Sprite control. Let’s start by creating the chicken sprite.
The chicken came first The chicken is the player’s character and is the one that will be controlled with the keyboard. It moves back and forth and catches the eggs. Create a new Silverlight Templated Control called Chicken. Then change the Chicken class to inherit from Sprite instead of Control. public class Chicken : Sprite { public Chicken() { this.DefaultStyleKey = typeof(Chicken); Width = 75; Height = 115; } } The Width and Height properties will be used in some of the game logic later. By inheriting from
Sprite we will be able to define properties and methods common to all sprites and have all sprites leverage them. When we created the chicken as a new templated control there was an entry added to
Generic.xaml for it. All we need to do now is paste our XAML into the chicken’s control template. We could list out the pages of XAML for the shapes that combine for the chicken but when it comes right down to it, the graphics are not that important and don’t factor into actually making the game work. In your game you’ll probably want to make your own anyway. Our chicken sprite happens to have ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
204 been created in Microsoft Expression Blend, but it could have been created in anything that can export to the XAML format. Your sprite could also be a bitmap image if it makes more sense for your game. In this case you would just place an Image element in the control template. Finally, in Game.xaml, replace the Sprite element with a Chicken element: Now on your design surface you should see the chicken sprite in the upper left corner as displayed here.
Figure 8.4 The game surface with the chicken sprite This isn’t going to be an interesting game if the chicken just sits up in the corner, so let’s see what we can do about setting its position. Remember from way back in Chapter 2 that the position of an element in a Canvas panel is controlled by the Canvas.Left and Canvas.Top attached properties. You can use these if you want to and they will work fine, but based on some testing and comparisons it is actually more efficient to use a translate transform to position your sprites. The chicken isn’t the only thing we’re going to need to control the position of, so it makes sense to add some logic to the Sprite control so that all sprites have access to it. We’ll add a translate transform to the control and set its values via X and Y public properties. public class Sprite : Control { TranslateTransform translate; public Sprite() { this.DefaultStyleKey = typeof(Sprite); translate = new TranslateTransform(); this.RenderTransform = translate; } public double X ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
205 { get { return translate.X; } set { translate.X = value; } } public double Y { get { return translate.Y; } set { translate.Y = value; } } } Now in the chicken element in Game.xaml we can set the X and Y values: And in the designer pane you’ll now see that the chicken has responded to this positioning.
not bad but i want to see it move.
Figure 8.4 The chicken with its X and Y properties set In order to make the sprite move based on user input we need something called a game loop.
Creating a game loop When writing a game it is very common to need to have more control over positioning and animation of elements than a storyboard can provide. You want to be able to change direction on a moment’s notice based on input, or stop items that collide, or apply physics to game objects. This is usually done through a game loop.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
206
what’s a game loop and where can i get one? A game loop, as the name implies, is a piece of code that is executed repeatedly on an interval and contains most of your game logic. It needs to execute often enough to allow for smooth animation and timely response to input, usually at least once per frame that is drawn. It often calls a method which you can put your own code inside. There are a few different ways to make something fire on an interval in Silverlight. For our game loop we’ll use the CompositionTarget.Rendering event. This event fires once per frame so it’s a good place to update positions and do other game related tasks. In the Game constructor, add the following: CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); Then for the event handler, we’ll add some code to move the chicken once per frame. DateTime lastUpdateTime = DateTime.Now; void Update(double elapsedSeconds) { chicken.X++; } void CompositionTarget_Rendering(object sender, EventArgs e) { DateTime now = DateTime.Now; TimeSpan elapsed = now - lastUpdateTime; lastUpdateTime = now; Update(elapsed.TotalSeconds); } Now if you run the game the chicken should move slowly to the right. You’ll notice that we’re calculating the time since the last time the Rendering event fired and we’re passing that to the Update method. This is because many factors could impact how often this event fires. With Silverlight running on netbooks and coming to mobile devices it’s a good idea to account for a wide range of performance. In basic physics the position of an object at any given time can be determined by calculating the distance traveled from a previous position.
I remember this from high school physics, d=vt, but I forget what v and t stand for That’s right. The distance can be calculated from multiplying the velocity by the elapsed time. We can change the Update method to use this formula as follows: ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
207 void Update(double elapsedSeconds) { chicken.X += 200 * elapsedSeconds; } This assumes that the chicken’s X velocity is 200. If you run the game now you’ll see a problem. The chicken goes right off of the end of the screen. Let’s constrain the valid positions for a sprite to an area inside of a given rectangle. We’ll also add a field of type Point to store the X and Y velocity.
what? velocity is a vector, not a point This is true and it may seem odd to store a velocity in a Point but Silverlight doesn’t have a built in vector data type. Since the Point data type holds an X and a Y value it’s a good fit even though we’re not actually storing a location in it. Since we will have other sprites besides the chicken that need to move let’s put this logic in the Sprite class. First we’ll declare fields to hold the bounding rectangle and the velocity. public Point Velocity; public Rect BoundingRect = Rect.Empty; Then let’s add an Update method to the Sprite class which updates the position of the sprite based on its previous position, elapsed time, and velocity. We’ll also constrain the position so that it falls inside the specified bounding rectangle. public virtual void Update(double elapsedSeconds) { X += Velocity.X * elapsedSeconds; Y += Velocity.Y * elapsedSeconds; if (BoundingRect != Rect.Empty) { X = Math.Max(Math.Min(BoundingRect.Right, X), BoundingRect.Left); Y = Math.Max(Math.Min(BoundingRect.Bottom, Y), BoundingRect.Top); } } The Update method is virtual so that we can override it in inherited sprite controls that may need a different behavior. If the bounding rectangle isn’t changed from its default value of Empty we won’t constrain the sprite’s position. In the chicken’s constructor let’s set its bounding rectangle: BoundingRect = new Rect(25, 0, 650 – Width, 480);
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
208 We’re leaving a little room on either side so that the chicken can’t get right to the edge. The Y values don’t matter because the chicken is only moving in the X direction but they need to be big enough to contain the chicken. Now we can change the Update method of the Game class as follows: void Update(double elapsedSeconds) { chicken.Velocity.X = 200; chicken.Update(elapsedSeconds); } If you run the game now you’ll see that the chicken will move to the right edge and then stop.
shouldn’t the player be controlling this? You’re right, now that we can give the sprite a velocity and make it move let’s see how we can use the keyboard to control it.
Keyboard Handling Keyboard input is one of the most common forms of input for online web based games. The other main input is the mouse which isn’t necessary for this game but is easy to implement by handling mouse events. When a key is pressed in Silverlight it causes some events to fire. Keyboard events are tied to a visual element, and in a game will usually be the root visual of the game. There are KeyDown and KeyUp events you can handle and act upon them to move sprites, but what most games are really interested in is not when a key down or key up event happens but whether a key is currently pressed. This is the case with our game. If the left or right cursor key is pressed we’ll move the chicken appropriately otherwise it will stand still. To make this easier and give you something you can easily reuse in your own games, we’ll create a keyboard handler class that listens for the key events and gives us a way to just ask if a key is pressed. Add a new class to the project called KeyboardHandler, and here is the code for it: using System.Windows; using System.Windows.Input; using System.Collections.Generic; namespace EggScramble { public class KeyboardHandler { Dictionary _keys = new Dictionary(); public KeyboardHandler() { UIElement root = Application.Current.RootVisual; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
209 root.KeyDown += new KeyEventHandler(root_KeyDown); root.KeyUp += new KeyEventHandler(root_KeyUp); } void root_KeyDown(object sender, KeyEventArgs e) { _keys[e.Key] = true; } void root_KeyUp(object sender, KeyEventArgs e) { _keys[e.Key] = false; } public bool IsPressed(Key key) { return (_keys.ContainsKey(key) && _keys[key] == true); } public bool IsPressed(Key key1, Key key2) { return IsPressed(key1) || IsPressed(key2); } } } It’s not critical to know how everything in this class works since the game will only really worry about calling the IsPressed methods, but for a quick overview it’s keeping a list of the keys that have been pressed and whether that key is currently pressed. The key down and key up events are wired to the root visual of the application. As long as the Silverlight application has focus all key presses will be handled. When a key down event occurs, the list entry for the key that was pressed is set to true, and when it’s released the entry is set to false. The IsPressed method checks to see if the requested key is in the list, and if so, if its value is true. Finally there is another IsPressed method which checks two keys and will return true if either or both are pressed. This second IsPressed method is useful if you want two keys to do the same thing.
i’m sure this is all very interesting, but can you just tell me how to use it? Sure, it’s actually pretty easy. We’ll add code to the Game class to check to see if keys are pressed in the game loop. First declare a field for the keyboard handler object and create an Initialize method to create it. KeyboardHandler keyHandler; public void Initialize() { keyHandler = new KeyboardHandler(); ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
210 } Next let’s handle the Loaded event of the MainPage class and call the game’s Initialize method in there. We need to do it there instead of in the constructor because we can’t wire up the keyboard events until loading is complete. Add a Loaded event handler to the UserControl
element in
MainPage.xaml and in this event handler we’ll call the game’s Initialize method. private void UserControl_Loaded(object sender, RoutedEventArgs e) { game.Initialize(); } Finally in the game’s Update method we’ll call HandleInput to see what keys are pressed and act accordingly. public double Speed = 200; public double Delay = .4; void HandleInput() { if (keyHandler.IsPressed(Key.Left, Key.A)) chicken.Velocity.X = -Speed; else if (keyHandler.IsPressed(Key.Right, Key.D)) chicken.Velocity.X = Speed; else chicken.Velocity.X = 0; } void Update(double elapsedSeconds) { HandleInput(); chicken.Update(elapsedSeconds); } The reason for checking Left or A and Right or D is that some keyboards don’t have cursor keys and the common alternate cursor keys for games are W, A, S, and D which correspond to Up, Left,
Down, and Right respectively. By checking for the cursor key and the alternative keys we have both bases covered. We have also replaced the constant speed of 200 with a public Speed field. This will let us speed up the game as the difficulty increases. The Delay field will come into play later when we start moving the dog around. If you run the game now, you should be able to move the chicken using the keyboard.
that didn’t work for me. am i missing something?
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
211 Yes there is a little problem with keyboard input for browser plug-ins like Silverlight. You need to click somewhere on the game surface first so that the plug-in get focus and recognizes keyboard input. We’ll look at a clever way around this later in the chapter, but for now just click somewhere and then the keyboard should work. Let’s move on now and add the dog to the game.
8.2 This game needs an enemy
The dog is different from the chicken because it is not responding to user input to move. It’s going to move to a spot, wait for a bit, drop an egg, wait a bit longer, and then move to a new spot. This could be done using storyboards by changing the target value in code before each time the dog moves, but since we already have a game loop and some code to help move a sprite let’s use that instead. Create a new templated control called Dog.cs. Just like with the chicken, change the base class for the dog from
Control to Sprite. Again we’ll do some initialization in the constructor. public class Dog : Sprite { public Dog() { this.DefaultStyleKey = typeof(Dog); Width = 80; BoundingRect = new Rect(25, 0, 650 - Width, 480); } } The graphics for the dog were created in Expression Blend, but you could insert graphics from your favorite paint or drawing program.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
212
Figure 8.5 Don’t let her fool you, she’s sneaky. Just like with the chicken, the XAML for the dog gets put into the dog’s control template in
Generic.xaml. Now we can add the dog sprite to Game.xaml. While we’re in there let’s also add a background image to make the visuals more interesting.
Figure 8.6 What a difference the background makes, it really pulls everything together. The background image was provided by Diane Leeper. You can see more of her work at dianeleeper.com. The XAML changes for the background and dog are as follows:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
213 Now that we have our dog, it needs to do something. Let’s take a look at how to control our dog’s behavior.
Controlling the dog Since the dog’s behavior is independent of player input and needs to have a mind of its own it is actually harder to implement than the chicken. The dog has a very simple behavior, but depending on the game you may need complex artificial intelligence logic.
artificial? that’s an insult, why can’t we just say intelligence? The dog has a few states that it can be in. It can be moving, waiting to drop an egg, waiting to move after dropping an egg, or stopped when a game isn’t in progress. The first three states also have an amount of time that they will be active before the next state and each state has a next state that it will transition to. To keep track of the dog’s state we can create a simple state machine. In the Dog class, add the following: double duration; DogState state = DogState.Stopped;
1
public enum DogState { Stopped, Moving, WaitingToDrop, AfterDrop }
2 2 2 2 2 2 2
Dictionary nextState = new Dictionary() { { DogState.Stopped, DogState.Moving }, { DogState.Moving, DogState.WaitingToDrop }, { DogState.WaitingToDrop, DogState.AfterDrop { DogState.AfterDrop, DogState.Moving } };
A A A A }, A A A
1 The current state of the dog 2 States the dog can be in A Next state for each state value
A To store a list of current states and their corresponding next states we will use a Dictionary which is a collection of keys and values. For each key, in our case the current state, we can retrieve a value which is the next state we want to dog to be in. For example, when the Moving state is completed, the dog will go into the WaitingToDrop state. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
214 There are a couple of things we’re going to need to be able to tell the dog to do and then it can do the rest by itself. We need to be able to tell it to start moving when starting a new game and we need to be able to tell it to stop when the game is over. To do this let’s add a Start and Stop method to the
Dog class.
brilliant did you think that up all by yourself?
Game game; public void Start(Game game) { this.game = game; Transition(state, DogState.Moving); } public void Stop() { Transition(state, DogState.Stopped); }
1 A
B
1 Keep a reference to the Game object A Go to the Moving state B Go to the Stopped state
Cueballs in code and text Since we’ll need access to some of the data stored in the Game object we’ll pass it into the Start method (#1). This makes sense since the Start method will always be called before we need it. The
Transition method will transition to the next state and run any logic associated with the state transition. The Transition method will look like this: void Transition(DogState currentState, DogState nextState) { state = nextState; if (state != DogState.Moving) { Velocity.X = 0; 1 if (state != DogState.Stopped) 2 duration = game.Delay; 2 } else { ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
215 SetNextTarget();
3
} } 3 Velocity 0 will stop the dog 4 Set amount of time to wait 5 Calculate next position
Cueballs in code and text, replace #1-#3 below with cueballs If the dog is entering anything but the Moving state we set its velocity to zero (#1) and set the time to wait based on the game’s current Delay value (#2). Otherwise the dog is entering the Moving state and we need to figure out where it is moving to and how long it will take to get there. This is done by calling the SetNextTarget method(#3).
I don’t think we have one of those yet.
That’s right so let’s vreate it now. Here is the body of our SetNextTarget method: Random rand = new Random(); void SetNextTarget() { double speed = game.Speed; double targetX = 0; double distance = 0; double delta = 0; while (distance < 100) { targetX = rand.Next( (int)BoundingRect.Left, (int)BoundingRect.Right); delta = targetX - X; distance = Math.Abs(delta); } Velocity.X = speed * (delta / distance); duration = distance / speed; }
3 1 1 1 1 2 2 4 5
Cueballs in code and text This method randomly generates the next position of the dog based on the maximum and minimum X values for the dog (#1). This value’s distance from the current position is calculated (#2) and if it is more than 100 pixels from the current position it will be used as the next position (#3). The X velocity of the dog will come from the current game speed and delta divided by distance will tell us whether the ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
216 velocity is positive or negative (#4). The duration is calculated by distance divided by speed (#5). All of this state transitioning code needs to be called from somewhere, and we’ll do that in the dog’s Update method.
public override void Update(double elapsedSeconds) { if (state == DogState.Stopped) return; 1 base.Update(elapsedSeconds); 2 duration -= elapsedSeconds; 3 if (duration = 0; i--) { Egg egg = Eggs[i]; egg.Update(elapsedSeconds); if (egg.Y > 430) { Eggs.Remove(egg); } } }
Why ar e we l o o pink backvar ds?
We’re looping backwards because it makes it easier to remove eggs from the collection, otherwise we would have to create a list of eggs to remove and then remove them. When the eggs fall past a certain point, this is a “miss” and the egg is removed. This new method needs to be called from the
Update method of the Game class: UpdateEggs(elapsedSeconds); Run the game again and you’ll see the following:
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
221
Figure 8.8 The dog is now dropping eggs and they disappear when they hit the ground. Don’t ask why, just go with it.
isn’t the chicken supposed to be catching the eggs? Absolutely. In order for the chicken to catch the eggs though we need to add some logic to the game to do something very common to game development. We need to be able to detect when the egg is touching the chicken. This is commonly known as collision handling.
Collision handling Checking for collisions of sprites in games can be as simple as seeing if two circles overlap or as complex as comparing two sprites pixel by pixel. For this game we’ll check if two rectangles intersect. Since this is a common requirement for sprites, it makes sense to add a check to the Sprite class. In the Sprite class, declare a field to hold the collision rectangle. public Rect CollisionRect; Then in the Egg constructor, add the code to initialize this Rect based on the width and height of the sprite. public Egg() { this.DefaultStyleKey = typeof(Egg); Width = 28; Height = 36; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
222 CollisionRect = new Rect(0, 0, Width, Height); } The chicken is a bit different since if we allow a collision with any part of the chicken, the game won’t feel right. What we really need is to detect whether the egg is in contact with the nest. In the Chicken constructor, add the following: CollisionRect = new Rect(5, 15, 65, 20); The egg’s collision rectangle was the entire sprite. The chicken’s collision rectangle covers the bottom part of the nest and a bit extra.
Figure 8.9 The modified collision rectangle for the chicken. Only this smaller area will count as a collision.
Now for the actual collision check. This code will go in the Sprite class so all sprites can take advantage of it. public bool CollidesWith(Sprite s) { Rect r1 = this.CollisionRect; r1.X += this.X; r1.Y += this.Y; Rect r2 = s.CollisionRect; r2.X += s.X; r2.Y += s.Y; r1.Intersect(r2); return !(r1.IsEmpty); }
1 1 1 2 2 2 3 3
1 Calculate collision rectangle for current sprite 2 Calculate collision rectangle for other sprite 3 See if the two rectangles overlap
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
223 To calculate the current collision rectangle we need to move the collision rectangle based on the current position and then see if they overlap. If the resulting rectangle is empty there is no overlap. Then the UpdateEggs method needs to be modified to check whether the egg collides with the chicken. void UpdateEggs(double elapsedSeconds) { for (int i = Eggs.Count - 1; i >= 0; i--) { Egg egg = Eggs[i]; egg.Update(elapsedSeconds); if (egg.Y > 430) { Eggs.Remove(egg); } else if (egg.CollidesWith(chicken)) { Eggs.Remove(egg); } } } For now we’ll just remove the egg if there’s a collision. If you run the game now, the chicken can catch eggs.
So we’re done now right? let’s put the game up on our site It’s true that most of the game mechanics are in place but we still need to do some important things like tracking the score and the current level and checking for game over. We’ll get to those in a bit but first let’s handle resizing of the game and full screen mode.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
224
8.4 Managing the game window
Running your game in full screen mode is a nice touch that your users will appreciate and it’s really not that hard. The first step to getting this working is to automatically adjust to the size of the window that is hosting the game. This is very easy and can be done using the built in Viewbox control. In
MainPage.xaml, wrap the Game control with a Viewbox: By setting the Stretch property to Uniform, the contents of the Viewbox are resized to fill the available space without distorting the contents. This is similar to letterbox mode on an HDTV. There are other stretch modes to fill the whole space or to fill the whole space without distortion. Take a moment to play with these and see how they look.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
225 Figure 8.10 The game in a browser window that is short and wide causes a letterbox effect. One thing that is very nice about scaling elements in Silverlight is that if you were to have a mouse event wired up to the scaled canvas, the mouse position will also be scaled relative to the current scale, so no changes are necessary to the mouse handling logic.
How does this help with full screen mode?
By adding the ability for the game to resize based on the size of the control this also will scale to full screen. Let’s see what else we need to do to make this happen.
Full screen mode, it’s easy! To trigger full screen mode we’ll use a Button. Add the Button just before the Canvas element in the Game.xaml: Note the IsTabStop property. If the IsTabStop property wasn’t set to False then the Button could grab focus and steal keystrokes from the game. The Click event handler will then toggle the full screen property. private void fullScreenButton_Click(object sender, RoutedEventArgs e) { App.Current.Host.Content.IsFullScreen = !App.Current.Host.Content.IsFullScreen; } Now if you run the game, you should be able to toggle in and out of full screen mode. Pretty cool huh? Note that when in full screen mode only certain key presses are sent through to the application.
argh why would they limit me like that?
This is to keep someone from creating an application that takes over your screen and mimics your desktop to capture sensitive information. The keys that are allowed are the cursor keys, space bar, tab,
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
226 home, end, page up, page down and enter, so if going full screen and accepting keyboard input for your game, limit it to those keys.
Speaking of keyboard input, what about the problem where we need to click on the game? Thanks for reminding me. This can be solved with a startup screen.
Adding a startup screen A startup screen is a clever way of getting the user to click on the game. This screen could be a menu if you have different game modes or options that can be set but in this game it will just be a button to click to start the game. After the Canvas element in Game.xaml add the following: Basically it’s just some text and a button. In the click event handler we’ll hide the splash screen and start a new game. Make sure to remove the NewGame call from the
Loaded event handler in
MainPage. private void startGameButton_Click(object sender, RoutedEventArgs e) { splashScreen.Visibility = Visibility.Collapsed; NewGame(); } In order to give the text for the startup screen and for the rest of the game a less boring look, you can set the FontFamily on the MainPage control to Comic Sans MS as well as the default font weight and size. If this is set at the UserControl level, all elements beneath it will also be set unless explicitly set to something else: The namespace declarations were removed above for clarity, but don’t remove them from the control. Now the startup screen should look like the one below and when you click on it the game should start and the keyboard should work. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
227
Figure 8.11 The startup screen, boring but functional.
it’s looking like a real game, when can i play it?
It won’t be too much longer until you can play it. First we need to add scoring, levels, and game over logic. Let’s get these in place next.
8.5 Lives, levels, and scoring
Most games have at least a couple if not all of these. Players love to try to beat their best score or level reached. Levels are also important because they increase the game difficulty so that you’re not stuck at the same difficulty long enough to get bored.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
228 In Egg Scramble, each level will have 20 eggs to be caught and a 5 misses allowed. Each level increase has a corresponding speed increase for the dog, eggs, and chicken. The first thing we need is a way to display the information.
time for some data binding again right? Yes using data binding here would be the proper way to do it. However we’ve seen similar code a few times already so we’re going to cheat a bit here. Immediately before the Dog element in the
Game.xaml add the following: In the Game class we need some fields to hold the score and other important game data. We also need a method we can call to update the scoreboard text. public public public public
int int int int
Score; Level; CatchesNeeded; MissesAllowed;
void UpdateScoreboard() { scoreboard.Text = string.Format("Level: {0}\nScore: {1}\n" + "Catches Needed: {2}\nMisses Left: {3}", Level, Score, CatchesNeeded, MissesAllowed); } The number of catches needed, misses allowed, and speed of the elements depends on what level you’re on so there needs to be a way to set all of these. Let’s add a StartLevel method to the Page class. void StartLevel() { CatchesNeeded = 20; MissesAllowed = 5; double speed = 200 + Level * 40; double delay = .4 - Level * .02; if (delay < 0) delay = 0; UpdateScoreboard(); } Any time any of our fields displayed in the scoreboard are changed we’ll need to call the
UpdateScoreboard method, just a price we have to pay for not using data binding. Next the NewGame method needs to be updated to reset the score and level and call StartLevel.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
229 public void NewGame() { Level = 1; Score = 0; StartLevel(); dog.Start(); } With this start game logic in place you should now see the scoreboard displayed when you run the game.
Figure 8.12 At the start of a new game the game data is initialized and the scoreboard is updated. Finally we need to add logic to the UpdateEggs method of the Game class to do the right thing when an egg is caught or missed. void DoGameOver() { } void UpdateEggs(double elapsedSeconds) { for (int i = Eggs.Count - 1; i >= 0; i--) { Egg egg = Eggs[i]; egg.Update(elapsedSeconds); if (egg.Y > 430) { Eggs.Remove(egg); ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=443
Licensed to Wow! eBook
230 MissesAllowed--; if (MissesAllowed