Professional Documents
Culture Documents
M I C R O S O F T
L E A R N I N G
P R O D U C T
Information in this document, including URL and other website references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, email addresses, logos, people, places, and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, email address, logo, person, place, or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. The names of manufacturers, products, or URLs are provided for informational purposes only and Microsoft makes no representations and warranties, either expressed, implied, or statutory, regarding these manufacturers or the use of the products with any Microsoft technologies. The inclusion of a manufacturer or product does not imply endorsement of Microsoft of the manufacturer or product. Links may be provided to third party sites. Such sites are not under the control of Microsoft and Microsoft is not responsible for the contents of any linked site or any link contained in a linked site, or any changes or updates to such sites. Microsoft is not responsible for webcasting or any other form of transmission received from any linked site. Microsoft is providing these links to you only as a convenience, and the inclusion of any link does not imply endorsement of Microsoft of the site or the products contained therein. 2011 Microsoft Corporation. All rights reserved. Microsoft, and Windows are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries. All other trademarks are property of their respective owners.
Module 1
Lab Instructions: Introduction to Building Silverlight Business Applications
Contents:
Exercise 1: Creating a Silverlight Application 4 Exercise 2: Configuring Out-of-Browser settings for the Silverlight Application 7
Lab Introduction
In this lab, you will create a new Silverlight application based on the Silverlight Business Application project template, and you will modify application resource strings for the solution. You will also experiment with the WCF RIA services that provide authentication and user registration functionality to the Silverlight application. Then, you will configure the application for running outside the browser, including setting elevated trust requirements, and you will experiment with the various installation and uninstallation processes. Finally, you will add code for detecting when the application is running outside the browser and will develop code that enables the user to close the application. The exercises for this lab are: Exercise 1: Creating a Silverlight Application Exercise 2: Configuring Out-of-Browser settings for the Silverlight Application
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Task 2: Test and examine the authentication and User Registration Services of the Silverlight application.
Start the application. Review the content on the Home page. Review the content on the About page. Log on by registering a new user with the following information. User name: KimA Friendly name: Kim Abercrombie Email: KimA@contoso.com Password: Pa$$w0rd Confirm password: Pa$$w0rd Security question: What is your pets name? Security answer: Buster
You are registered through the built-in authentication services provided by the application, and you are logged on to the application. The authentication is handled by the WCF service in the GreetingCardManagement.Web hosting application. Note that the application displays a welcome message that uses the friendly name you specified. Log off from the site. Note that the application welcome message is removed as is the friendly user name. Close Internet Explorer. Examine the Services\AuthenticationService.vb or Services\AuthenticationService.cs code file in the GreetingCardManagement.Web project. Notice how the AuthenticationService class is annotated with the EnableClientAccess attribute, to enable the Silverlight application to access it. The AuthenticationService class is derived from the generic AuthenticationBase(Of T) or AuthenticationBase<T> class, which in turn implements the generic AuthenticationBase<T> interface. The type of the user entity to authenticate is specified using T.
Examine the user entity, named User, by going to the definition of the class User. Notice how the User class in the User.shared.vb or User.shared.cs file is partial, because it extends the User type by adding shared properties and methods that are available both to the server application and the client application, Silverlight, in this case. The User class implements the DisplayName property
Examine the main partial class User, in the Models folder. Note how the main partial class User is derived from the UserBase class, and implements the FriendlyName property.
Examine the Services\UserRegistrationService.vb or Services\UserRegistrationService.cs code file in the GreetingCardManagement.Web project. Notice how the UserRegistrationService class is also annotated with the EnableClientAccess attribute. The UserRegistrationService class is derived from the abstract DomainService class, which is the base class for all System.ServiceModel.DomainServices.Server.DomainServices. The UserRegistrationService class uses the ASP.NET Membership and Roles API to provide the user registration services.
Examine the content of the App_Data folder in the GreetingCardManagement.Web project. Notice how the ASPNETDB.MDF SQL Server Express database is located in the App_Data folder. It was automatically created when you used the user registration services as part of creating or registering a new user.
Save the changes. Start the application. Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. Note that you are logged on automatically as 10554A-SEA-DEV\Student because you have configured the website to use Windows authentication.
Log off from the site. A custom dialog box with an error message appears, because it is not supported to log off from a Windows-authenticated application.
Close the custom error dialog box. Close Internet Explorer. Open the App.xaml.vb or App.xaml.cs code file in the GreetingCardManagement project. In the App class constructor, comment out the line of code that initializes forms authentication.
In the App class constructor, uncomment the line of code that initializes Windows authentication. Save the changes. Start the application. Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. Note that you are logged on automatically as 10554A-SEA-DEV\Student because you have configured the website to use Windows authentication. Note also that the logout link is no longer available, because you have configured the Silverlight application to correctly use Windows authentication.
Start the application. Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. The application is still running in the browser.
Install the application locally, and add shortcuts to the Start menu and the desktop. The application opens in its own window.
Close Internet Explorer. The application continues to run in its own window.
Close the GreetingCard Desktop Management localhost window. On the Start menu, click All Programs, and then click GreetingCard Desktop Management. The application opens directly in its own window.
Create an event handler for the Click event of the closeLink HyperlinkButton control. [Visual C# only] Visual Studio adds a Click attribute to the markup for the HyperlinkButton control. Visual Studio creates the event handler stub for the event, and the code file appears.
In the Click event handler, close the MainWindow form by using the App.Current property. Save the changes.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 2
Lab Instructions: Building Data-Driven Applications
Contents:
Exercise 1: Connecting to a Database in a Silverlight Project Exercise 2: Querying and Displaying Data from a Database 4 5
Lab Introduction
In this lab, you will be accessing database data by using the Entity Framework. You will add ADO.NET Entity Data Models to the GreetingCardManagement solution that is used by employees of the site. In the GreetingCardManagement solution, you will implement read-only data access for reporting purposes, but you will customize the data access by modifying the default queries in the Domain Service class. You will develop this solution to include data-bound controls and grid views, along with data paging, to build a master-detail view of the registered senders in the site and the details of the cards that they have sent. The exercises for this lab are: Exercise 1: Connecting to a Database in a Silverlight Project Exercise 2: Querying and Displaying Data from a Database
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Save the changes. Hint: Refer to the topic, Customizing Domain Services Queries for an example of how to sort data.
Add the following property attribute immediately above the property you have just located.
[Visual Basic] <Include()>
Save the changes. Hint: Refer to the topic, Customizing Domain Services Queries for an example of how to use the Include function.
Task 3: Add data controls for senders data to the Silverlight application.
In the Views folder, open Home.xaml. Delete the Home TextBlock control. Delete the Home page content TextBlock control. Show the data sources for the GreetingCardManagement project. To show the data in master-details view, in the Data Sources window, select the details for the Sender entity. Drag the Sender item from the Data Sources window to the upper-left corner of Home.xaml in Design view. Delete the User ID grid row. Hint: Right-click the User ID label and then use the context menu to delete the row. In the Data Sources window, expand the Sender item, drag the Cards sub-item to Home.xaml in Design view and drop it below the Sender data you just added. Note: Ensure that you drag the Cards item that belongs to Sender, and not the top-level Card item. Open the editor for the columns collection of the DataGrid control for the Cards data. In the Collection Editor: Columns dialog box, remove the CardGuidColumn or cardGuidColumn, CardIDColumn or cardIDColumn, and SenderIDColumn or senderIDColumn DataGridTextColumn objects from the list on the left. Move up the RecipientEmailColumn or recipientEmailColumn DataGridTextColumn object on the list on the left. Show the Layout section for RecipientEmailColumn or recipientEmailColumn DataGridTextColumn. Set the following property value for RecipientEmailColumn or recipientEmailColumn DataGridTextColumn in the Layout section. Width: 300 Pixel
Show the Layout section for CardDataColumn or cardDataColumn DataGridTextColumn. Set the following property value for CardDataColumn or cardDataColumn DataGridTextColumn in the Layout section. Width: 295 Pixel
Close the Collection Editor: Columns dialog box, saving the changes. In the Properties window, set the following property values for the DataGrid control. Width: 600 HorizontalAlignment: Left VerticalAlignment: Top
Task 4: Add paging controls for senders data to the Silverlight application.
In the XAML window, locate the markup that begins with the following code.
<sdk:DataGrid
Insert a blank line immediately above the markup you have just located. Drag a DataPager control from the All Silverlight Controls section of the toolbox to the XAML window, where you just added a blank line. Review the markup for the DataPager control. A DataPager control is added to the XAML window, and to the design surface (between the Sender data and the Card grid). Arrange the data controls so that they resemble the following image.
From the Data Sources window, drag Sender and drop it on the DataPager control. Review the modified markup for the DataPager control and note that a Source element has been added that binds the DataPager to the Sender data. In the Properties window, set the following property values for the DataPager control. PageSize: 1 DisplayMode: FirstLastPreviousNextNumeric
Hint Refer to the Implementing Master-Detail Views of Data from Domain Services topic for details on how to bind a DataPager control. Save the changes.
Review the sender data that is displayed, and then use the DataPager control to navigate through the data. Note how Card Data updates when different Senders are displayed. Close Internet Explorer. Close Visual Studio.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 3
Lab Instructions: Advanced Data Management
Contents:
Exercise 1: Implementing Full Read-Write Scenarios 3
Lab Introduction
In this lab, you will be modifying database data by using the Entity Framework. You will add an ADO.NET Entity Data Model to the GreetingCardConsumer solution that is used by site visitors. In the GreetingCardConsumer solution, you will also modify the default queries in the Domain Service class, which will restrict the data to the currently logged on user. You will also implement write operations for the data to add newly registered site users as senders in the GreetingCard database. Finally, you will implement an edit-and-save scenario that enables the currently logged on user to make changes to their email address or display name, and have those changes persisted in the database. The exercises for this lab are: Exercise 1: Implementing Full Read-Write Scenarios
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
You should type this expression all on a single line. It represents a pattern match for a valid email address.
Hint You can copy the expression from the Email property, which is located just below the UserName property. In the GreetingCardConsumer.Web project, open the Resource Designer for the Resources\ValidationErrorResources.resx resource file. The Resource Designer opens displaying a three-column grid containing the resource strings in the ValidationErrorResources.resx resource file. In the row with a Name column value of ValidationErrorInvalidUserName, replace the content of the Value column with Invalid user name. You must use a valid email address. Save the changes. Close the Resource Designer.
Task 7: Edit the GetSenders Query in the Domain Service Class to Restrict Data to the Current User.
In the GreetingCardConsumerDomainService code file, modify the code in the GetSenders method so that it reads:
[Visual Basic] Dim userName As String = System.Web.HttpContext.Current.User.Identity.Name Return Me.ObjectContext.Senders.Include("Cards") _ .Where(Function(sndr) sndr.Email = userName)
[Visual C#] string userName = System.Web.HttpContext.Current.User.Identity.Name; return this.ObjectContext.Senders.Include("Cards") .Where(sndr => sndr.Email == userName);
Task 9: Add Data Controls for Senders Data to the Silverlight Application.
To the GreetingCardConsumer project, add a new Silverlight Page named Details to the Views folder. In the GreetingCardConsumer project, open MainPage.xaml. In MainPage.xaml, in the XAML view, locate the markup that begins <HyperlinkButton x:Name="Link2". Immediately above the located markup, add a new self-closing HyperlinkButton control element named Details. Set the following properties: Property Value Your Details /Details {StaticResource LinkStyle} ContentFrame
The NavigateUri property specifies that when a user clicks the HyperlinkButton control, it should navigate to the Details.xaml page, by using a relative uri (/Details). The TargetFrame property specifies that the content available at the NavigateUri should be displayed in the ContentFrame navigation frame. This is part of the navigation automatically provided when creating a Silverlight Business Application. Immediately below the DetailsHyperlinkButton control, add a new self-closing Rectangle control element named DetailsDivider. Set the following property: Property Name Property Value
In Details.xaml, in the XAML view, select all the markup, and then type in the following markup or copy from the D:\Labfiles\Mod03\VB\DetailsMarkup.txt or D:\Labfiles\Mod03\CS\DetailsMarkup.txt file, and paste it in:
[Visual Basic] <navigation:Page x:Class="GreetingCardConsumer.Details" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:navigation="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="Details Page" xmlns:riaControls="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:my="clr-namespace:GreetingCardConsumer" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <sdk:Page.Resources> <CollectionViewSource x:Key="SenderCardsViewSource" Source="{Binding Path=Data.Cards, ElementName=SenderDomainDataSource}" /> </sdk:Page.Resources> <Grid x:Name="LayoutRoot"> <riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:Sender, CreateList=true}" Height="0" Name="SenderDomainDataSource" QueryName="GetSendersQuery" Width="0"> <riaControls:DomainDataSource.DomainContext> <my:GreetingCardConsumerDomainContext /> </riaControls:DomainDataSource.DomainContext> </riaControls:DomainDataSource> <Grid DataContext="{Binding ElementName=SenderDomainDataSource, Path=Data}" HorizontalAlignment="Left" Margin="12,12,0,0" Name="Grid1" VerticalAlignment="Top" Height="66" Width="220"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <sdk:Label Content="Display Name:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="DisplayNameTextBox" Text="{Binding Path=DisplayName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Email:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="EmailTextBox" Text="{Binding Path=Email, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> </Grid> <sdk:DataGrid AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" ItemsSource="{Binding Source={StaticResource
SenderCardsViewSource}}" Margin="12,84,0,0" Name="CardsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="600"> <sdk:DataGrid.Columns> <sdk:DataGridTextColumn x:Name="RecipientEmailColumn" Binding="{Binding Path=RecipientEmail}" Header="Recipient Email" Width="300" /> <sdk:DataGridTextColumn x:Name="CardDataColumn" Binding="{Binding Path=CardData}" Header="Card Data" Width="295" /> </sdk:DataGrid.Columns> </sdk:DataGrid> </Grid> </navigation:Page>
[Visual C#] <navigation:Page x:Class="GreetingCardConsumer.Details" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:navigation="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="Details Page" xmlns:riaControls="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:my="clr-namespace:GreetingCardConsumer.Web" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <sdk:Page.Resources> <CollectionViewSource x:Key="SenderCardsViewSource" Source="{Binding Path=Data.Cards, ElementName=SenderDomainDataSource}" /> </sdk:Page.Resources> <Grid x:Name="LayoutRoot"> <riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:Sender, CreateList=true}" Height="0" Name="SenderDomainDataSource" QueryName="GetSendersQuery" Width="0"> <riaControls:DomainDataSource.DomainContext> <my:GreetingCardConsumerDomainContext /> </riaControls:DomainDataSource.DomainContext> </riaControls:DomainDataSource> <Grid DataContext="{Binding ElementName=SenderDomainDataSource, Path=Data}" HorizontalAlignment="Left" Margin="12,12,0,0" Name="Grid1" VerticalAlignment="Top" Height="66" Width="220"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <sdk:Label Content="Display Name:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="DisplayNameTextBox" Text="{Binding Path=DisplayName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Email:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="EmailTextBox" Text="{Binding Path=Email, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> </Grid>
<sdk:DataGrid AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" ItemsSource="{Binding Source={StaticResource SenderCardsViewSource}}" Margin="12,84,0,0" Name="CardsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="600"> <sdk:DataGrid.Columns> <sdk:DataGridTextColumn x:Name="RecipientEmailColumn" Binding="{Binding Path=RecipientEmail}" Header="Recipient Email" Width="300" /> <sdk:DataGridTextColumn x:Name="CardDataColumn" Binding="{Binding Path=CardData}" Header="Card Data" Width="295" /> </sdk:DataGrid.Columns> </sdk:DataGrid> </Grid> </navigation:Page>
In the GreetingCardConsumer project, add a reference to the System.Windows.Controls.Data and System.Windows.Controls.DomainServices assemblies. Build the solution.
Task 10: Develop Code to Add Newly Registered Users to the GreetingCard Database.
In the GreetingCardConsumer project, open Views\Login\RegistrationForm.xaml in Code view. In the RegistrationForm class, locate the RegisterButton_Click event handler. Append code to the If/if block in the event handler to create and instantiate an instance of the GreetingCardConsumerDomainContext class, create and instantiate an instance of the Sender class. Set the Email property of the sender instance to the value of the local registrationData.UserName, set the DisplayName property of the sender instance to the value of the local registrationData.FriendlyName and add the sender instance to the Senders collection property of the GreetingCardConsumerDomainContext instance. Finally, use the GreetingCardConsumerDomainContext instance, to submit the changes. Save the changes.
Insert an empty line above the markup that you located. Add the following markup to the space you have just created. You can type in the markup manually, or you can copy from the D:\Labfiles\Mod03\EditDetailsMarkup.txt file, and paste it in.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Border BorderBrush="Silver" BorderThickness="2" Height="70" Margin="10" Name="editDetails" Width="50" CornerRadius="10" VerticalAlignment="Top" Cursor="Hand"> <StackPanel Orientation="Vertical">
<Border CornerRadius="10,10,0,0" Height="50"> <Border.Background> <ImageBrush ImageSource="/GreetingCardConsumer;component/Assets/Images/HeadPhoto.png"/> </Border.Background> </Border> <ContentControl Height="20" Content="Edit" HorizontalAlignment="Center" Background="Silver"/> </StackPanel> </Border> <StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="10" HorizontalAlignment="Left" x:Name="editControls" Visibility="Collapsed"> <Button x:Name="saveChanges" Content="Save" Width="50" Margin="5" Cursor="Hand"/> <Button x:Name="cancelChanges" Content="Cancel" Width="50" Margin="5" Cursor="Hand"/> </StackPanel> </StackPanel>
The markup adds controls for editing the details, wrapped a StackPanel control, which contains controls for displaying the image you added previously, and controls for saving or cancelling the changes. Save the changes. Add an event handler for the MouseLeftButtonDown event of the editDetailsBorder control. Add code to the event handler to check if the name of the current user, WebContext.Current.User.Name, is null or empty. If so, create and display a new error window, using the ErrorWindow.CreateNew method, displaying the message, You must log in to edit your data, but never show the stack trace. In addition, exit the method, if the name of the current user is null or empty. Otherwise, enable the EmailTextBox or emailTextBox, and DisplayNameTextBox or displayNameTextBox controls, set the Visibility property of the editDetails control to a value of System.Windows.Visibility.Collapsed, and set the Visibility property of the editControls control to a value of System.Windows.Visibility.Visible. Switch back to the XAML view of Details.xaml. Add an event handler for the Click event of the saveChangesButton control. Add code to the event handler to use the SenderDomainDataSource or senderDomainDataSource object to submit the changes. Wrap the code in a Try...Catch...Finally or try...catch...finally construct, with just one general Catch or catch (Exception). If an exception is thrown, use the SenderDomainDataSource or senderDomainDataSource object to reject the changes, create and display a new error window, using the ErrorWindow.CreateNew method, displaying the message, The following problem occurred when attempting to save your changes, but never show the stack trace. To the error message, append content of the Message property of the caught exception. In the Finally or finally part, disable the EmailTextBox or emailTextBox, and DisplayNameTextBox or displayNameTextBox controls, set the Visibility property of the editDetails control to a value of System.Windows.Visibility.Visible, and set the Visibility property of the editControls control to a value of System.Windows.Visibility.Collapsed. Switch back to the XAML view of Details.xaml. Add an event handler for the Click event of the cancelChangesButton control.
10
Add code to the event handler to disable the EmailTextBox or emailTextBox, and DisplayNameTextBox or displayNameTextBox controls, set the Visibility property of the editDetails control to a value of System.Windows.Visibility.Visible, and set the Visibility property of the editControls control to a value of System.Windows.Visibility.Collapsed. Finally, use the SenderDomainDataSource or senderDomainDataSource object to reject the changes. Save the changes.
Close the dialog box. Go to Home page Log in by registering a new user with the following information. User name: GregG Friendly name: Greg Guzik Email: GregG@contoso.com Password: Pa$$w0rd Confirm password: Pa$$w0rd Security question: What is your pets name? Security answer: Lily
Note that you cannot register because the user name must be a valid email address. You set this validation earlier. Change the user name to GregG@contoso.com, and register You are registered through the built-in authentication services provided by the application, and you are logged into the application. Leave Internet Explorer running.
11
Switch back to Microsoft SQL Server Management Studio. On the Query menu, click Execute. The data refreshes and shows a new sender for the user you just registered in the Silverlight application.
Switch back to Internet Explorer. Go to Home page. Edit the details for the current user. Change the display name to Gregory, and save the change. Switch back to Microsoft SQL Server Management Studio. Rerun the top 1000 query. The data refreshes and shows the updated DisplayName for Gregory.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 4
Lab Instructions: Implementing User Controls and Navigation
Contents:
Exercise 1: Adding a Navigation User Control Exercise 2: Updating the Number of Cards Periodically 4 6
Lab Introduction
In this lab, in the GreetingCardManagement solution, you will be creating a new user control for containing the navigation controls displayed to the user. The controls and code from the main user control will be moved to the new user control, which will be inserted into the main user control. You will add markup and code to the Home page to retrieve and display the number of cards in the database, at a regular interval. In the GreetingCardManagement.Web solution, you will add a new method for returning the number of rows in the Cards entity. The exercises for this lab are: Exercise 1: Adding a Navigation User Control Exercise 2: Updating the Number of Cards Periodically
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Locate the constructor. Append the cut code. Save the changes.
Append the NavigationSidebar user control with default values to the NavigationGrid Grid control. Name the control NavigationSidebar.
<local:NavigationSidebar x:Name="NavigationSidebar" />
Switch to the MainPage.xaml.vb or MainPage.xaml.cs file. If you are developing in Visual C#, bring the GreetingCardManagement.Views namespace into scope. Locate the ContentFrame_Navigated event handler. In the ContentFrame_Navigated event handler, add code to create a new StackPanel control named linkStackPanel, and assign the value of the LinksStackPanel control in the NavigationSidebar user control. You can use the FindName method of the UIElement class. In the ContentFrame_Navigated event handler, in the For Each/foreach loop, change the name of the control being iterated from LinksStackPanel to linkStackPanel. The code in the top-level user control correctly changes the visual state of the HyperlinkButton controls, now contained in the child NavigationSidebar user control.
Build the solution. Run the application. Check that the navigation controls are still displayed in the upper-right corner. Close Internet Explorer.
In the GreetingCardManagement project, in the Views folder, open the Home.xaml code file. Locate the constructor. Append code to the constructor to create a new instance of the System.Windows.Threading.DispatcherTimer class. The timer will be used to regularly update the TextBlock control.
Append code to the constructor to create an anonymous event handler for the Tick event of the countTimer DispatcherTimer object. The event handler, which does not return a value, should create an instance of the GreetingCardManagementDomainContext domain context class. Name the instance gcmContext. Call the GetCardCount method on gcmContext, passing a callback method named OnGetCardCountCompleted and a null value as the only parameters. Finally, destroy the gcmContext object. Append code to the constructor to set the timer interval to 15 seconds and start the timer.
Append a new callback method named OnGetCardCountCompleted to the Home class. The method should accept a single parameter named op, of the generic type System.ServiceModel.DomainServices.Client.InvokeOperation, restricted to integers only. Add code to the callback method OnGetCardCountCompleted to call the SetCardCount method, which accepts an integer parameter. The method should check if the currently executing code is running on the UI thread by using the Dispatcher object, and subsequently call the SetCardCount method directly, or by using a new delegate named CardCountDelegate, which accepts a single integer parameter. Because the DispatcherTimer class is used, it is not necessary to check if the code is running on the current thread. However, you will now be able to use a different approach, such as using the System.Threading.Timer class instead by making simple adjustments to your code. The System.Threading.Timer class runs on a non-UI thread.
Append a new method named SetCardCount to the Home class. The method should accept a single integer parameter named cardCount. The Text property of the NoCardsTextBlock control should be updated with the text, Number of cards in database:, followed by the number of cards in the database, passed in the cardCount parameter. Build the solution. Run the application. Check that the number of cards, 501, is displayed after 15 seconds. Close Internet Explorer. Close all applications. You have now completed this lab.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 5
Lab Instructions: Creating Advanced User Interfaces
Contents:
Exercise 1: Creating ItemsControl Objects Exercise 2: Configuring Content Classes for use in the ItemsControl Class Exercise 3: Using the Custom ItemsControl object in a User Control
4
7 11
Lab Introduction
In this lab, you will create a complex container control that displays categories of greeting cards in a coverflow style. You will also develop template classes, event arguments, and dependency properties to show the greeting cards in a slick, professional way in the coverflow control. In addition, you will create a class that can be used to instantiate objects that are added to the coverflow controlwhen the user clicks or otherwise selects a category in the coverflow container, the exposed properties of those objects will be used to populate other user interface elements in an engaging way. The exercises for this lab are: Exercise 1: Creating ItemsControl Objects Exercise 2: Configuring Content Classes for use in the ItemsControl Class Exercise 3: Using the Custom ItemsControl object in a User Control
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
[Visual C#] public int Index { get; set; } public object Item { get; set; } public bool MouseClick { get; set; }
Save the changes. Hint Refer to the topic Creating Events for an ItemsControl Class in Lesson 1.
Hint
Add an event delegate named SelectedItemChangedEvent immediately above the class declaration you have just modified. Ensure that the delegate accepts a parameter type of CoverFlowEventArgs. In the CoverFlowControl class, declare a public event of type SelectedItemChangedEvent and name it SelectedItemChanged. Hint Refer to the Creating Events for an ItemsControl Class topic in Lesson 1.
Declare a class-level variable of type ItemsPresenter and name it ItemsPresenter. Declare a class-level variable of type ItemsPresenter and name it ItemsPresenter. Declare a generic class-level variable of type List restricted to CoverFlowItemControl objects and name it cfItems. Add the following procedure to manage changes when an item is selected.
[Visual Basic] Private Sub IndexSelected(ByVal index As Integer, ByVal mouseclick As Boolean, ByVal layoutChild As Boolean) If Items.Count > 0 AndAlso Items.Count > index Then selIndex = index If layoutChild Then LayoutChildren() End If Dim e As CoverFlowEventArgs = New CoverFlowEventArgs() With { .Index = index, .Item = cfItems(index).Content, .MouseClick = mouseclick } RaiseEvent SelectedItemChanged(e) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("SelectedIndex")) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("SelectedItem")) End If End Sub
[Visual C#] private void IndexSelected(int index, bool mouseclick, bool layoutChildren) { if ((Items.Count > 0) && (Items.Count>index)) { selectedIndex = index; if(layoutChildren) { LayoutChildren(); } CoverFlowEventArgs e = new CoverFlowEventArgs() { Index = index, Item = cfItems[index].Content, MouseClick = mouseclick };
if (SelectedItemChanged != null) { SelectedItemChanged(e); } if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("SelectedIndex")); PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem")); }
[Visual C#] public double RotationAngle { get { return (double)GetValue(RotationAngleProperty); } set { SetValue(RotationAngleProperty, value); } }
[Visual C#] public static readonly DependencyProperty RotationAngleProperty = DependencyProperty.Register("RotationAngle", typeof(double), typeof(CoverFlowControl), new PropertyMetadata(60d, new PropertyChangedCallback (CoverFlowControl.OnValuesChanged)));
Declare a class-level variable called ContentPresenter type of ContentControl. Add the following public event to the CoverFlowItemControl class.
[Visual Basic] Public Event ItemSelected As EventHandler
Add the following code directly beneath the comment you have just identified.
[Visual Basic] If Not IsNothing(ContentPresenter) Then AddHandler ContentPresenter.MouseLeftButtonUp, New MouseButtonEventHandler(AddressOf ContentPresenter_MouseLeftButtonUp) End If
Add the following event handler directly beneath the OnApplyTemplate procedure.
[Visual Basic] Sub ContentPresenter_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs) RaiseEvent ItemSelected(Me, Nothing) End Sub
ItemSelected(this, null);
[Visual C#] public string modTitle { get; set; } public string modToolTip { get; set; } public string modImageUrl { get; set; } public List<string> modMediaFileTitles { get; set; } public List<string> modMediaFileUrls { get; set; } public List<string> modMediaFileThumbnailPaths { get; set; } public List<string> modMediaFileTooltips { get; set; }
[Visual C#] public CardCategory(XElement xModule) { modMediaFileTitles = new List<string>(); modMediaFileUrls = new List<string>(); modMediaFileThumbnailPaths = new List<string>(); modMediaFileTooltips = new List<string>(); parseXML(xModule); }
Add the following procedure directly beneath the constructor you have just added.
[Visual Basic] Sub parseXML(ByVal xModule As XElement) If Not IsNothing(xModule.Element("THUMBNAIL")) Then Dim thumbNail As XElement = xModule.Element("THUMBNAIL") If Not IsNothing(thumbNail.Attribute("path")) Then modImageUrl = thumbNail.Attribute("path").Value End If If Not IsNothing(thumbNail.Attribute("tooltip")) Then modToolTip = thumbNail.Attribute("tooltip").Value End If End If If Not IsNothing(xModule.Element("CARDS")) Then Dim xMediaFile As XElement For Each xMediaFile In _ xModule.Element("CARDS").Descendants("CARD") If Not IsNothing(xMediaFile.Attribute("previewUrl")) Then modMediaFileUrls.Add _ (xMediaFile.Attribute("previewUrl").Value) Else modMediaFileUrls.Add(String.Empty) End If If Not IsNothing(xMediaFile.Attribute("previewThumb")) Then modMediaFileThumbnailPaths.Add _ (xMediaFile.Attribute("previewThumb").Value) Else modMediaFileThumbnailPaths.Add(String.Empty) End If If Not IsNothing(xMediaFile.Attribute("tooltip")) Then modMediaFileTooltips.Add _ (xMediaFile.Attribute("tooltip").Value) Else
10
modMediaFileTooltips.Add _ ("Tooltip has not been set in XML file") End If Next End If End Sub
[Visual C#] private void parseXML(XElement xModule) { if (xModule.Element("THUMBNAIL") != null) { XElement thumbNail = xModule.Element("THUMBNAIL"); if (thumbNail.Attribute("path") != null) { modImageUrl = thumbNail.Attribute("path").Value; } if (thumbNail.Attribute("tooltip") != null) { modToolTip = thumbNail.Attribute("tooltip").Value; } } if (xModule.Element("CARDS") != null) { foreach (XElement xMediaFile in xModule.Element("CARDS").Descendants("CARD")) { if (xMediaFile.Attribute("previewUrl") != null) { modMediaFileUrls.Add (xMediaFile.Attribute("previewUrl").Value); } else { modMediaFileUrls.Add(string.Empty); } if (xMediaFile.Attribute("previewThumb") != null) { modMediaFileThumbnailPaths.Add (xMediaFile.Attribute("previewThumb").Value); } else { modMediaFileThumbnailPaths.Add(string.Empty); } if (xMediaFile.Attribute("tooltip") != null) { modMediaFileTooltips.Add (xMediaFile.Attribute("tooltip").Value); } else { modMediaFileTooltips.Add ("Tooltip has not been set in XML file"); } } } }
11
Between the opening and closing tags of the MainGrid Grid element add the following markup.
<local:CoverFlowControl x:Name="flowControl" Margin="0,0,0,0" Width="600"> <local:CoverFlowControl.ItemTemplate> <DataTemplate> <Border CornerRadius="1"> <Image Source="{Binding modImageUrl}" CacheMode="BitmapCache" Grid.Row="0" Height="130" ToolTipService.ToolTip="{Binding modToolTip}"/> </Border> </DataTemplate> </local:CoverFlowControl.ItemTemplate> </local:CoverFlowControl>
Add the following code directly below the comment you have just located.
[Visual Basic] For Each xModule As XElement in xDoc.Descendants("CATEGORY") Dim cardCat As New CardCategory(xModule) categories.Add(cardCat) Next
[Visual C#] foreach (XElement xModule in xDoc.Descendants("CATEGORY")) { CardCategory cardCategory = new CardCategory(xModule); categories.Add(cardCategory); }
Build the solution. Press Ctrl+F5 to run and test the application.
12
The application loads categories of cards in the CoverFlowControl that you created in this lab. Each category displays a number of different cards, as specified by the GreetingCardConfig.xml file. Click each of the card categories in the CoverFlowControl to see how the contents of each category are updated. The contents of each category are represented by the XML file, and you exposed the relevant data as properties of the CardCategory class to update the other components in the user interface. Close all applications.
Lab Shutdown
After finishing the lab, you must shutdown the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 6
Lab Instructions: Using Local Assets
Contents:
Exercise 1: Printing from Silverlight Applications Exercise 2: Accessing the Clipboard in Silverlight Applications Exercise 3: Accessing Isolated Storage in Silverlight Applications Exercise 4: Accessing the File System in Silverlight Applications 4 6 8 10
Lab Introduction
In this lab, you will add various features to the GreetingCardConsumer project and the GreetingCardManagement project. You will start by adding custom multi-page printing support to the GreetingCardConsumer project, and then develop that project by adding copy and paste clipboard operations. In addition, you will implement multi-session isolated storage in the GreetingCardConsumer project to enable the user to choose a default card category. Finally, you will develop the GreetingCardManagement project as an out-of-browser elevated trust application. The application will read and write diagnostic information in files on the local file system, for use in a customer-support instant messaging scenario. The exercises in this lab are: Exercise 1: Printing from Silverlight Applications Exercise 2: Accessing the Clipboard in Silverlight Applications Exercise 3: Accessing Isolated Storage in Silverlight Applications Exercise 4: Accessing the File System in Silverlight Applications
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Additionally, the GreetingCardManagement project will include the ability to read and write diagnostic information in files on the local file system. Note Step-by-step instructions for completing the labs in this course are available in the lab answer keys provided. The completed working code is available in the Solution folders under the Lab files folder for each lab exercise on the virtual machine.
Locate the print_Click event handler that Visual Studio created for you. Add code to create and instantiate a new PrintDocument class named docToPrint. Create a new generic EventHandler, constrained to PrintPageEventArgs objects, and assign it to the PrintPageevent of the PrintDocument object, passing the docToPrint_PrintPage method (not yet created). Finally, call the Print method of the PrintDocument object, passing the text, Silverlight Document. Save the changes.
Save the changes. Run the application. In Internet Explorer, click Print.
In the Select Location in OneNote dialog box, click OK. OneNote displays a page for each card category in the Silverlight application.
The contents of the clipboard are pasted into the instant messaging text box. Open Notepad. Paste the copied content into Notepad. The contents of the clipboard are pasted into Notepad. Close Notepad, not saving the changes, and then close Internet Explorer.
Create an event handler for the Set Default button by using the Design view of Home.xaml. Visual Studio creates the snapshot_Click event handler for the Click event of the button.
Add code to the snapshot_Click event handler to check if the storage variable contains a setting named currentCategory. If it does, set the currentCategory setting to the value of flowControl.SelectedIndex; otherwise, add the currentCategory setting with a value of flowControl.SelectedIndex. Save the changes. Hint Refer to the Persisting Data in Isolated Storage topic in Lesson 2.
10
[Visual Basic] Try Dim filePathName As String = _ System.IO.Path.Combine(Environment.GetFolderPath( _ Environment.SpecialFolder.MyDocuments), _ "GreetingCardDiagnostics.txt") If (File.Exists(filePathName)) Then File.Delete(filePathName) End If Dim fileContents As StreamWriter = File.CreateText(filePathName) Dim displayName As String = "Your Display Name is: " & _ displayNameTextBox.Text Dim email As String = "Your Email Address is: " & _ emailTextBox.Text fileContents.WriteLine(displayName) fileContents.WriteLine(email) fileContents.Close() Catch secEx As System.Security.SecurityException ErrorWindow.CreateNew(New Exception( _ "Only out-of-browser applications can save files")) Catch ex As Exception ErrorWindow.CreateNew(ex) End Try
[Visual C#] try { string filePathName = System.IO.Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), "GreetingCardDiagnostics.txt"); if (File.Exists(filePathName)) File.Delete(filePathName); StreamWriter fileContents = File.CreateText(filePathName); string displayName = "Your Display Name is: "
11
+ displayNameTextBox.Text; string email = "Your Email Address is: " + emailTextBox.Text; fileContents.WriteLine(displayName); fileContents.WriteLine(email); fileContents.Close();
} catch (System.Security.SecurityException secEx) { ErrorWindow.CreateNew(new Exception ("Only out-of-browser applications can save files")); } catch (Exception ex) { ErrorWindow.CreateNew(ex); }
[Visual C#] try { string filePathName = System.IO.Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), "GreetingCardDiagnostics.txt"); if (!File.Exists(filePathName))
12
} catch (System.Security.SecurityException secEx) { ErrorWindow.CreateNew(new Exception ("Only out-of-browser applications can open files")); }
13
Review the content that is added to the instant messaging text box. It should be the same content you saw in the diagnostic file. Close all open applications, including Visual Studio.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 7
Lab Instructions: Implementing Advanced Media Techniques in Silverlight
Contents:
Exercise 1: Adding a Deep Zoom Image Exercise 2: Adding a Media Player 3 5
Lab Introduction
In this lab, you will work on a Greenfield project that will ultimately be added to the GreetingCard online presence. In addition to the somewhat static e-greeting cards, the company also wants offer the customers to be able to zoom in and out of the images used, and offer videos as greeting cards. This means you will create a deep zoom image as part of the solution. This will allow the user to see multiple images displayed as a fast loading single image with zoom and pan capabilities. In addition, you will create a media player that will play greeting videos. The exercises in this lab are: Exercise 1: Adding a Deep Zoom Image Exercise 2: Adding a Media Player
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Note Step-by-step instructions for completing the labs in this course are available in the lab answer keys provided. The completed working code is available in the Solution folders under the Lab files folder for each lab exercise on the virtual machine.
Exported Data\deepzoomimage folder, to the GreetingCardGreenfield.Web project, by using Windows Explorer. Close Windows Explorer and switch to Visual Studio. In the MainPage.xaml window, change the Grid control to a StackPanel control with no property values set explicitly. Add a MultiScaleImage control named, dzMultiScaleImage, to the StackPanel control. Set the Source property to http://localhost:26982/GeneratedImages/dzc_output.xml. The port number is most likely different on your setup, so use the Project Designer for the GreetingCardGreenfield.Web project to find the actual port number. If the port number is different than 26982, change the number in markup after localhost:. Build the solution. The deep zoom images will display in the Design view. Run the application to see the results. Close Internet Explorer.
If the port number is different than 26982, change the number in markup after localhost:. You checked the port number in the previous exercise.
Add code to the stopButton_Click event handler to call theStop method of the MediaElement control. Add code to the pauseButton_Click event handler to call the Pause method of the MediaElement control. Add code to the playButton_Click event handler to call the Play method of the MediaElement control. Run the application and demonstrate the video controls. Close Internet Explorer. Close Visual Studio. You have now completed this lab.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 8
Lab Instructions: Developing Silverlight Media Framework Solutions
Contents:
Exercise 1: Adding Support for the Silverlight Media Framework Exercise 2: Configuring the Silverlight Media Framework Player 4 6
Lab Introduction
In this lab, you will create a media player based on the Silverlight Media Framework. The media player will be embedded in the GreetingCardConsumer application with which you have already been working, and will support advanced features such as closed captions and custom full-screen capabilities. The exercises in this lab are: Exercise 1: Adding Support for the Silverlight Media Framework Exercise 2: Configuring the Silverlight Media Framework Player
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Build the solution. Hint Refer to the Referencing Silverlight Media Framework DLLs topic in Lesson 1.
Place the cursor at the end of the line you have just located, and then press Enter. On the empty line you have just created, add the following namespace declaration.
xmlns:SMF="clr-namespace:Microsoft.SilverlightMediaFramework.Core; assembly=Microsoft.SilverlightMediaFramework.Core"
Note
Ensure you add the preceding namespace declaration all on a single line.
Switch to Code view. Import the following namespaces in the code file: Microsoft.SilverlightMediaFramework.Core Microsoft.SilverlightMediaFramework.Core.Media Microsoft.SilverlightMediaFramework.Plugins. Primitives
To add a media player to the About page, wrapped in a Canvas element, add the following markup directly beneath the comment that you have just located.
<Canvas x:Name="videoPlayer" Canvas.ZIndex="0" Width="350" Height="310"> <Canvas.RenderTransform> <TranslateTransform x:Name="videoPlayerPosition" X="10" Y="10"></TranslateTransform> </Canvas.RenderTransform> <SMF:SMFPlayer x:Name="xVideoPlayer" AutoLoad="False" AutoPlay="False" PlaylistVisibility="Disabled" ScriptableName="VideoPlayer" CaptionsVisibility="Visible" ChaptersVisibility="Disabled" LoggingConsoleVisibility="Disabled" PlayerGraphVisibility="Disabled" VersionInformationVisibility="Disabled" AllowFullScreenPinning="True" Width="350" Height="310"/> </Canvas>
Add the following code directly beneath the comment you have just located.
[Visual Basic] Public Shared vidToResize As SMFPlayer
Public Shared vidToMove As TranslateTransform Public Shared vidContainer As Canvas Public Shared Sub PositionVideo() Dim thisVid As SMFPlayer = App.vidToResize Dim thisVidPos As TranslateTransform = App.vidToMove Dim thisVidContainer As Canvas = App.vidContainer If Application.Current.Host.Content.IsFullScreen = False Then ' Reset to default position thisVidContainer.Height = 310 thisVid.Height = 310 thisVidContainer.Width = 350 thisVid.Width = 350 thisVidPos.X = 10 thisVidPos.Y = 10 Return End If ' Make the video fill the screen thisVid.Height = _ Application.Current.Host.Content.ActualHeight - 100 thisVid.Width = _ Application.Current.Host.Content.ActualWidth - 5 thisVidContainer.Height = _ Application.Current.Host.Content.ActualHeight - 100 thisVidContainer.Width = _ Application.Current.Host.Content.ActualWidth - 5 thisVidPos.X = 0 thisVidPos.Y = 0 End Sub
[Visual C#] public static SMFPlayer vidToResize; public static TranslateTransform vidToMove; public static Canvas vidContainer; public static void PositionVideo() { SMFPlayer thisVid = App.vidToResize; TranslateTransform thisVidPos = App.vidToMove; Canvas thisVidContainer = App.vidContainer; if (!Application.Current.Host.Content.IsFullScreen) { // Reset to default position thisVidContainer.Height = 310; thisVid.Height = 310; thisVidContainer.Width = 350; thisVid.Width = 350; thisVidPos.X = 10; thisVidPos.Y = 10; } return;
// Make the video fill the screen thisVid.Height = Application.Current.Host.Content.ActualHeight - 100; thisVid.Width =
Open About.xaml in Code view. Add the following code to the Page_Loaded or About_Loaded event handler.
[Visual Basic] App.vidToResize = xVideoPlayer App.vidContainer = videoPlayer App.vidToMove = videoPlayerPosition App.PositionVideo() AddHandler xVideoPlayer.FullScreenChanged, _ AddressOf xVideoPlayer_FullScreenChanged
[Visual C#] App.vidToResize = xVideoPlayer; App.vidContainer = videoPlayer; App.vidToMove = videoPlayerPosition; App.PositionVideo(); xVideoPlayer.FullScreenChanged += new EventHandler(xVideoPlayer_FullScreenChanged);
[Visual C#] string videoUri = App.Current.Resources["videoPath"].ToString(); xVideoPlayer.CurrentPlaylistItem = null; xVideoPlayer.Playlist.Clear(); PlaylistItem currentVid = new PlaylistItem(); currentVid.MediaSource = new Uri(videoUri, UriKind.Absolute); currentVid.DeliveryMethod = DeliveryMethods.ProgressiveDownload;
MarkerResource vidMarkerResource = new MarkerResource(); vidMarkerResource.Format = "TTAF1-DFXP"; vidMarkerResource.Source = new Uri(captionUri, UriKind.Absolute);
10
currentVid.MarkerResource = vidMarkerResource; } catch { // Silent failure if captions are missing // Video will play without captions. }
Save the changes. Start the application. Click About. The video plays and displays the captions from the caption file.
Near the lower part of the video player, click CC. The captions are hidden.
Click CC to turn the captions back on. Near the lower-right of the video player, click the full screen button. In the Microsoft Silverlight dialog box, click Yes. The application switches to full screen view and the video is resized and positioned to occupy most of the screen. You wrote the code to position and resize the video in this lab.
Switch back to non-full screen view. The application switches out of full screen view and the video is resized and positioned to occupy its original location. You wrote the code to position and resize the video in this lab.
11
Lab Shutdown
After finishing the lab, you must shutdown the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 9
Lab Instructions: Accessing Hardware in Silverlight Applications
Contents:
Exercise 1: Interacting with the Mouse Wheel Exercise 2: Interacting with the Keyboard 3 5
Lab Introduction
In this lab, you will develop a solution that interacts with hardware such as mouse wheels and keyboard keys. The exercises for this lab are: Exercise 1: Interacting with the mouse wheel Exercise 2: Interacting with the keyboard
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Event handlers that enable the user to use keyboard navigation keys to scroll through the CoverFlowControl of card categories.
Add code directly beneath the comment you have just located, to attach the following event handlers to the corresponding event of the specified object, by using the AttachEvent method. This will help ensure the mouse wheel works on different platforms than Windows, and different browsers than Internet Explorer. Object Window Window Document Event Name DOMMouseScroll onmousewheel onmousewheel Event Handler OnMouseWheel OnMouseWheel OnMouseWheel
Overload the private event handler named, OnMouseWheel, with the usual event handler signature, accepting the sender argument of type Object/object, and args argument of type HtmlEventArgs. Add code to suppress the events by calling the PreventDefault method of the args argument. This will help ensure the mouse wheel works on different platforms than Windows, and different browsers than Internet Explorer. Save the changes.
b.
Create a variable named, index, of type Integer/int, and assign the value of the flowControl.SelectedIndex property, plus the value of the variable i. This means the control will navigate one cover for each mouse scroll. Check if the value of index is less than 0, and if it is, call the First method of the flowControl object. If not, check if index is larger than the value of the flowControl.Items.Count property, minus 1. If it is, call the Last method of the flowControl object. If not, set the flowControl.SelectedIndex property to the value of the index variable. This ensures the selected cover will never be less than 0 or higher than the last cover.
c.
[Visual C#] public void OnKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Right || e.Key == Key.Down || e.Key == Key.PageDown) { NextItem(); e.Handled = true; } else if (e.Key == Key.Left || e.Key == Key.Up || e.Key == Key.PageUp) { PreviousItem(); e.Handled = true; } else if (e.Key == Key.Home || e.Key == Key.Q) { First(); e.Handled = true; } else if (e.Key == Key.End || e.Key == Key.E) { Last(); e.Handled = true; } }
Locate the IndexSelected method that accepts three arguments, and add code at the top of the method, to set focus to the current object. Save the changes.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 10
Lab Instructions: Globalization and Localization
Contents:
Exercise 1: Globalizing an Application Exercise 2: Localizing an Application 3 6
Lab Introduction
In this lab you will globalize and localize the existing GreetingCardConsumer application, to make it ready for international customers. The exercises in this lab are: Exercise 1: Globalization Exercise 2: Localization
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Note Step-by-step instructions for completing the labs in this course are available in the lab answer keys provided. Completed working code is available in the Solution folders under the Lab files folder for each lab exercise on the virtual machine.
The sealed ResourceWrapper class is defined in the ResourceWrapper.vb or ResourceWrapper.cs file, located in the Helpers folder. The file is automatically generated when you create a Silverlight by using the Silverlight Business Application project template. In the GreetingCardConsumer project, in the Helpers folder, open ResourceWrapper.vb or ResourceWrapper.cs. In the ResourceWrapper.vb or ResourceWrapper.cs file, notice that two read-only properties exist, ApplicationStrings and SecurityQuestions. These two properties return the following shared/static member fields: _applicationStrings or applicationStrings and _securityQuestions or securityQuestions. They are type ApplicationStrings and SecurityQuestions. In the GreetingCardConsumer project, in the Assets\Resources folder, open ApplicationStrings.resx. The Resource Designer opens. In the Resource Designer, notice the Name column, which holds the resource key, used for looking up a specific value, which is shown the Value column. One example is the ApplicationName key, which has a current value of Greeting Card Consumer. In the GreetingCardConsumer project, open MainPage.xaml. In the MainPage.xaml window, in the XAML view, locate the TextBlock control named ApplicationNameTextBlock.
<TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}" Text="{Binding ApplicationStrings.ApplicationName, Source={StaticResource ResourceWrapper}}"/>
Notice that the Text property is bound to the ApplicationStrings.ApplicationName property, found in the ResourceWrapper resource. When you build the application, the ApplicationStrings class is automatically generated by the resource compiler, from the resource files, .resx, making it accessible from within your application. The two HyperlinkButton controls have also been bound to properties in the ApplicationStrings class, so at this stage, MainPage.xaml needs no further attention.
Close MainPage.xaml.
In the StackPanel control, locate the copyButton control, and notice the Content property holds a static or hardcoded string, Copy. The same is true for the print and snapshotButton controls.
<Button Grid.Row="1" Grid.Column="1" Content="Copy" Height="23" HorizontalAlignment="Left" Name="copy" VerticalAlignment="Top" Width="75" />
Add a resource file named HomeStrings.resx to the Assets/Resources folder. Make the resources publicly available. Set the custom tool namespace for the HomeStrings.resx file to an empty string (Visual Basic) or GreetingCardConsumer (Visual C#), by using the Properties window. Build the solution. Switch back to the Resource Designer. Add a new resource string named CopyButton with a value of Copy. Add a new resource string named PrintButton with a value of Print. Add a new resource string named SnapShotButton with a value of Set Default. Build the solution. Switch to ResourceWrapper.vb or ResourceWrapper.cs. Create a new shared/static member level field named _homeStrings or homeStrings of type HomeStrings. Create a new readonly property named HomeStrings of type HomeStrings. The property must return the shared/static member variable _homeStrings or homeStrings. Switch to Home.xaml. In the XAML view, modify the Content property of the copyButton control so it contains the following value:
{Binding HomeStrings.CopyButton, Source={StaticResource ResourceWrapper}}
In the XAML view, modify the Content property of the printButton control so it contains the following value:
{Binding HomeStrings.PrintButton, Source={StaticResource ResourceWrapper}}
In the XAML view, modify the Content property of the snapshotButton control so it contains the following value:
{Binding HomeStrings.SnapShotButton, Source={StaticResource ResourceWrapper}}
Run the application. Check to see if the button captions contain the text from the resource files. Close Internet Explorer.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 11
Lab Instructions: Implementing Network Communications
Contents:
Exercise 1: Consuming Initiation Parameters Exercise 2: Consuming Data by Using HTTPWebRequest Objects Exercise 3: Consuming Data by Using WebClient Objects Exercise 4: Sending and Receiving Data by Using Local Connections 3 5 8 10
Lab Introduction
In this lab, you will put into practice many of the concepts you have learned in this module. You will continue working with the Greeting Card scenario and you will implement initiation parameters that point to XML configuration files. You will retrieve those URIs at run time and you will then use both the HttpWebRequest and the WebClient objects to connect to and consume the data in the XML files. Finally, you will implement local connections for instant-messaging style communications between the GreetingCardConsumer application and the GreetingCardManagement application. The exercises for this lab are: Exercise 1: Consuming Initiation Parameters Exercise 2: Consuming Data by Using the HttpWebRequest Objects Exercise 3: Consuming Data by Using the WebClient Objects Exercise 4: Sending and Receiving Data by Using Local Connections
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
some features already completed, but they do not have initiation parameters or code for communicating with XML files or each other. You will add these required features as part of the lab.
Add a new line immediately after the located markup. Add the following markup to the empty line you have just created.
<param name="initParams" value="configXmlFileUri=http://localhost:2234/ClientBin/GreetingCardConfig.xml,colorXmlF ileUri=http://localhost:2234/ClientBin/GreetingCardColors.xml" />
Note
Save the changes. In the GreetingCardConsumer.Web project, in the ClientBin folder, open GreetingCardColors.xml. You will consume this data in Exercise 2.
In the GreetingCardConsumer.Web project, in the ClientBin folder, open GreetingCardConfig.xml. Hint The files are located in the ClientBin folder of the Web application.
Task 2: Retrieve the initiation parameters at run time and add them to the application Resources collection.
In the GreetingCardConsumer project, open App.xaml in Code view.
Add code directly beneath the comment that checks if e.InitParams is not null, and if so, creates a new local variable name item of type Object/object. Subsequently, loop through the items in the e.InitParams collection and add them to the Resources collection by using the Add method, passing the Key and Value properties of the current item. Save the changes. Hint Refer to the Consuming and Storing Initiation Parameters topic in Lesson 1 of this module for an example of storing initiation parameters in the Resources collection.
Directly beneath the comment you have just located, add code to create the following local variables, assigned specific initial values. Variable Name colorSettingsUri Variable Type Initial Value String/string App.Current.Resources("colorXmlFileUri"). ToString() / App.Current.Resources["colorXmlFileUri"]. ToString() App.Current.Resources("configXmlFileUri"). ToString() / App.Current.Resources["configXmlFileUri"]. ToString()
categorySettingsUri
String/string
Add code immediately after the previously added code, to show the value of the colorSettingsUri and categorySettingsUri variables, each in a separate message box. Save the changes. Hint Refer to the Consuming and Storing Initiation Parameters topic in Lesson 1 of this module for an example of assigning values from the Resources collection to string variables.
Run the application. Two message boxes should be displayed showing the URLs you will use later in the lab. Take a note of these URLs.
In the Home_Loaded or Page_Loaded event handler, append code to call the BeginGetResponse method of the httpRequest object. Pass in a new AsyncCallback object with the httpRequest_CallBack method and the httpRequest object. Hint Refer to the Initiating Communication by Using HttpWebRequest Objects topic in Lesson 2 of this module.
[Visual C#] void httpRequest_CallBack(IAsyncResult result) { HttpWebRequest httpRequest = (HttpWebRequest) result.AsyncState; HttpWebResponse httpResponse = (HttpWebResponse) httpRequest.EndGetResponse(result); using (StreamReader strmReader = new StreamReader(httpResponse.GetResponseStream())) { settings = strmReader.ReadToEnd();
} } Dispatcher.BeginInvoke(setColors);
Important You must call the procedure the same name as you specified when you wrote code to call the HttpWebRequests BeginGetResponse method. Also, refer to the Receiving Replies to HttpWebRequest Object Calls topic in Lesson 2 of this module to see an example of the procedure signature. Hint Refer to the Managing HttpWebRequest Objects and Threading Issues topic in Lesson 2 of this module to see how to switch execution back to the main UI thread.
Task 3: Create a function that updates the user interface from the main UI thread.
Add the following method after the method you have just added.
[Visual Basic] Sub setColors() Dim xDoc As XDocument = XDocument.Parse(settings) Dim root As XElement = xDoc.Root Dim bannerColor As String = String.Empty Dim backgroundColor As String = String.Empty Dim playerSettings As XElement = root.Element("PLAYERSETTINGS") Dim colorSettings As XElement = _ playerSettings.Element("COLORSETTINGS") If Not IsNothing(colorSettings.Attribute("bannerColor")) Then bannerColor = colorSettings.Attribute("bannerColor").Value End If If Not IsNothing(colorSettings.Attribute("backgroundColor")) Then backgroundColor = _ colorSettings.Attribute("backgroundColor").Value End If Dim bannerCol As SolidColorBrush = _ New SolidColorBrush(makeColor(bannerColor)) Dim backgroundCol As SolidColorBrush = _ New SolidColorBrush(makeColor(backgroundColor)) App.NavigationGrid.Background = bannerCol App.LayoutRoot.Background = backgroundCol End Sub
[Visual C#] void setColors() { XDocument xDoc = XDocument.Parse(settings); XElement root = xDoc.Root; string bannerColor = string.Empty; string backgroundColor = string.Empty; XElement playerSettings = root.Element("PLAYERSETTINGS"); XElement colorSettings = playerSettings.Element("COLORSETTINGS"); if (colorSettings.Attribute("bannerColor") != null) { bannerColor = colorSettings.Attribute("bannerColor").Value; }
if (colorSettings.Attribute("backgroundColor") != null) { backgroundColor = colorSettings.Attribute("backgroundColor").Value; } SolidColorBrush bannerCol = new SolidColorBrush(makeColor(bannerColor)); SolidColorBrush backgroundCol = new SolidColorBrush(makeColor(backgroundColor)); App.NavigationGrid.Background = bannerCol; App.LayoutRoot.Background = backgroundCol;
Review the function named makeColor that already exists in the class in which you have been working. Your code calls this function to convert the hexadecimal string data into a .NET color. Silverlight does not have methods for converting hexadecimal string data into a .NET color, so this function has been provided for you as part of the starter solution.
Save the changes. Run and test the application. The section at the top of the Silverlight application is now orange and the background of the main section is grey.
Hint Refer to the Initiating Communication by Using WebClient Objects topic in Lesson 2 of this module. Save the changes.
[Visual Basic] Sub webClient_DownloadStringCompleted(ByVal sender As Object, _ ByVal e As DownloadStringCompletedEventArgs) If Not IsNothing(e.Error) Then MessageBox.Show("There was an error retrieving xml") Return End If Dim xDoc As XDocument = XDocument.Parse(e.Result) Dim root As XElement = xDoc.Root Dim appTitle As String = String.Empty If Not IsNothing(root.Attribute("title")) Then appTitle = root.Attribute("title").Value App.ApplicationNameTextBlock.Text = appTitle End If End Sub
[Visual C#] void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show("There was an error retrieving xml"); } return;
XDocument xDoc = XDocument.Parse(e.Result); XElement root = xDoc.Root; string appTitle = string.Empty; if (root.Attribute("title") != null) { appTitle = root.Attribute("title").Value; App.ApplicationNameTextBlock.Text = appTitle; }
Save the changes. Run and test the application. The application displays the text Greeting Card! This text was retrieved from the GreetingCardConfig.xml file. You will work with the rest of the XML data that was retrieved in subsequent labs.
10
Create an event handler for the KeyUp event of the imContent TextBox control. Visual Studio creates an event handler stub for the KeyUp event.
Leave the current instance of Visual Studio running. Open a second instance of Visual Studio. A second instance of Visual Studio appears.
Open the GreetingCardManagement solution from the D:\Labfiles\Mod11\VB\Starter or D:\Labfiles\Mod11\CS\Starter folder. In the GreetingCardManagement project, in the Views folder, open About.xaml. In the XAML pane, locate the StackPanel element. Add the following markup between the opening and closing tags of the StackPanel element, replacing the existing content.
<TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}" Text="Instant Communications"/> <TextBox x:Name="imContent" Width="400" Height="200" HorizontalAlignment="Left"></TextBox> <TextBlock x:Name="imReceived" Width="400" Height="200" HorizontalAlignment="Left"></TextBlock>
Task 2: Add code for sending and receiving messages to the GreetingCardConsumer application.
Switch to the instance of Visual Studio that has the GreetingCardConsumer solution open. Ensure that you are viewing the code file for About.xaml. Bring the System.Windows.Messaging namespace into scope. Above the imContent_KeyUp event handler, declare the following member variables and instantiate them by passing the constructor values indicated. Name msgSender Type LocalMessageSender Constructor Value GreetingCardConsumer
11
Name msgReceiver
Type LocalMessageReceiver
Create a new event handler named msgReceiver_MessageReceived, with the standard event handler signature, accepting an argument named sender of type Object/object, and an argument named e of type MessageReceivedEventArgs. Place the event handler immediately below the imContent_KeyUp event handler. Add code to the imContent_KeyUp event handler to call the SendAsync method of the msgSender object, passing the value of the imContent.Text property. Add code to the OnNavigatedTo event handler to map an event handler named msgReceiver_MessageReceived to the MessageReceived method of the msgReceiver, and then call the Listen method of the msgReceiver object. Save the changes.
Task 3: Add code for sending and receiving messages to the GreetingCardManagement application.
Switch to the instance of Visual Studio that has the GreetingCardManagement solution open. Ensure that you are viewing the code file for About.xaml. Above the imContent_KeyUp event handler, declare the following member variables and instantiate them by passing the constructor values indicated. Name msgSender msgReceiver Type LocalMessageSender LocalMessageReceiver Constructor Value GreetingCardManagement GreetingCardConsumer
Note Note that these names are the opposite of the messages you defined in Task 2. This enables the two applications to communicate with each other over two well-defined connections. Build the solution.
12
Arrange the two Internet Explorer windows so that they are side-by-side. In the instance of Internet Explorer that has the Greeting Card Consumer application running, in the text box, type Help! Note Note that the text Help! is displayed in the GreetingCard Management application.
In the GreetingCard Management application, in the text box, type OK! Note Note that the text OK! is displayed in the GreetingCard Consumer application.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 12
Lab Instructions: Deploying Silverlight Applications
Contents:
Exercise 1: Adding Application Features Exercise 2: Loading Resources Dynamically 3 6
Lab Introduction
In this lab, you will put into practice some of the concepts you have learned in this module. You will continue working with the Greeting Card scenario and you will add toast notifications, for running out-ofbrowser, which appear whenever the network connection changes. In addition, you will enable assembly caching and load external resources on demand to reduce the size of the Silverlight application package and enable the application to start faster. The exercises for this lab are: Exercise 1: Adding Application Features Exercise 2: Loading Resources Dynamically
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
In the NetworkChangeNotification.xaml window, set the Width property of the Grid control to 200, and the Height property to 50. In the NetworkChangeNotification.xaml window, to the Grid control, add a self-closing TextBlock element, which is centered both horizontally and vertically. Bind the Text property, without specifying the exact binding. Save the changes. Switch to MainPage.xaml in Code view. [Visual C# only] Bring the GreetingCardConsumer.Views namespace into scope. Build the solution. To the OnNetworkChange method, append code to the If...Then/if construct that checks if the application is running out-of-browser to instantiate a new object of type NetworkChangeNotification, named nwWindow. Set the Content property of the notificationWnd object to the nwWindow object. To the OnNetworkChange method, append code to the If...Then/if construct that checks if the application is running out-of-browser to set the Content.DataContext property of the notificationWnd object to the message variable. This way, you can programmatically set the correct message to appear, without using different windows/user controls. In addition, set the Height and Width properties to the corresponding values of the LayoutRoot object of the nwWindow object. This will ensure that your notification window has the same size as the UserControl you just created. Note that you need to use the Height and Width of the LayoutRoot, which in this case is the Grid control in the UserControl, because the Height and Width of the UserControl is not accessible at this stage because the UserControl is not displayed. To the OnNetworkChange method, append code to the If...Then/if construct that checks if the application is running out-of-browser to show the notificationWnd object for 5 seconds, and then activate the MainWindow of the current application.
Notice the toast notification being displayed for 5 seconds as the network connection is now unavailable. In the Settings window, change the network adapter from Not connected to Private Network. Notice the toast notification being displayed for 5 seconds as the network connection is available again. Close the Settings window. Uninstall the application.
and pass the current object and a new System.Uri object with the following relative URI: /FabrikamLibrary;component/DynamicPage.xaml Locate the Home_Loaded or Page_Loaded event handler method. Append code to the Home_Loaded or Page_Loaded method to create and instantiate a variable named wClient, of type WebClient. Append the code to the existing If...Then/if construct. Append code to the Home_Loaded or Page_Loaded method to map the OpenReadCompleted event of the existing wClient object to the wClient_OpenReadCompleted method. Append the code to the existing If...Then/if construct. Append code to the Home_Loaded or Page_Loaded method to start the asynchronous download of the FabrikamLibrary.dll assembly by calling the OpenReadAsync method of the wClient object, passing a relative uri for the FabrikamLibrary.dll assembly. Run and test the application. Notice how the content of the Home page is replaced with the content loaded from the DynamicPage.xaml file, and how the styles are merged with the current project. Close all applications. You have now completed this lab.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 13
Lab Instructions: Application Guidance
Contents:
Exercise 1: Installing the Simple MVVM Toolkit Exercise 2: Examining a Silverlight MVVM project Exercise 3: Implementing MVVM Items 3 4 8
Lab Introduction
In this lab, you will put into practice some of the concepts you learned in this module. You will work on a new MVVM-based project, and modify it to display data from the GreetingCard database. The exercises for this lab are: Exercise 1: Installing the Simple MVVM Toolkit Exercise 2: Examining a Silverlight MVVM Project Exercise 3: Implementing MVVM Items
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Task 3: Register the Simple MVVM Toolkit project and item templates.
On the Start menu, click All Programs, expand Accessories, right-click Command Prompt, and then click Run as administrator. In the User Account Control dialog box, in the Password box, type Pa$$w0rd, and then click Yes. In the Administrator: Command Prompt box, at the prompt, type cd C:\Program Files\SimpleMvvmToolkit, and then press Enter. At the prompt, type SimpleMvvm-32wp.reg, and then press Enter. In the Registry Editor dialog box, click Yes. In the Registry Editor dialog box, click OK. Close the Administrator: Command Prompt box.
Close the HomeViewModel view-model. Open and examine the MainPageViewModel view-model, located in the ViewModels folder. Notice how the default constructor calls the RegisterToReceiveMessages method, inherited from the base class, to register or subscribe to messages that arrive on the MessageBus implementation. Only navigation token messages are subscribed to, by specifying the MessageTokens.Navigation constant value. This value is defined in the MessageTokens class, located in the Others folder. The Navigation value is currently the only value defined, but to extend the messaging system, you can add new constant values to this class.
Expand the collapsed regions. Notice the SelectedPage property declaration, located in the Properties region. This property and the corresponding backing member field are used to keep track of the current page in the navigation system implemented, and the view can bind to this property. You will see more of how navigation is implemented by using an event handler, methods, and the MainPage view later.
Notice the Navigate and OnNavigationRequested methods, both located in the Methods region. The latter should be familiar because it is the same event handler method you have seen throughout the implementations of the Silverlight Business Application and Silverlight Navigation Application. However, unlike the previous implementations, the OnNavigationRequested method calls the Navigate method, passing the page name, stored in the Message property of the NotificationEventArgs argument. This class is custom to the Simple MVVM Toolkit, and part of the messaging implementation.
Notice the NavigateCommand property, located in the Commands region. This property is used by the HyperlinkButton controls, used as part of the menu and user exposed navigation implementation, by way of binding. The DelegateCommand is custom to the Simple MVVM Toolkit, and used as the type for the NavigateCommand property.
The Source property of the Frame control used by the Silverlight navigation system is bound to the SelectedPage property of the MainPageViewModel. The HyperlinkButton control, named Link1, uses the Command and CommandParameter properties for navigation, instead of the NavigateUri property used by previous implementations of the MainPage.xaml view. The Command property binds to the NavigateCommand property of the MainPageViewModel property, passing the string Home, in the CommandParameter property.
Expression Blend. If not, then a new DeploymentCatalog object is created and passed to the CompositionHost.Initialize method. This is used with the Microsoft Extensibility Framework (MEF) for supporting dependency injection of the service agents. Without going into details beyond the scope of this course, it basically means that you can "inject" any type of service agent. Notice how a public property named AgentType, and the corresponding backing member variable, get or set the type of service agents used. The property returns a member of the AgentType enumeration, which contains three members: Mock, Real, and Unspecified. You use the values to specify whether you want to use the Mock or the Real service agents, allowing you to dynamically select if mock data or real data should be used. In the InjectedViewModelLocator class, notice the default constructor that also checks if the element or controls is currently running in the context of a designer. If not, the CompositionInitializer.SatisfyImports MEF method is used inject the current service agent object. In addition, the Debug.Assert method is used to throw an exception, if the HomeViewModel is not created correctly, and thus, the corresponding service agent and the correct agent type. The actual instantiation of the both of these objects happens when the Debug.Assert method is called, or when the HomeViewModel property, covered later, is bound to from the view. Notice the public and read-only property named, HomeViewModel, which creates and returns an instance of the HomeViewModel. If a corresponding service agent is found, it is created, and passed the HomeViewModel constructor. Notice the public and read-only property named MainPageViewModel, which returns an instance of the MainPageViewModel. The MainPageViewModel does not need a service agent, so the property code is straight-forward. Close the InjectedViewModelLocator code file.
[Visual C# only] Bring the System.Collections.Generic and MVVMGreetingCardManagement.Web.Models namespaces into scope in the ISendersServiceAgent.cs code file. These namespaces contain the argument types used in the method added next.
Add a method named, GetSenders, to the interface. The method should not return a value, but accept one argument named, completed. The argument must be of delegate type, Action, which accepts a generic List of type, Sender, and an Exception.
Save and close the ISendersServiceAgent code file. Implement the ISendersServiceAgent service agent interface in a new class named, SendersServiceAgent. [Visual C# only] Bring the MVVMGreetingCardManagement.Web.Services namespace into scope in the SendersServiceAgent.cs code file. This namespace contains the argument types used in the variable added next.
Bring the SimpleMvvmToolkit namespace into scope in the SendersServiceAgent code file. This namespace contains the attribute type applied to the class next. To the SendersServiceAgent class, apply the ServiceAgentExport attribute. This attribute is used to specify which type of service agents are exported, and the type of agent. The attribute should resemble the following code.
[Visual Basic] <ServiceAgentExport(GetType(ISendersServiceAgent), AgentType:=AgentType.Real)>
In the SendersServiceAgent class, add and instantiate a new private member variable named, gcDomainContext, of type, GreetingCardDomainContext. This variable will be used for accessing the Entity Data Model on the hosting server, by using the generated RIA Service code, available on the client.
Bring the System.ServiceModel.DomainServices.Client namespace into scope in the SendersServiceAgent code file. This namespace contains the argument types used in the method implemented next.
[Visual C# only] Bring the MVVMGreetingCardManagement.Web.Models, System.Linq, and System.Collections.Generic namespaces into scope in the SendersServiceAgent.cs code file. These namespaces contain the argument types used in the method implemented next.
In the SendersServiceAgent class, to the GetSenders method, add the following code (overwriting any existing code). The code creates a new entity query that calls the GetSenders query on the server (named GetSendersQuery on the client), by using the private gcDomainContext variable. Subsequently, the load operation is initiated and executed. If everything goes well, the retrieved collection of Sender objects is assigned to the completed argument callback.
[Visual Basic] Dim query As EntityQuery(Of Sender) = gcDomainContext.GetSendersQuery() ' Load GetSendersQuery gcDomainContext.Load(query, Sub(load) ' Declare error and result Dim err As Exception = Nothing Dim senders As IEnumerable(Of Sender) = Nothing ' Set error or result If load.HasError Then err = load.Error Else senders = load.Entities End If ' Invoke completion callback completed(senders.ToList, err) End Sub, Nothing)
[Visual C#] EntityQuery<Sender> query = gcDomainContext.GetSendersQuery(); // Load GetSendersQuery gcDomainContext.Load(query, load => { // Declare error and result Exception err = null; IEnumerable<Sender> senders = null; // Set error or result if (load.HasError) { err = load.Error; } else
10
{ }
senders = load.Entities;
11
Create an empty default constructor. [Visual C# only] Bring the MVVMGreetingCardManagement.Services namespace into scope in the SendersViewModel.cs code file. This namespace contains the argument types used in the nondefault constructor implemented next.
Create a non-default constructor that accepts a single argument of type, ISendersServiceAgent. The constructor must assign the passed service agent argument to the private member variable, serviceAgent, and call the LoadSenders method to be created later. The constructor is used by the view-model locator.
Append the following code to the SendersViewModel class. The Notifications region contains two public events, used to make the view aware of an error or when the sender objects have been loaded from the model.
[Visual Basic] #Region "Notifications" Public Event ErrorNotice As EventHandler(Of NotificationEventArgs(Of Exception)) Public Event SendersLoadedNotice As EventHandler(Of NotificationEventArgs) #End Region
[Visual C#] #region Notifications public event EventHandler<NotificationEventArgs<Exception>> ErrorNotice; public event EventHandler<NotificationEventArgs> SendersLoadedNotice; #endregion
Append the following code to the SendersViewModel class. The Properties region contains several public properties, all used by the view and the view-model for binding the two objects and managing when an operation can be performed. All properties ensure that any change to the property value is notified to those binding to the property, such as controls in the view.
[Visual Basic] #Region "Properties" Public Property Senders As ObservableCollection(Of Sender) Get Return Me.sndrs End Get Set(ByVal value As ObservableCollection(Of Sender)) Me.sndrs = value NotifyPropertyChanged(Function(m) m.Senders) End Set End Property Public Property SelectedSender As Sender Get Return Me.selSender End Get
12
Set(ByVal value As Sender) Me.selSender = value Me.CanLoad = Not IsBusy NotifyPropertyChanged(Function(m) m.SelectedSender) End Set End Property Public Property IsBusy As Boolean Get Return Me.busy End Get Set(ByVal value As Boolean) Me.busy = value Me.CanLoad = Not IsBusy NotifyPropertyChanged(Function(m) m.IsBusy) End Set End Property Public Property CanLoad As Boolean Get Return Me.ableToLoad End Get Set(ByVal value As Boolean) Me.ableToLoad = value NotifyPropertyChanged(Function(m) m.CanLoad) End Set End Property #End Region
[Visual C#] #region Notifications public event EventHandler<NotificationEventArgs<Exception>> ErrorNotice; public event EventHandler<NotificationEventArgs> SendersLoadedNotice; #endregion #region Properties public ObservableCollection<Sender> Senders { get { return this.sndrs; } set { this.sndrs = value; NotifyPropertyChanged(m => m.Senders); } } public Sender SelectedSender { get { return this.selSender; } set { this.selSender = value;
13
public bool IsBusy { get { return this.busy; } set { this.busy = value; this.CanLoad = !IsBusy; NotifyPropertyChanged(m => m.IsBusy); } } public bool CanLoad { get { return this.ableToLoad; } set { this.ableToLoad = value; NotifyPropertyChanged(m => m.CanLoad); } } #endregion
Append the following code to the SendersViewModel class. The Methods region contains a single method named, LoadSenders, which does as the name indicates; it uses the GetSenders method of the service agent to initiate and execute the loading of the senders from the model.
[Visual Basic] #Region "Methods" Public Sub LoadSenders() serviceAgent.GetSenders( _ Sub(entities, err) SendersLoaded(entities, err) End Sub ) ' Reset property Me.Senders = Nothing ' Flip busy flag Me.IsBusy = True End Sub #End Region
14
public void LoadSenders() { serviceAgent.GetSenders( (entities, err) => { SendersLoaded(entities, err); }); // Reset property this.Senders = null; // Flip busy flag this.IsBusy = true;
#endregion
[Visual C# only] Bring the System.Collections.Generic namespace into scope in the SendersViewModel.cs code file. This namespace contains the types used in the callback methods implemented next.
Append the following code to the SendersViewModel class. The Completion Callbacks region contains a single method named, SendersLoaded, which is asynchronously invoked from the LoadSenders method, described previously. The SendersLoaded method checks for errors, and notifies the view if an error occurred. If no error occurred, the Senders property is set, the SelectedSender is set to the first Sender object returned, and finally the view is notified that the loading of the sender objects has completed.
[Visual Basic] #Region "Completion Callbacks" Private Sub SendersLoaded(ByVal entities As List(Of Sender), ByVal err As Exception) ' If no error is returned, set the model to entities If err Is Nothing Then Senders = New ObservableCollection(Of Sender)(entities) Else NotifyError("Unable to retrieve sender", err) End If ' Set SelectedSender to the first item If Senders.Count > 0 Then SelectedSender = Senders(0) End If ' Notify view Notify(SendersLoadedNoticeEvent, New NotificationEventArgs("Senders were successfully loaded")) ' Finished IsBusy = False End Sub #End Region
15
private void SendersLoaded(List<Sender> entities, Exception err) { // If no error is returned, set the model to entities if (err == null) { Senders = new ObservableCollection<Sender>(entities); } else { NotifyError("Unable to retrieve sender", err); } // Set SelectedSender to the first item if (Senders.Count > 0) { SelectedSender = Senders[0]; } // Notify view Notify(SendersLoadedNotice, new NotificationEventArgs("Senders were successfully loaded")); // Finished IsBusy = false; } #endregion
Append the following code to the SendersViewModel class. The Helpers region contains a single method, NotifyError, which was described previously in Task 3 of Exercise 2.
[Visual Basic] #Region "Helpers" ' Helper method to notify View of an error Private Sub NotifyError(ByVal message As String, ByVal err As Exception) ' Notify view of an error Notify(Of Exception)(ErrorNoticeEvent, New NotificationEventArgs(Of Exception)(message, err)) End Sub #End Region
[Visual C#] #region Helpers // Helper method to notify View of an error private void NotifyError(string message, Exception err) { // Notify view of an error Notify<Exception>(ErrorNotice, new NotificationEventArgs<Exception>(message, err)); } #endregion
16
Immediately below the located markup, but above the closing StackPanel tag, add a new selfclosing HyperlinkButton control element named, SendersHyperlinkButton. The HyperlinkButton control is bound to the NavigateCommand property of the MainPageViewModel, which you examined in Exercise 2. The name of the view to which to
17
navigate is passed to the NavigateCommand property by setting the CommandParameter property of the HyperlinkButton. Set the following properties.
Property Name Content Command CommandParameter Style TargetName Property Value {Binding Path=ApplicationStrings.SendersPageTitle, Source={StaticResource ResourceWrapper}} {Binding NavigateCommand} SendersView {StaticResource LinkStyle} ContentFrame
[Visual C# only] Bring the MVVMGreetingCardManagement.Services namespace into scope in the InjectedViewModelLocator.cs code file. The namespace contain the types used in the properties added next.
18
Public ReadOnly Property SendersViewModel As SendersViewModel Get Dim serviceAgent = SendersServiceAgents _ .Where(Function(sa) sa.Metadata.AgentType = AgentType).FirstOrDefault() Dim viewModel As SendersViewModel = Nothing If Not serviceAgent Is Nothing Then viewModel = New SendersViewModel(serviceAgent.Value) ElseIf DesignerProperties.IsInDesignTool Then viewModel = New SendersViewModel() End If Return viewModel End Get End Property
[Visual C#] [ImportMany] public Lazy<ISendersServiceAgent, IServiceAgentMetadata>[] SendersServiceAgents { get; set; } public SendersViewModel SendersViewModel { get { var serviceAgent = SendersServiceAgents .Where(sa => sa.Metadata.AgentType == agentType).FirstOrDefault(); SendersViewModel viewModel = null; if (serviceAgent != null) viewModel = new SendersViewModel(serviceAgent.Value); else if (DesignerProperties.IsInDesignTool) viewModel = new SendersViewModel(); } return viewModel;
19
Lab Shutdown
After finishing the lab, you must shutdown the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 14
Lab Instructions: Windows Phone DevelopmentFirst Look
Contents:
Exercise 1: Capturing User Input Exercise 2: Responding to Orientation Changes Exercise 3: Adding Page Navigation Exercise 4: Monitoring Game Status Exercise 5: Sharing Data Between Pages 3 6 9 10 13
Lab Introduction
In this lab, you will put into practice some of the concepts you have learned in this module. You will work with a Tic Tac Toe game, where you will examine how to capture user input and ensure the application can respond to orientation changes. In addition, you will add navigation between the two application pages, and set up game monitoring by using a DispatcherTimer object. Finally, you will set up data sharing between pages by using a public property in the App class. The exercises for this lab are: Exercise 1: Capturing User Input Exercise 2: Responding to Orientation Changes Exercise 3: Adding Page Navigation Exercise 4: Monitoring Game Status Exercise 5: Sharing Data Between Pages
Lab Setup
For this lab, you will use the available virtual machine environment. Before you begin the lab, you must: Start the 10554A-SEA-DEV virtual machine, and then log on by using the following credentials: User name: Student Password: Pa$$w0rd
Copy the text by using the copy icon. Notice how the paste icon is displayed just above the SIP keyboard.
[Visual C#] <TextBox Height="73" HorizontalAlignment="Left" Margin="26,60,0,0" Name="player1TextBox" Text="enter name to begin" VerticalAlignment="Top" Width="272" GotFocus="player1TextBox_GotFocus" Grid.Row="1" />
Examine the textBlock1 control, which is a TextBlock control. In addition, notice the player2TextBox and textBlock2 controls that are used for player display and user input.
[Visual Basic] <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,24,0,0" Name="textBlock1" Text="Player 1" VerticalAlignment="Top" Grid.Row="1" /> <TextBox Height="73" HorizontalAlignment="Left" Margin="26,172,0,0" Name="player2TextBox" Text="Computer" VerticalAlignment="Top" Width="269" Grid.Row="1" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,136,0,0" Name="textBlock2" Text="Player 2" VerticalAlignment="Top" Grid.Row="1" />
[Visual C#] <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,24,0,0" Name="textBlock1" Text="Player 1" VerticalAlignment="Top" Grid.Row="1" /> <TextBox Height="73" HorizontalAlignment="Left" Margin="26,172,0,0" Name="player2TextBox" Text="Computer" VerticalAlignment="Top" Width="269" Grid.Row="1" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,136,0,0" Name="textBlock2" Text="Player 2" VerticalAlignment="Top" Grid.Row="1" />
Open the MainPage.xaml in Code view. Examine the GotFocus event handler for the player1TextBox control. Notice that the current text in the TextBox control is selected, when the TextBox control receives focus by calling the SelectAll method of the TextBox class.
[Visual Basic] ' select the text name of player 1 when the TextBox gets focus - makes a name change easier Private Sub player1TextBox_GotFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles player1TextBox.GotFocus player1TextBox.SelectAll() End Sub
[Visual C#] // select the text name of player 1 when the TextBox gets focus - makes a name change easier private void player1TextBox_GotFocus(object sender, RoutedEventArgs e) { player1TextBox.SelectAll(); }
Modify the value for the SupportedOrientations property, to support both portrait and landscape orientations. Save the changes.
Task 2: Modify MainPage.xaml to handle the OrientationChanged event and support the Grid layout technique.
Switch to the MainPage.xaml code file. Locate the TODO comment, Call the PositionControls method, in the MainPage_OrientationChanged or OnOrientationChanged event handler.
[Visual Basic] ' this method responds to a phone orientation change Private Sub MainPage_OrientationChanged(ByVal sender As Object, ByVal e As Microsoft.Phone.Controls.OrientationChangedEventArgs) Handles MyBase.OrientationChanged MyBase.OnOrientationChanged(e) ' TODO: Call the PositionControls method End Sub
[Visual C#] // this method responds to a phone orientation change protected override void OnOrientationChanged(OrientationChangedEventArgs args) { base.OnOrientationChanged(args); // TODO: Call the PositionControls method }
To the MainPage_OrientationChanged or OnOrientationChanged event handler, append code to call the PositionControls method, passing the event arguments received. Locate the TODO comment, Change the display location of controls, in the PositionControls method.
[Visual Basic] ... ' If not in portrait, move buttonList content to visible row and column. Else ' TODO: Change the display location of controls End If
[Visual C#] ... // If not in portrait, move buttonList content to visible row and column. else { // TODO: Change the display location of controls }
In the PositionControls method, append code to the Else/else part of the If...Then...Else/if...else structure, to place the textBlock1, player1TextBox, textBlock2, player2TextBox, and textBlock3 controls, in row 1, column 2, by using the grid layout technique, and the Grid.SetRow and Grid.SetColumn methods. Remember that the row and column numbers are zero based. Save the changes.
10
The Polyline control is generally used for drawing a series of connected straight lines. In the context of the Tic Tac Toe game, it is used to draw a straight line covering the three winning squares, when a game is won. Open the AdvancedGamePlay.xaml in Code view. Locate and examine the PrepareForWinnerLineDraw method. myPolyline is declared as a member variable at the top of the class as a Polyline object, and firstPoint and secondPoint are declared as member variables at the top of the class as Point objects. This method is used to initialize myPolyline with the data from winnerLine, which is also declared as a member variable at the top of the class as a Polyline object.
[Visual Basic] ' This method is used to initialize myPolyline with data from the winnerLine Public Sub PrepareForWinnerLineDraw() myPolyline = winnerLine ' get the first and last point of the winning path (polyline) firstPoint = winnerLine.Points.First() lastPoint = winnerLine.Points.Last() myPolyline.Points.Clear() ' add the first point of the winning path to the new polyline myPolyline.Points.Add(firstPoint) myPolyline.Visibility = System.Windows.Visibility.Visible ' create a point that is the end of the line as it is constructed/drawn myEndPoint = myPolyline.Points.First() End Sub
11
[Visual C#] // This method is used to initialize myPolyline with data from the winnerLine public void PrepareForWinnerLineDraw() { myPolyline = winnerLine; // get the first and last point of the winning path (polyline) firstPoint = winnerLine.Points.First(); lastPoint = winnerLine.Points.Last(); myPolyline.Points.Clear(); // add the first point of the winning path to the new polyline myPolyline.Points.Add(firstPoint); myPolyline.Visibility = System.Windows.Visibility.Visible; // create a point that is the end of the line as it is constructed/drawn myEndPoint = myPolyline.Points.First();
Locate and examine the DrawWinnerLine method, which is used to draw the actual winner line.
[Visual Basic] Public Sub DrawWinnerLine() ' construct the next point to add to the polyline Dim myNextPoint As New Point() myNextPoint.X = myEndPoint.X + ((lastPoint.X - firstPoint.X) / CType(numberOfLineSegments, Double)) myNextPoint.Y = myEndPoint.Y + ((lastPoint.Y - firstPoint.Y) / CType(numberOfLineSegments, Double)) ' add the point of the winning path to the new polyline myPolyline.Points.Add(myNextPoint) lineSegmentCounter += 1 If lineSegmentCounter >= numberOfLineSegments Then gameStatus = "game complete, announcing game result" Else myEndPoint = myNextPoint End If End Sub
[Visual C#] public void DrawWinnerLine() { // construct the next point to add to the polyline Point myNextPoint = new Point(); myNextPoint.X = myEndPoint.X + ((lastPoint.X - firstPoint.X) / (double) numberOfLineSegments); myNextPoint.Y = myEndPoint.Y + ((lastPoint.Y - firstPoint.Y) / (double) numberOfLineSegments); // add the point of the winning path to the new polyline myPolyline.Points.Add(myNextPoint); lineSegmentCounter++; if (lineSegmentCounter >= numberOfLineSegments) { gameStatus = "game complete, announcing game result";
12
} else { } }
myEndPoint = myNextPoint;
13
14
Switch to the advanced level. Notice how the Student name has been copied across from the MainPage and is now stored in the Player 1 text box. In the game pad, click any of the squares, separated by a dashed line, to start the game. Notice how the application automatically selects a box. This time the application selection is more advanced; it is seriously trying to win. You, as Player 1, use the X as the symbol for a selected box, whereas the application or computer as Player 2 uses the O symbol. Keep playing till you win, draw, or lose. Notice that if you draw, the message box pops up with the winning message. If you win or lose, you will see a message box stating whether the X's or O's win. In addition, notice the straight line being drawn across the three winning squares. Close the message box. Notice the new message box, prompting you to play another game. Cancel a new game. Switch to the basic level. Close the Windows Phone 7 emulator. Close all applications. You have now completed this lab.
Lab Shutdown
After finishing the lab, you must shut down the virtual machine and revert the changes to prepare for the next lab. In the Hyper-V virtual machine window, on the Action menu, click Turn Off. In the Turn Off Machine dialog box, click Turn Off. On the Action menu, click Revert. In the Revert Virtual Machine dialog box, click Revert.
Module 1
Lab Answer Key: Introduction to Building Silverlight Business Applications
Contents:
Exercise 1: Creating a Silverlight Application 2 Exercise 2: Configuring Out-of-Browser settings for the Silverlight Application 6
Create a new Silverlight business application named GreetingCardManagement in the D:\Labfiles\Mod01\VB\Starter or D:\Labfiles\Mod01\CS\Starter folder, by using the Silverlight Business Application template. a. b. c. d. e. f. On the File menu, point to New, and then click Project. In the Installed Templates section, expand Visual Basic or Visual C#, and then click Silverlight. In the list of project types, click Silverlight Business Application. In the Name box, type GreetingCardManagement. In the Location box, type D:\Labfiles\Mod01\VB\Starter or D:\Labfiles\Mod01\CS\Starter. Click OK.
A Silverlight project named GreetingCardManagement is created. A website named GreetingCardManagement.Web for hosting the Silverlight application and its services is also created.
Task 2: Test and examine the authentication and User Registration Services of the Silverlight application.
1. Start the application. 2. 3. Press Ctrl+F5 to start the application.
Review the content on the Home page. Review the content on the About page. Click About and review the content on the About page.
4.
Log on by registering a new user with the following information: a. User name: KimA Friendly name: Kim Abercrombie Email: KimA@contoso.com Password: Pa$$w0rd Confirm password: Pa$$w0rd Security question: What is your pets name? Security answer: Buster Click Login.
b. c. d. e. f. g. h. i. j.
In the Login dialog box, click Register now. In the User name box, type KimA In the Friendly name box, type Kim Abercrombie. In the Email box, type KimA@contoso.com. In the Password box, type Pa$$w0rd. In the Confirm password box, type Pa$$w0rd. In the Security question list, click What is your pets name? In the Security answer box, type Buster. Click OK.
You are registered through the built-in authentication services provided by the application, and you are logged into the application. The authentication is handled by the WCF service in the GreetingCardManagement.Web hosting application. Note that the application displays a welcome message that uses the friendly name you specified. 5. Log off from the site. Click logout.
Note that the application welcome message is removed as is the friendly user name. 6. 7. Close Internet Explorer. Examine the Services\AuthenticationService.vb or Services\AuthenticationService.cs code file in the GreetingCardManagement.Web project. In Solution Explorer, under GreetingCardManagement.Web, expand Services, and then doubleclick AuthenticationService.vb or AuthenticationService.cs.
Notice how the AuthenticationService class is annotated with the EnableClientAccess attribute, to enable the Silverlight application to access it. The AuthenticationService class is derived from the generic AuthenticationBase(Of T)or AuthenticationBase<T>class, which in turn implements the generic AuthenticationBase<T> interface. The type of the user entity to authenticate is specified by using T. 8. Examine the user entity, named User, by going to the definition of the class User. a. b. In the AuthenticationService.vb or AuthenticationService.cs code file, right-click User, and then click Go To Definition, or press F12. [Visual C# only] In the Find Symbols Results window, double-click D:\Labfiles\Mod01\CS\Starter\GreetingCardManagement\ GreetingCardManagement.Web\Models\Shared\User.shared.cs.
Notice how the User class in the User.shared.vb or User.shared.cs file is partial because it extends the User type by adding shared properties and methods that are available both to the server application and the client application, Silverlight, in this case. The User class implements the DisplayName property 9. Examine the main partial class User, in the Models folder. In Solution Explorer, under GreetingCardManagement.Web, double-click Models\User.vb or Models\User.cs.
Note how the main partial class, User, is derived from the UserBase class and implements the FriendlyName property. 10. Examine the Services\UserRegistrationService.vb or Services\UserRegistrationService.cs code file in the GreetingCardManagement.Web project. In Solution Explorer, under GreetingCardManagement.Web, expand Services, and then doubleclick UserRegistrationService.vb or UserRegistrationService.cs.
Notice how the UserRegistrationService class is also annotated with the EnableClientAccess attribute. The UserRegistrationService class is derived from the abstract DomainService class, which is the base class for all System.ServiceModel.DomainServices.Server.DomainServices. The UserRegistrationService class uses the ASP.NET Membership and Roles API to provide the user registration services. 11. Examine the content of the App_Data folder in the GreetingCardManagement.Web project. a. b. In Solution Explorer, click GreetingCardManagement.Web, and then click the Show All Files button. Expand App_Data.
Notice how the ASPNETDB.MDF SQL Server Express database is located in the App_Data folder. It was automatically created when you used the user registration services as part of creating or registering a new user.
3.
4.
5.
Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. Note that you are logged on automatically as 10554A-SEA-DEV\Student because you have configured the website to use Windows authentication. 6. Log off from the site. Click logout.
A custom dialog box displaying an error message appears, because it is not supported to log off from a Windows-authenticated application. 7. Close the custom error dialog box. 8. 9. In the custom error dialog box, click OK.
Close Internet Explorer. Open the App.xaml.vb or App.xaml.cs code file in the GreetingCardManagement project. In Solution Explorer, under GreetingCardManagement, expand App.xaml, and then doubleclick App.xaml.vb or App.xaml.cs.
10. In the App class constructor, comment out the line of code that initializes forms authentication.
[Visual Basic] webContext.Authentication = New FormsAuthentication() [Visual C#] webContext.Authentication = new FormsAuthentication();
11. In the App class constructor, uncomment the line of code that initializes Windows authentication.
[Visual Basic] webContext.Authentication = New WindowsAuthentication() [Visual C#] webContext.Authentication = new WindowsAuthentication();
12. Save the changes. On the File menu, click Save All.
Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. Note that you are logged on automatically as 10554A-SEA-DEV\Student because you have configured the website to use Windows authentication. Note also that the logout link is no longer available because you have configured the Silverlight application to correctly use Windows authentication. 14. Close Internet Explorer.
Enable the application to run outside of the browser. Open the Silverlight page, and then ensure that the Enable running application out of the browser check box is selected.
3.
Set the following out-of-browser settings: a. b. c. d. e. f. g. h. i. Window Title: GreetingCard Desktop Management Width: 600 Height: 400 Set window location manually: Selected Top: 100 Left: 100 Shortcut name: GreetingCard Desktop Management Show install menu: selected Click Out-of-Browser Settings. In the Window Title box, type GreetingCard Desktop Management. In the Width box, type 600. In the Height box, type 400. Select the Set window location manually check box. In the Top box, type 100. In the Left box, type 100. In the Shortcut name box, type GreetingCard Desktop Management. Ensure that the Show install menu check box is selected and then click OK.
4.
Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. The application is still running in the browser. 5. Install the application locally, and add shortcuts to the Start menu and the desktop. a. b. Right-click the background of the application, and then click Install GreetingCard Desktop Management onto this computer. Ensure that Start menu and Desktop check boxes are selected and then click OK.
The application continues to run in its own window. 7. 8. Close the GreetingCard Desktop Management localhost window. On the Start menu, click All Programs, and then click GreetingCard Desktop Management. The application opens directly in its own window. 9. Remove the locally installed application, by using the context menu. a. b. In the application, right-click anywhere, and then click Remove this application. Click Yes.
In the XAML view, locate the HyperlinkButton control with a name of Link2. Under the Link2 element, add the following markup.
<Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/> <HyperlinkButton x:Name="closeLink" Style="{StaticResource LinkStyle}" Content="Exit" Visibility="Collapsed"/>
4.
Create a Click event handler for the closeLink HyperlinkButton control. a. b. Ensure that the insertion point is in the markup for the closeLink HyperlinkButton control. In the Properties window, click Events, and then double-click Click.
[Visual C# only] Visual Studio adds a Click attribute to the markup for the HyperlinkButton control. Visual Studio creates the event handler stub for the event, and the code file appears. 5. In the Click event handler, close the MainWindow form by using the App.Current property. Add the following code to the closeLink_Click event handler.
App.Current.MainWindow.Close();
6.
Task 4: Control the visibility of the Exit button in the Out-of-Browser mode.
1. In the MainPage class, in the constructor append code to check if the application is running out-ofbrowser and with elevated trust, by using the App.Current property. If so, make closeLink HyperlinkButton control visible.
[Visual Basic] If App.Current.IsRunningOutOfBrowser AndAlso App.Current.HasElevatedPermissions Then closeLink.Visibility = Visibility.Visible End If [Visual C#] if (App.Current.IsRunningOutOfBrowser && App.Current.HasElevatedPermissions) { closeLink.Visibility = Visibility.Visible; }
2.
3.
Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. The application is still running in the browser. Note that the Exit button is not visible. 4. Install the application locally, add shortcuts to the Start menu and the desktop, and accept the security advice. a. In the application, right-click anywhere, and then click Install GreetingCard Desktop Management onto this computer.
Because the application now requests elevated trust when it runs outside the browser, Internet Explorer displays a security warning dialog box. b. c. Click More options and ensure that Start menu and Desktop check boxes are checked. Click Install.
The application opens in its own window, with a rounded borderless style. Note that the Exit button is visible. 5. Close Internet Explorer. The application continues to run in its own window. 6. Close the application.
7.
Click Exit.
Module 2
Lab Answer Key: Building Data-Driven Applications
Contents:
Exercise 1: Connecting to a Database in a Silverlight Project Exercise 2: Querying and Displaying Data from a Database 2 4
Open the GreetingCardManagement solution from the D:\Labfiles\Mod02\VB\Starter or D:\Labfiles\Mod02\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod02\VB\Starter\ GreetingCardManagement.sln or D:\Labfiles\Mod02\CS\Starter\ GreetingCardManagement.sln, and then click Open.
3.
4.
Briefly review the solutionit is very similar to the completed solution that you created for Lab 1. The solution consists of a simple Silverlight project and an ASP.NET Web application for hosting the Silverlight application and for providing WCF RIA Services.
The Entity Data Model Wizard appears. 2. 3. In the Entity Data Model Wizard, ensure that Generate from database is selected, and then click Next. Create a new connection to the 10554A-SEA-DEV\SQLEXPRESS SQL Server instance and the GreetingCard database. a. b. c. d. In the Entity Data Model Wizard, on the Choose Your Data Connection page, click New Connection. In the Choose Data Source dialog box, click Microsoft SQL Server, and then click Continue. In the Connection Properties dialog box, in the Server name box, type 10554A-SEADEV\SQLEXPRESS. In the Select or enter a database name list, click GreetingCard, and then click OK.
4.
Ensure that the connection settings are saved to Web.config as GreetingCardEntities, and then move to the next page. Ensure that the Save entity connection settings in Web.config as check box is selected, GreetingCardEntities is entered in the Save entity connection settings in Web.config as box, and then click Next.
5.
Select the Card (dbo) and Sender (dbo) tables. In the Entity Data Model Wizard, on the Choose Your Database Objects page, in the Which database objects do you want to include in your model? list, expand Tables, and then select the Card (dbo) and Sender (dbo) check boxes.
6.
Ensure that the Model Namespace box reads GreetingCardModel, and then click Finish. The ADO.NET Entity Data Model Designer appears and displays the tables and the relationship between them.
7. 8.
Save the changes. Build the GreetingCardManagement.Web project. On the Build menu, click Build GreetingCardManagement.Web.
The Add New Domain Service Class wizard appears. 2. For the GreetingCardManagementDomainService class, have the Add New Domain Service Class wizard generate associated metadata for the Card and Sender entities in the GreetingCardEntities collection, but do not enable editing of these entities. In the Add New Domain Service Class wizard, in the list, select the Card and Sender check boxes, ensure that the corresponding Enable editing check boxes are cleared, and then click OK.
Leave Visual Studio runningyou will work with the data objects you have just created in the next exercise.
2.
2.
3.
Add the following property attribute immediately above the property you have just located.
4.
Task 3: Add data controls for senders data to the Silverlight application.
1. In the Views folder, open Home.xaml. 2. In Solution Explorer, in the GreetingCardManagement project, expand the Views folder, and then double-click Home.xaml.
3. 4.
Delete the Home page content TextBlock control. Show the data sources for the GreetingCardManagement project. On the Data menu, click Show Data Sources.
5.
To show the data in master-details view, in the Data Sources window, select the details for the Sender entity. In the Data Sources window, click Sender, click the drop-down arrow for the Sender item, and then click Details.
6. 7.
Drag the Sender item from the Data Sources window to the upper-left corner of Home.xaml in Design view. Delete the User ID grid row. Right-click User ID, point to Grid Row, and then click Delete.
8.
In the Data Sources window, expand the Sender item, drag the Cards sub-item to Home.xaml in Design view and drop it below the Sender data you just added. Note: Ensure that you drag the Cards item that belongs to Sender, and not the top-level Card item.
9.
Open the editor for the columns collection of the DataGrid control for the Cards data. Ensure that the DataGrid control for the Cards data in Home.xaml is selected, and then in the Properties window, click Columns. Click the ellipsis () button for the Columns property.
10. In the Collection Editor: Columns dialog box, remove the CardGuidColumn or cardGuidColumn, CardIDColumn or cardIDColumn, and SenderIDColumn or senderIDColumn DataGridTextColumn objects from the list on the left. a. b. c. In the Collection Editor: Columns dialog box, click DataGridTextColumn (CardGuidColumn) or DataGridTextColumn (cardGuidColumn), and then click the Remove item button. Click DataGridTextColumn (CardIDColumn) or DataGridTextColumn (cardIDColumn), and then click the Remove item button. Click DataGridTextColumn (SenderIDColumn) or DataGridTextColumn (senderIDColumn), and then click the Remove item button.
11. Move up the RecipientEmailColumn or recipientEmailColumn DataGridTextColumn object on the list on the left. Click DataGridTextColumn (RecipientEmailColumn) or DataGridTextColumn (recipientEmailColumn), and then click the Move item up button.
12. Show the Layout section for RecipientEmailColumn or recipientEmailColumn DataGridTextColumn. Ensure that DataGridTextColumn (RecipientEmailColumn) or DataGridTextColumn (recipientEmailColumn) is selected, and then in the Properties section, expand Layout.
13. Set the following property value for RecipientEmailColumn or recipientEmailColumn DataGridTextColumn in the Layout section. Width: 300 Pixel
a. b.
14. Show the Layout section for the CardDataColumn or cardDataColumn DataGridTextColumn. Ensure that DataGridTextColumn (CardDataColumn) or DataGridTextColumn (cardDataColumn) is selected, and then in the Properties section, expand Layout.
15. Set the following property value for CardDataColumn or cardDataColumn DataGridTextColumn in the Layout section. a. b. Width: 295 Pixel In the Width list, click Pixel. In the box, type 295.
16. Close the Collection Editor: Columns dialog box, saving the changes. Click OK.
17. In the Properties window, set the following property values for the DataGrid control. a. b. c. d. Width: 600 HorizontalAlignment: Left VerticalAlignment: Top Ensure that the DataGrid for the Cards data is selected. In the Properties window, expand Layout, click Width, type 600, and then press Enter. Click HorizontalAlignment, and then click Left in the list. Click VerticalAlignment, and then click Top in the list.
Task 4: Add paging controls for senders data to the Silverlight application.
1. In the XAML window, locate the markup that begins with the following code.
<sdk:DataGrid
2. 3.
Insert a blank line immediately above the markup you have just located. Drag a DataPager control from the All Silverlight Controls section of the toolbox to the XAML window, where you just added a blank line. Review the markup for the DataPager control. A DataPager control is added to the XAML window, and to the design surface (between the Sender data and the Card grid). Arrange the data controls so that they resemble the following image.
4. 5. 6.
From the Data Sources window, drag Sender and drop it on the DataPager control. Review the modified markup for the DataPager control and note that a Source element has been added that binds the DataPager to the Sender data. In the Properties window, set the following property values for the DataPager control. a. b. PageSize: 1 DisplayMode: FirstLastPreviousNextNumeric Ensure that the DataPager control is selected and then, in the Properties window, expand Other, click PageSize, type 1, and then press Enter. Expand Common, click DisplayMode, and then click FirstLastPreviousNextNumeric.
7.
Internet Explorer appears and navigates to the test page in the GreetingCardManagement.Web site. 2. 3. 4. Review the sender data that is displayed, and then use the DataPager control to navigate through the data. Note how Card Data updates when different Senders are displayed. Close Internet Explorer. Close Visual Studio.
Module 3
Lab Answer Key: Advanced Data Management
Contents:
Exercise 1: Implementing Full Read-Write Scenarios 2
Create a new Silverlight Business application named GreetingCardConsumer in the D:\Labfiles\Mod03\VB\Starter or D:\Labfiles\Mod03\CS\Starter folder, by using the Silverlight Business Application template. a. b. c. d. e. f. On the File menu, point to New, and then click Project. In the Installed Templates section, expand Visual Basic or Visual C#, and then click Silverlight. In the list of project types, click Silverlight Business Application. In the Name box, type GreetingCardConsumer. In the Location box, type D:\Labfiles\Mod03\VB\Starter or D:\Labfiles\Mod03\CS\Starter. Click OK.
A Silverlight project named GreetingCardConsumer is created. A website named GreetingCardConsumer.Web for hosting the Silverlight application and its services is also created.
The Resource Designer opens displaying a three-column grid containing the resource strings in the ApplicationStrings.resx resource file. 2. Replace the value Application Name with Greeting Card Consumer. 3. 4. In the Value column, select the text that reads Application Name, type Greeting Card Consumer, and press Enter.
Locate the UserName property declaration. The RegularExpression attribute for the UserName property declaration currently reads:
"^[a-zA-Z0-9_]*$"
3.
You should type this expression all on a single line. It represents a pattern match for a valid email address. Note You can copy the expression from the Email property, which is located just below the UserName property. 4. In the GreetingCardConsumer.Web project, open the Resource Designer for the Resources\ValidationErrorResources.resx resource file. In Solution Explorer, in the GreetingCardConsumer.Web project, folder, expand Resources, and then double-click ValidationErrorResources.resx.
The Resource Designer opens displaying a three-column grid containing the resource strings in the ValidationErrorResources.resx resource file. 5. In the row with a Name column value of ValidationErrorInvalidUserName, replace the content of the Value column with Invalid user name. You must use a valid email address. 6. 7. In the Value column, replace the current content with Invalid user name. You must use a valid email address., and then press Enter.
Build the GreetingCardConsumer.Web project. On the Build menu, click Build GreetingCardConsumer.Web.
Update the data model from database. In the Entity Designer, right-click a blank area and then click Update Model from Database.
The Update Wizard appears. 3. Create a new connection to the 10554A-SEA-DEV\SQLEXPRESS SQL Server instance and the GreetingCard database. a. b. c. d. 4. In the Update Wizard, on the Choose Your Data Connection page, click New Connection. In the Choose Data Source dialog box, click Microsoft SQL Server, and then click Continue. In the Connection Properties dialog box, in the Server name box, type 10554A-SEADEV\SQLEXPRESS. In the Select or enter a database name list, click GreetingCard, and then click OK.
In the Update Wizard, on the Choose Your Data Connection page, click Next. The connection string will now be saved to the Web.config file.
5.
Finish the Update Wizard. In the Update Wizard, on the Choose Your Database Objects page, click Finish.
6. 7.
Save and close the GreetingCardModel entity data model. Build the solution. On the Build menu, click Build Solution.
b.
In the Add New Item - GreetingCardConsumer.Web dialog box, in the Installed Templates section, click Web, in the templates list, click Domain Service Class, in the Name box, type GreetingCardConsumerDomainService, and then click Add.
The Add New Domain Service Class wizard appears. 2. For the GreetingCardConsumerDomainService class, have the Add New Domain Service Class wizard generate associated metadata for the Card and Sender entities in the GreetingCardEntities collection, and enable editing of these entities. In the Add New Domain Service Class wizard, in the list, select the Card and Sender check boxes, select the corresponding Enable editing check boxes, and then click OK.
Task 7: Edit the GetSenders Query in the Domain Service Class to Restrict Data to the Current User.
1. In the GreetingCardConsumerDomainService code file, modify the code in the GetSenders method so that it reads:
[Visual Basic] Dim userName As String = System.Web.HttpContext.Current.User.Identity.Name Return Me.ObjectContext.Senders.Include("Cards") _ .Where(Function(sndr) sndr.Email = userName)
[Visual C#] string userName = System.Web.HttpContext.Current.User.Identity.Name; return this.ObjectContext.Senders.Include("Cards") .Where(sndr =>sndr.Email == userName);
2.
2. 3.
Locate the Cards property declaration in the SenderMetadata class. Add the Include attribute to the Cards property declaration. In the GreetingCardConsumerDomainService. metadata.vb, or GreetingCardConsumerDomainService. metadata.cs file, in the SenderMetadata class, add the following code attribute immediately above the Cards property declaration.
[Visual Basic] <Include()>
4.
Task 9: Add Data Controls for Senders Data to the Silverlight Application.
1. To the GreetingCardConsumer project, add a new Silverlight Page named Details, to the Views folder. a. b. In Solution Explorer, in the GreetingCardConsumer project, right-click the Views folder, point to Add, and then click New Item. In the Add New Item - GreetingCardConsumer dialog box, in the Installed Templates section, click Silverlight, in the templates list, click Silverlight Page, in the Name box, type Details, and then click Add.
2.
In the GreetingCardConsumer project, open MainPage.xaml. In Solution Explorer, in the GreetingCardConsumer project, double-click MainPage.xaml.
3. 4.
In MainPage.xaml, in the XAML view, locate the markup that begins <HyperlinkButton x:Name="Link2". Immediately above the located markup, add a new self-closing HyperlinkButton control element named Details. Set the following properties: Property Value Your Details /Details {StaticResource LinkStyle} ContentFrame
The NavigateUri property specifies that when a user clicks the HyperlinkButton control, it should navigate to the Details.xaml page, by using a relative uri (/Details). The TargetFrame property specifies that the content available at the NavigateUri should be displayed in the ContentFrame navigation frame. This is part of the navigation automatically provided when creating a Silverlight Business Application. 5. Immediately below the DetailsHyperlinkButton control, add a new self-closing Rectangle control element named DetailsDivider. Set the following property: Property Value {StaticResource DividerStyle}
6. 7.
Switch back to Details.xaml. Build the solution. On the Build menu, click Build Solution.
8.
In Details.xaml, in the XAML view, select all the markup, and then type in the following markup or copy from the D:\Labfiles\Mod03\VB\DetailsMarkup.txt or D:\Labfiles\Mod03\CS\DetailsMarkup.txt file, and paste it in:
[Visual Basic] <navigation:Page x:Class="GreetingCardConsumer.Details" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:navigation="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="Details Page" xmlns:riaControls="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:my="clr-namespace:GreetingCardConsumer" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <sdk:Page.Resources> <CollectionViewSource x:Key="SenderCardsViewSource" Source="{Binding Path=Data.Cards, ElementName=SenderDomainDataSource}" /> </sdk:Page.Resources> <Grid x:Name="LayoutRoot"> <riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:Sender, CreateList=true}" Height="0" Name="SenderDomainDataSource" QueryName="GetSendersQuery" Width="0"> <riaControls:DomainDataSource.DomainContext> <my:GreetingCardConsumerDomainContext /> </riaControls:DomainDataSource.DomainContext> </riaControls:DomainDataSource> <Grid DataContext="{Binding ElementName=SenderDomainDataSource, Path=Data}" HorizontalAlignment="Left" Margin="12,12,0,0" Name="Grid1" VerticalAlignment="Top" Height="66" Width="220"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <sdk:Label Content="Display Name:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="DisplayNameTextBox" Text="{Binding Path=DisplayName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Email:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="EmailTextBox" Text="{Binding Path=Email, Mode=TwoWay,
NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> </Grid> <sdk:DataGrid AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" ItemsSource="{Binding Source={StaticResource SenderCardsViewSource}}" Margin="12,84,0,0" Name="CardsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="600"> <sdk:DataGrid.Columns> <sdk:DataGridTextColumn x:Name="RecipientEmailColumn" Binding="{Binding Path=RecipientEmail}" Header="Recipient Email" Width="300" /> <sdk:DataGridTextColumn x:Name="CardDataColumn" Binding="{Binding Path=CardData}" Header="Card Data" Width="295" /> </sdk:DataGrid.Columns> </sdk:DataGrid> </Grid> </navigation:Page>
[Visual C#] <navigation:Page x:Class="GreetingCardConsumer.Details" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:navigation="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="Details Page" xmlns:riaControls="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:my="clr-namespace:GreetingCardConsumer.Web" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <sdk:Page.Resources> <CollectionViewSource x:Key="SenderCardsViewSource" Source="{Binding Path=Data.Cards, ElementName=SenderDomainDataSource}" /> </sdk:Page.Resources> <Grid x:Name="LayoutRoot"> <riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:Sender, CreateList=true}" Height="0" Name="SenderDomainDataSource" QueryName="GetSendersQuery" Width="0"> <riaControls:DomainDataSource.DomainContext> <my:GreetingCardConsumerDomainContext /> </riaControls:DomainDataSource.DomainContext> </riaControls:DomainDataSource> <Grid DataContext="{Binding ElementName=SenderDomainDataSource, Path=Data}" HorizontalAlignment="Left" Margin="12,12,0,0" Name="Grid1" VerticalAlignment="Top" Height="66" Width="220"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <sdk:Label Content="Display Name:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="DisplayNameTextBox" Text="{Binding Path=DisplayName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Email:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="EmailTextBox" Text="{Binding Path=Email, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> </Grid> <sdk:DataGrid AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" ItemsSource="{Binding Source={StaticResource SenderCardsViewSource}}" Margin="12,84,0,0" Name="CardsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="600"> <sdk:DataGrid.Columns> <sdk:DataGridTextColumn x:Name="RecipientEmailColumn" Binding="{Binding Path=RecipientEmail}" Header="Recipient Email" Width="300" /> <sdk:DataGridTextColumn x:Name="CardDataColumn" Binding="{Binding Path=CardData}" Header="Card Data" Width="295" /> </sdk:DataGrid.Columns> </sdk:DataGrid> </Grid> </navigation:Page>
9.
In the GreetingCardConsumer project, add a reference to the System.Windows.Controls.Data and System.Windows.Controls.DomainServices assemblies. a. b. In the Solution Explorer window, right-click GreetingCardConsumer, and then click Add Reference. In the Add Reference dialog box, in the list, select System.Windows.Controls.DomainServices and System.Windows.Controls.Data, and then click OK.
10. Build the solution. On the Build menu, click Build Solution.
Task 10: Develop Code to Add Newly Registered Users to the GreetingCard Database.
1. In the GreetingCardConsumer project, open Views\Login\RegistrationForm.xaml in Code view. 2. 3. In Solution Explorer, in the GreetingCardConsumer project, expand Views, expand Login, rightclick RegistrationForm.xaml, and then click View Code.
In the RegistrationForm class, locate the RegisterButton_Click event handler. Append code to the If/if block in the event handler to create and instantiate an instance of the GreetingCardConsumerDomainContext class, create and instantiate an instance of the Sender class. Set the Email property of the sender instance to the value of the local registrationData.UserName, set the DisplayName property of the sender instance to the value of the local registrationData.FriendlyName and add the sender instance to the Senders collection property of the GreetingCardConsumerDomainContext instance. Finally, use the GreetingCardConsumerDomainContext instance, to submit the changes. Append the following code to the existing code in the event handler, in the If/if block:
[Visual Basic] Dim context As New GreetingCardConsumerDomainContext() Dim sndr As New Sender() sndr.Email = Me.registrationData.UserName sndr.DisplayName = Me.registrationData.FriendlyName context.Senders.Add(sndr) context.SubmitChanges()
10
[Visual C#] GreetingCardConsumerDomainContext context = new GreetingCardConsumerDomainContext(); Sender sndr = new Sender(); sndr.Email = this.registrationData.UserName; sndr.DisplayName = this.registrationData.FriendlyName; context.Senders.Add(sndr); context.SubmitChanges();
4.
Add the existing D:\Labfiles\Mod03\HeadPhoto.png image file to the Images folder. a. b. In Solution Explorer, in the GreetingCardConsumer project, right-click the Images folder, point to Add, and then click Existing Item. In the Add Existing Item- GreetingCardConsumer dialog box, in the File name box, type D:\Labfiles\Mod03\HeadPhoto.png, and then click Add.
3. 4.
Switch back to Details.xaml. Disable the two TextBox controls. a. b. Select the TextBox next to the DisplayNameLabel control, press and hold Ctrl, and then select the TextBox next to the EmailLabel control. In the Properties window, clear the IsEnabled check box.
5.
In the XAML view of Details.xaml, locate the markup that begins as follows:
<sdk:DataGrid
6. 7.
Insert an empty line above the markup that you located. Add the following markup to the space you have just created. You can type in the markup manually, or you can copy from the D:\Labfiles\Mod03\EditDetailsMarkup.txt file, and paste it in.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Border BorderBrush="Silver" BorderThickness="2" Height="70" Margin="10" Name="editDetails" Width="50" CornerRadius="10" VerticalAlignment="Top" Cursor="Hand"> <StackPanel Orientation="Vertical"> <Border CornerRadius="10,10,0,0" Height="50"> <Border.Background> <ImageBrush ImageSource="/GreetingCardConsumer;component/Assets/Images/HeadPhoto.png"/> </Border.Background> </Border> <ContentControl Height="20" Content="Edit" HorizontalAlignment="Center" Background="Silver"/> </StackPanel> </Border> <StackPanel Orientation="Vertical" VerticalAlignment="Top"
11
Margin="10" HorizontalAlignment="Left" x:Name="editControls" Visibility="Collapsed"> <Button x:Name="saveChanges" Content="Save" Width="50" Margin="5" Cursor="Hand"/> <Button x:Name="cancelChanges" Content="Cancel" Width="50" Margin="5" Cursor="Hand"/> </StackPanel> </StackPanel>
The markup adds controls for editing the details, wrapped a StackPanel control, which contains controls for displaying the image you added previously, and controls for saving or cancelling the changes. 8. 9. Save the changes. Add an event handler for the MouseLeftButtonDown event of the editDetailsBorder control. a. b. Place the insertion point in the opening tag of the Border element named editDetails. In the Properties window, click Events, and then double-click MouseLeftButtonDown.
Visual Studio creates an event handler stub for you. 10. Add code to the event handler to check whether the name of the current user, WebContext.Current.User.Name, is null or empty. If so, create and display a new error window using the ErrorWindow.CreateNew method, displaying the message, You must log in to edit your data, but never show the stack trace. In addition, exit the method, if the name of the current user is null or empty. Otherwise, enable the EmailTextBox or emailTextBox, and DisplayNameTextBox or displayNameTextBox controls, set the Visibility property of the editDetails control to a value of System.Windows.Visibility.Collapsed, and set the Visibility property of the editControls control to a value of System.Windows.Visibility.Visible. Add the following code to event handler:
[Visual Basic] If String.IsNullOrEmpty(WebContext.Current.User.Name) Then ErrorWindow.CreateNew("You must log in to edit your data", StackTracePolicy.Never) Return End If EmailTextBox.IsEnabled = True DisplayNameTextBox.IsEnabled = True editDetails.Visibility = System.Windows.Visibility.Collapsed editControls.Visibility = System.Windows.Visibility.Visible
[Visual C#] if (string.IsNullOrEmpty(WebContext.Current.User.Name)) { ErrorWindow.CreateNew("You must log in to edit your data", StackTracePolicy.Never); } return;
12
editControls.Visibility = System.Windows.Visibility.Visible;
11. Switch back to the XAML view of Details.xaml. 12. Add an event handler for the Click event of the saveChangesButton control. a. b. Place the insertion point in the opening tag of the Button element named saveChanges. In the Properties window, click Events, and then double-click Click.
Visual Studio creates an event handler stub for you. 13. Add code to the event handler to use the SenderDomainDataSource or senderDomainDataSource object to submit the changes. Wrap the code in a Try...Catch...Finally or try...catch...finally construct, with just one general Catch or catch (Exception). If an exception is thrown, use the SenderDomainDataSource or senderDomainDataSource object to reject the changes, create and display a new error window, using the ErrorWindow.CreateNew method, displaying the message, The following problem occurred when attempting to save your changes, but never show the stack trace. To the error message, append content of the Message property of the caught exception. In the Finally or finally part, disable the EmailTextBox or emailTextBox, and DisplayNameTextBox or displayNameTextBox controls, set the Visibility property of the editDetails control to a value of System.Windows.Visibility.Visible, and set the Visibility property of the editControls control to a value of System.Windows.Visibility.Collapsed. Add the following code to the event handler:
[Visual Basic] Try SenderDomainDataSource.SubmitChanges() Catch ex As Exception SenderDomainDataSource.RejectChanges() ErrorWindow.CreateNew("The following problem occurred " _ & "when attempting to save your changes:" _ & ex.Message, StackTracePolicy.Never) Finally EmailTextBox.IsEnabled = False DisplayNameTextBox.IsEnabled = False editDetails.Visibility = System.Windows.Visibility.Visible editControls.Visibility = System.Windows.Visibility.Collapsed End Try
[Visual C#] try { senderDomainDataSource.SubmitChanges(); } catch (Exception ex) { senderDomainDataSource.RejectChanges(); ErrorWindow.CreateNew("The following problem occurred when attempting to save your changes:" + ex.Message, StackTracePolicy.Never); } finally { emailTextBox.IsEnabled = false; displayNameTextBox.IsEnabled = false; editDetails.Visibility = System.Windows.Visibility.Visible; editControls.Visibility = System.Windows.Visibility.Collapsed;
13
14. Switch back to the XAML view of Details.xaml. 15. Add an event handler for the Click event of the cancelChangesButton control. a. b. Place the insertion point in the opening tag of the Button element named cancelChanges. In the Properties window, click Events, and then double-click Click.
Visual Studio creates an event handler stub for you. 16. Add code to the event handler to disable the EmailTextBox or emailTextBox, and DisplayNameTextBox or displayNameTextBox controls, set the Visibility property of the editDetails control to a value of System.Windows.Visibility.Visible, and set the Visibility property of the editControls control to a value of System.Windows.Visibility.Collapsed. Finally, use the SenderDomainDataSource or senderDomainDataSource object to reject the changes. Add the following code to the event handler:
[Visual Basic] EmailTextBox.IsEnabled = False DisplayNameTextBox.IsEnabled = False editDetails.Visibility = System.Windows.Visibility.Visible editControls.Visibility = System.Windows.Visibility.Collapsed SenderDomainDataSource.RejectChanges()
[Visual C#] emailTextBox.IsEnabled = false; displayNameTextBox.IsEnabled = false; editDetails.Visibility = System.Windows.Visibility.Visible; editControls.Visibility = System.Windows.Visibility.Collapsed; senderDomainDataSource.RejectChanges();
Connect to the 10554A-SEA-DEV\SQLEXPRESS SQL Server Instance. In the Connect to Server dialog box, click Connect.
3.
4.
Show the tables for the GreetingCard database. In Object Explorer, under GreetingCard, expand Tables.
5.
View the first 1000 rows of the Sender table. Right-click dbo.Sender, and then click Select Top 1000 Rows.
14
Notice the 10 rows of users or senders in the Results window. Leave Microsoft SQL Server Management Studio running 6. 7. Switch back to Visual Studio. Start the application. 8. 9. Press Ctrl+F5.
Click Your Details. Click Edit. Note box. The error message, for which you wrote the validation for, is displayed in a dialog
10. Close the dialog box. 11. Go to Home page. Click Home.
12. Log on by registering as a new user with the following information. a. b. c. d. e. f. g. h. i. j. User name: GregG Friendly name: Greg Guzik Email: GregG@contoso.com Password: Pa$$w0rd Confirm password: Pa$$w0rd Security question: What is your pets name? Security answer: Lily Click Login. In the Login dialog box, click Register now. In the User name box, type GregG. In the Friendly name box, type Greg Guzik. In the Email box, type GregG@contoso.com. In the Password box, type Pa$$w0rd. In the Confirm password box, type Pa$$w0rd. In the Security question list, click What is your pets name? In the Security answer box, type Lily. Click OK.
Note that you cannot register because the user name must be a valid email address. You set this validation earlier.
15
13. Change the user name to GregG@contoso.com, and register In the User name text box, type GregG@contoso.com, and then click OK.
You are registered through the built-in authentication services provided by the application, and you are logged on to the application. Leave Internet Explorer running. 14. Switch back to Microsoft SQL Server Management Studio. 15. On the Query menu, click Execute. The data refreshes and shows a new sender for the user you just registered in the Silverlight application. 16. Switch back to Internet Explorer. 17. Go to Home page Click Home.
18. Edit the details for the current user. a. b. Click Your Details. Click Edit.
19. Change the display name to Gregory, and save the change. a. b. In the Display Name box, type Gregory. Click Save.
20. Switch back to Microsoft SQL Server Management Studio. 21. Rerun the top 1000 query. On the Query menu, click Execute.
The data refreshes and shows the updated DisplayName for Gregory. 22. Close all applications. You have now completed this lab.
Module 4
Lab Answer Key: Implementing User Controls and Navigation
Contents:
Exercise 1: Adding a Navigation User Control Exercise 2: Updating the Number of Cards Periodically 2 8
Open the GreetingCardManagement solution from the D:\Labfiles\Mod04\VB\Starter or D:\Labfiles\Mod04\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod04\VB\Starter\ GreetingCardManagement.sln or D:\Labfiles\Mod04\CS\Starter\ GreetingCardManagement.sln, and then click Open.
The solution consists of a simple Silverlight project and an ASP.NET web application for hosting the Silverlight application and for providing Windows Communication Foundation (WCF) Rich Internet Applications (RIA) services. This is the solution code from Module 2.
Remove the standard white background from the Grid control. In the NavigationSidebar.xaml window, in the XAML view, remove the Background attribute and value Background="White".
3.
Locate the Border control named LinksBorder. In the MainPage.xaml window, in the XAML view, locate the Border control with the following opening tag.
<Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}">
3.
In the MainPage.xaml window, in the XAML view, select the following markup, and then press Ctrl+X.
<Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}"> <StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}"> <HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}" NavigateUri="/Home" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.HomePageTitle, Source={StaticResource ResourceWrapper}}"/> <Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/> <HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}" NavigateUri="/About" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.AboutPageTitle, Source={StaticResource ResourceWrapper}}"/> <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/> <HyperlinkButton x:Name="closeLink" Style="{StaticResource LinkStyle}" Content="Exit" Visibility="Collapsed"/> </StackPanel> </Border>
4.
5.
Switch to the NavigationSidebar.xaml file. In the GreetingCardManagement Microsoft Visual Studio window, click NavigationSidebar.xaml.
6.
Locate the Grid control named LayoutRoot. In the NavigationSidebar.xaml window, in the XAML view, locate the Grid control with the following opening tag.
<Grid x:Name="LayoutRoot">
7.
Insert the cut markup between the opening and closing tags of the Grid control. In the NavigationSidebar.xaml window, place the cursor between the following markup,
<Grid x:Name="LayoutRoot">
8.
9.
In Solution Explorer, in the GreetingCardManagement project, in the Views folder, right-click NavigationSidebar.xaml, and then click View Code.
11. Open the MainPage.xaml code-behind file. In Solution Explorer, in the GreetingCardManagement project, right-click MainPage.xaml, and then click View Code.
12. Locate the closeLink_Click event handler. In the MainPage.xaml.vb or MainPage.xaml.cs window, locate the following code.
[Visual Basic] Private Sub closeLink_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles closeLink.Click
The closeLink_Click event handler must be moved to the same file as the closeLink control. 13. Select and cut the closeLink_Click event handler definition and code. In the MainPage.xaml.vb or MainPage.xaml.cs window, select the following code, and then press Ctrl+X.
[Visual Basic] Private Sub closeLink_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles closeLink.Click App.Current.MainWindow.Close() End Sub
14. Switch to the NavigationSidebar.xaml.vb or NavigationSidebar.xaml.cs file. In the GreetingCardManagement Microsoft Visual Studio window, click NavigationSidebar.xaml.vb or NavigationSidebar.xaml.cs.
15. Append the cut code at the end of NavigationSidebar class. In the NavigationSidebar.xaml.vb or NavigationSidebar.xaml.cs window, paste the cut code at the end of the NavigationSidebar class.
16. Switch to the MainPage.xaml.vb or MainPage.xaml.cs file. In the GreetingCardManagement Microsoft Visual Studio window, click MainPage.xaml.vb or MainPage.xaml.cs.
17. Locate the constructor. In the MainPage.xaml.vb or MainPage.xaml.cs window, locate the following code.
18. Select and cut the constructor code that accesses the closeLink control. In the MainPage.xaml.vb or MainPage.xaml.cs window, select the following code, and then press Ctrl+X.
[Visual Basic] If App.Current.IsRunningOutOfBrowser And App.Current.HasElevatedPermissions Then closeLink.Visibility = Visibility.Visible End If
The code that accesses the closeLink control must be moved to the same file as the closeLink control. 19. Switch to the NavigationSidebar.xaml.vb or NavigationSidebar.xaml.cs file. In the GreetingCardManagement Microsoft Visual Studio window, click NavigationSidebar.xaml.vb or NavigationSidebar.xaml.cs.
20. Locate the constructor. In the NavigationSidebar.xaml.vb or NavigationSidebar.xaml.cs window, locate the following code.
[Visual Basic] Public Sub New
21. Append the cut code. In the NavigationSidebar.xaml.vb or NavigationSidebar.xaml.cs window, paste the cut code at the end of the constructor.
2.
Add the XML namespace named local for the GreetingCardManagement or GreetingCardManagement.Views CLR namespace to the GreetingCardManagement.MainPage UserControl.
In the MainPage.xaml window, in the opening UserControl tag, after the xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" namespace, add the following markup.
[Visual Basic] xmlns:local="clr-namespace:GreetingCardManagement"
3.
Append the NavigationSidebar user control with default values to the NavigationGrid Grid control. Name the control NavigationSidebar.
In the MainPage.xaml window, between the opening and closing tag of the NavigationGrid Grid control tag, append the following markup.
<local:NavigationSidebar x:Name="NavigationSidebar" />
4.
Switch to the MainPage.xaml.vb or MainPage.xaml.cs file. In the GreetingCardManagement Microsoft Visual Studio window, click MainPage.xaml.vb or MainPage.xaml.cs.
5.
If you are developing in Visual C#, bring the GreetingCardManagement.Views namespace into scope. In the MainPage.xaml.cs window, add the following code at the top of file, within the namespace block.
using GreetingCardManagement.Views;
6.
Locate the ContentFrame_Navigated event handler. In the MainPage.xaml.vb or MainPage.xaml.cs window, locate the following code.
[Visual Basic] Private Sub ContentFrame_Navigated(ByVal sender As Object, ByVal e As NavigationEventArgs)
7.
In the ContentFrame_Navigated event handler, add code to create a new StackPanel control named linkStackPanel, and assign the value of the LinksStackPanel control in the NavigationSidebar user control. You can use the FindName method of the UIElement class. In the MainPage.xaml.vb or MainPage.xaml.cs window, at the top of the ContentFrame_Navigated event handler, add the following code.
[Visual Basic] Dim linkStackPanel As StackPanel = (Me.FindName("NavigationSidebar")).FindName("LinksStackPanel")
8.
In the ContentFrame_Navigated event handler, in the For Each/foreach loop, change the name of the control being iterated from LinksStackPanel to linkStackPanel. In the MainPage.xaml.vb or MainPage.xaml.cs window, at the top of the ContentFrame_Navigated event handler, modify the For Each/foreach loop declaration to resemble the following code.
[Visual Basic] For Each child As UIElement In linkStackPanel.Children
The code in the top-level user control correctly changes the visual state of the HyperlinkButton controls, now contained in the child NavigationSidebar user control. 9. Build the solution. On the Build menu, click Build Solution.
11. Check that the navigation controls are still displayed in the upper-right corner. 12. Close Internet Explorer.
2.
Add a public method named GetCardCount that returns an Integer/int. Add the method after the existing GetCards method. In the GreetingCardManagementDomainService.vb or GreetingCardManagementDomainService.cs window, add the following code after the GetCards method.
[Visual Basic] ''' <summary> ''' Returns the no of cards ''' </summary> ''' <returns></returns> ''' <remarks></remarks> Public Function GetCardCount() As Integer End Function
[Visual C#] /// <summary> /// Returns the no of cards /// </summary> /// <returns></returns> public int GetCardCount() { }
3.
To the public GetCardCount method, add code to return the number of rows in the Cards entity by using a LINQ query selecting on the CardID column. In the GreetingCardManagementDomainService.vb or GreetingCardManagementDomainService.cs window, add the following code to the GetCardCount method.
[Visual Basic] Dim count = (From cards In Me.ObjectContext.Cards Select cards.CardID).Count() Return count
[Visual C#] var count = (from cards in this.ObjectContext.Cards select cards.CardID).Count(); return count;
4.
Add a TextBlock control named NoCardsTextBlock below the DataGrid control. It should display the text Number of cards in database:, as shown in the following image.
In the Home.xaml window, in the XAML view, below the </sdk:DataGrid> closing tag, add the following markup.
<TextBlock x:Name="NoCardsTextBlock" Text="Number of cards in database: " />
3.
In the GreetingCardManagement project, in the Views folder, open the Home.xaml code file. In Solution Explorer, in the GreetingCardManagement project, right-click Home.xaml, and then click View Code.
4.
Locate the constructor. In the Home.xaml.vb or Home.xaml.cs window, locate the following code.
[Visual Basic] Public Sub New()
5.
Append code to the constructor to create a new instance of the System.Windows.Threading.DispatcherTimer class. a. Bring the System.Windows.Threading namespace into scope by adding the following code at the top of the code file.
[Visual Basic]
10
Imports System.Windows.Threading
b.
In the Home.xaml.vb or Home.xaml.cs window, in the constructor, append the following code.
[Visual Basic] ' Used to retrieve the no of cards in the database ' at a specified interval Dim countTimer As New DispatcherTimer()
[Visual C#] // Used to retrieve the no of cards in the database // at a specified interval DispatcherTimer countTimer = new DispatcherTimer();
The timer will be used to regularly update the TextBlock control. 6. Append code to the constructor to create an anonymous event handler for the Tick event of the countTimer DispatcherTimer object. The event handler, which does not return a value, should create an instance of the GreetingCardManagementDomainContext domain context class. Name the instance gcmContext. Call the GetCardCount method on gcmContext, passing a callback method named OnGetCardCountCompleted and a null value as the only parameters. Finally, destroy the gcmContext object. a. [Visual C# only] Bring the GreetingCardManagement.Web namespace into scope by adding the following code at the top of the code file.
[Visual C#] using GreetingCardManagement.Web;
b.
In the Home.xaml.vb or Home.xaml.cs window, in the constructor, append the following code.
[Visual Basic] ' Create event handler for the Tick event AddHandler countTimer.Tick, Sub(s As Object, e As EventArgs) ' Create instance of domain service Dim gcmContext As New GreetingCardManagementDomainContext() ' Start asynchronous get card count method, passing in callback method gcmContext.GetCardCount(AddressOf Me.OnGetCardCountCompleted, Nothing) ' Destroy domain service instance gcmContext = Nothing End Sub
[Visual C#] // Create event handler for the Tick event countTimer.Tick += delegate(object s, EventArgs e) { // Create instance of domain service GreetingCardManagementDomainContext gcmContext = new GreetingCardManagementDomainContext();
11
};
// Start asynchronous get card count method, passing in callback method gcmContext.GetCardCount(this.OnGetCardCountCompleted, null); // Destroy domain service instance gcmContext = null;
7.
Append code to the constructor to set the timer interval to 15 seconds and start the timer. a. [Visual C# only] Bring the System namespace into scope by adding the following code at the top of the code file.
[Visual C#] using System;
b.
In the Home.xaml.vb or Home.xaml.cs window, in the constructor, append the following code.
[Visual Basic] ' Set the timer interval to 15 seconds ' and start the timer countTimer.Interval = New TimeSpan(0, 0, 15) countTimer.Start()
[Visual C#] // Set the timer interval to 15 seconds // and start the timer countTimer.Interval = new TimeSpan(0, 0, 15); countTimer.Start();
8.
Append a new callback method named OnGetCardCountCompleted to the Home class. The method should accept a single parameter named op, of the generic type System.ServiceModel.DomainServices.Client.InvokeOperation, restricted to integers only. a. Bring the System.ServiceModel.DomainServices.Client namespace into scope by adding the following code at the top of the code file.
[Visual Basic] Imports System.ServiceModel.DomainServices.Client
b.
In the Home.xaml.vb or Home.xaml.cs window, append the following code to the Home class.
[Visual Basic] Private Sub OnGetCardCountCompleted(ByVal op As InvokeOperation(Of Integer)) End Sub
12
9.
Add code to the callback method OnGetCardCountCompleted to call the SetCardCount method, which accepts an integer parameter. The method should check if the currently executing code is running on the UI thread by using the Dispatcher object, and subsequently call the SetCardCount method directly, or by using a new delegate named CardCountDelegate, which accepts a single integer parameter. a. Add a new private delegate named CardCountDelegate to the Home class. It must accept a single integer parameter named cardCount.
[Visual Basic] Private Delegate Sub CardCountDelegate(ByVal cardCount As Integer)
b.
In the Home.xaml.vb or Home.xaml.cs window, in the OnGetCardCountCompleted method, add the following code.
[Visual Basic] ' Get dispatcher Dim disp As Dispatcher = Me.Dispatcher ' Check if we're on the UI-thread If (disp.CheckAccess()) Then ' Call directly SetCardCount(op.Value) Else ' Call by using delegate disp.BeginInvoke( New CardCountDelegate(AddressOf SetCardCount), op.Value) End If
[Visual C#] // Get dispatcher Dispatcher disp = this.Dispatcher; // Check if we're on the UI-thread if (disp.CheckAccess()) // Call directly SetCardCount(op.Value); else // Call by using delegate disp.BeginInvoke( new CardCountDelegate(SetCardCount), op.Value);
Because the DispatcherTimer class is used, it is not necessary to check if the code is running on the current thread. However, you will now be able to use a different approach, such as using the System.Threading.Timer class instead by making simple adjustments to your code. The System.Threading.Timer class runs on a non-UI thread. 10. Append a new method named SetCardCount to the Home class. The method should accept a single integer parameter named cardCount. The Text property of the NoCardsTextBlock control should be updated with the text, Number of cards in database:, followed by the number of cards in the database, passed in the cardCount parameter.
13
In the Home.xaml.vb or Home.xaml.cs window, append the following code to the Home class.
[Visual Basic] ''' <summary> ''' Displays the current number of cards in the database ''' </summary> ''' <param name="cardCount"></param> ''' <remarks></remarks> Private Sub SetCardCount(ByVal cardCount As Integer) ' Display the current number of cards in the database NoCardsTextBlock.Text = String.Format("Number of cards in database: {0}", cardCount.ToString()) End Sub
[Visual C#] /// <summary> /// Displays the current number of cards in the database /// </summary> /// <param name="cardCount"></param> private void SetCardCount(int cardCount) { // Display the current number of cards in the database NoCardsTextBlock.Text = string.Format("Number of cards in database: {0}", cardCount.ToString()); }
11. Build the solution. On the Build menu, click Build Solution.
13. Check that the number of cards, 501, is displayed after 15 seconds. 14. Close Internet Explorer. 15. Close all applications. You have now completed this lab.
Module 5
Lab Answer Key: Creating Advanced User Interfaces
Contents:
Exercise 1: Creating ItemsControl Objects Exercise 2: Configuring Content Classes for use in the ItemsControl Class Exercise 3: Using the Custom ItemsControl object in a User Control 2 6 11
Open the GreetingCardConsumer solution from the D:\Labfiles\Mod05\VB\Starter or D:\Labfiles\Mod05\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod05\VB\Starter\ GreetingCardConsumer.sln or D:\Labfiles\Mod05\CS\Starter\ GreetingCardConsumer.sln, and then click Open.
3.
To the GreetingCardConsumer project, add a new class named CoverFlowEventArgs. a. b. In Solution Explorer, right-click GreetingCardConsumer, point to Add, and then click Class. In the Add New Item GreetingCardConsumer dialog box, in the Name box, type CoverFlowEventArgs, and then click Add.
4.
5.
[Visual Basic] Public Property Index As Integer Public Property Item As Object Public Property MouseClick As Boolean
[Visual C#] public int Index { get; set; } public object Item { get; set;
6.
Modify the class declaration so that it inherits from the ItemsControl base class and implements the INotifyPropertyChanged interface. a. If you are working in Visual Basic, modify the class declaration so that it reads as follows.
b.
If you are working in Visual C#, modify the class declaration so that it reads as follows.
3.
Add an event delegate named SelectedItemChangedEvent immediately above the class declaration you have just modified. Ensure that the delegate accepts a parameter type of CoverFlowEventArgs. a. If you are working in Visual Basic, add the following event delegate immediately above the class declaration you have just modified.
b.
If you are working in Visual C#, add the following event delegate immediately above the class declaration you have just modified.
4.
In the CoverFlowControl class, declare a public event of type SelectedItemChangedEvent and name it SelectedItemChanged. a. If you are working in Visual Basic, add the following event declaration immediately above the SelectedIndex public property procedure.
b.
If you are working in Visual C#, add the following event declaration immediately above the SelectedIndex public property procedure.
5.
Declare a class-level variable of type ItemsPresenter and name it ItemsPresenter. Declare a generic class-level variable of type List restricted to CoverFlowItemControl objects and name it cfItems.
a.
If you are working in Visual Basic, add the following variable declarations immediately below the event declaration you have just added.
b.
If you are working in Visual C#, add the following variable declarations immediately below the event declaration you have just added.
6.
[Visual Basic] Private Sub IndexSelected(ByVal index As Integer, ByVal mouseclick As Boolean, ByVal layoutChild As Boolean) If Items.Count > 0 AndAlso Items.Count > index Then selIndex = index If layoutChild Then LayoutChildren() End If Dim e As CoverFlowEventArgs = New CoverFlowEventArgs() With { .Index = index, .Item = cfItems(index).Content, .MouseClick = mouseclick } RaiseEvent SelectedItemChanged(e) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("SelectedIndex")) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("SelectedItem")) End If End Sub
[Visual C#] private void IndexSelected(int index, bool mouseclick, bool layoutChildren) { if ((Items.Count > 0) && (Items.Count>index)) { selectedIndex = index; if(layoutChildren) { LayoutChildren(); } CoverFlowEventArgs e = new CoverFlowEventArgs() { Index = index, Item = cfItems[index].Content, MouseClick = mouseclick }; if (SelectedItemChanged != null) { SelectedItemChanged(e); }
7.
[Visual Basic] Public Property RotationAngle() As Double Get Return CType(GetValue(RotationAngleProperty), Double) End Get Set(ByVal value As Double) SetValue(RotationAngleProperty, value) End Set End Property
[Visual C#] public double RotationAngle { get { return (double)GetValue(RotationAngleProperty); } set { SetValue(RotationAngleProperty, value); } }
8.
[Visual Basic] Public Shared ReadOnly RotationAngleProperty As DependencyProperty _ = DependencyProperty.Register("RotationAngle", GetType(Double), GetType(CoverFlowControl), New PropertyMetadata(60R, New PropertyChangedCallback(AddressOf CoverFlowControl.OnValuesChanged)))
[Visual C#] public static readonly DependencyProperty RotationAngleProperty = DependencyProperty.Register("RotationAngle", typeof(double), typeof(CoverFlowControl), new PropertyMetadata(60d, new PropertyChangedCallback (CoverFlowControl.OnValuesChanged)));
9.
Modify the class declaration so that it inherits from ContentControl. Modify the class declaration so that it resembles the following.
[Visual Basic] Public Class CoverFlowItemControl Inherits ContentControl
3.
Declare a class-level variable called ContentPresenter type of ContentControl. Add the following class-level variable declaration to the CoverFlowItemControl class.
[Visual Basic] Private ContentPresenter as ContentControl
4.
5.
6.
Add the following code directly beneath the comment you have just identified.
[Visual Basic] If Not IsNothing(ContentPresenter) Then AddHandler ContentPresenter.MouseLeftButtonUp, New MouseButtonEventHandler(AddressOf ContentPresenter_MouseLeftButtonUp) End If
7.
Add the following event handler directly beneath the OnApplyTemplate procedure.
[Visual Basic] Sub ContentPresenter_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs) RaiseEvent ItemSelected(Me, Nothing) End Sub
[Visual C#] void ContentPresenter_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (ItemSelected != null) { ItemSelected(this, null); } }
8.
2.
[Visual Basic] Public Property Public Property Public Property Public Property Public Property
Public Property modMediaFileThumbnailPaths As List(Of String) Public Property modMediaFileTooltips As List(Of String)
[Visual C#] public string modTitle { get; set; } public string modToolTip { get; set; } public string modImageUrl { get; set; } public List<string> modMediaFileTitles { get; set; } public List<string> modMediaFileUrls { get; set; } public List<string> modMediaFileThumbnailPaths { get; set; } public List<string> modMediaFileTooltips { get; set; }
3.
[Visual Basic] Public Sub New(ByVal xModule As XElement) modMediaFileTitles = New List(Of String) modMediaFileUrls = New List(Of String) modMediaFileThumbnailPaths = New List(Of String) modMediaFileTooltips = New List(Of String) parseXML(xModule) End Sub
[Visual C#] public CardCategory(XElement xModule) { modMediaFileTitles = new List<string>(); modMediaFileUrls = new List<string>(); modMediaFileThumbnailPaths = new List<string>(); modMediaFileTooltips = new List<string>();
parseXML(xModule);
4.
Add the following procedure directly beneath the constructor you have just added.
[Visual Basic] Sub parseXML(ByVal xModule As XElement) If Not IsNothing(xModule.Element("THUMBNAIL")) Then Dim thumbNail As XElement = xModule.Element("THUMBNAIL") If Not IsNothing(thumbNail.Attribute("path")) Then modImageUrl = thumbNail.Attribute("path").Value End If If Not IsNothing(thumbNail.Attribute("tooltip")) Then modToolTip = thumbNail.Attribute("tooltip").Value End If End If If Not IsNothing(xModule.Element("CARDS")) Then Dim xMediaFile As XElement For Each xMediaFile In _ xModule.Element("CARDS").Descendants("CARD") If Not IsNothing(xMediaFile.Attribute("previewUrl")) Then modMediaFileUrls.Add _ (xMediaFile.Attribute("previewUrl").Value) Else modMediaFileUrls.Add(String.Empty) End If If Not IsNothing(xMediaFile.Attribute("previewThumb")) Then modMediaFileThumbnailPaths.Add _ (xMediaFile.Attribute("previewThumb").Value) Else modMediaFileThumbnailPaths.Add(String.Empty) End If If Not IsNothing(xMediaFile.Attribute("tooltip")) Then modMediaFileTooltips.Add _ (xMediaFile.Attribute("tooltip").Value) Else modMediaFileTooltips.Add _ ("Tooltip has not been set in XML file") End If Next End If End Sub
[Visual C#] private void parseXML(XElement xModule) { if (xModule.Element("THUMBNAIL") != null) { XElement thumbNail = xModule.Element("THUMBNAIL"); if (thumbNail.Attribute("path") != null) { modImageUrl = thumbNail.Attribute("path").Value; } if (thumbNail.Attribute("tooltip") != null)
10
{ }
modToolTip = thumbNail.Attribute("tooltip").Value;
} if (xModule.Element("CARDS") != null) { foreach (XElement xMediaFile in xModule.Element("CARDS").Descendants("CARD")) { if (xMediaFile.Attribute("previewUrl") != null) { modMediaFileUrls.Add (xMediaFile.Attribute("previewUrl").Value); } else { modMediaFileUrls.Add(string.Empty); } if (xMediaFile.Attribute("previewThumb") != null) { modMediaFileThumbnailPaths.Add (xMediaFile.Attribute("previewThumb").Value); } else { modMediaFileThumbnailPaths.Add(string.Empty); } if (xMediaFile.Attribute("tooltip") != null) { modMediaFileTooltips.Add (xMediaFile.Attribute("tooltip").Value); } else { modMediaFileTooltips.Add ("Tooltip has not been set in XML file"); } } }
5.
11
a. b.
In the Solution Explorer window, expand the Views folder, and then double-click Home.xaml. Near the top of the XAML pane, locate the namespace declaration that reads as follows.
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
c. d.
Place the cursor at the end of the line you have just located and press Enter. Add the following namespace declaration to the empty line you have just created.
xmlns:local="clr-namespace:GreetingCardConsumer"
2.
Between the opening and closing tags of the Grid element with a name of MainGrid, add the following markup.
<local:CoverFlowControl x:Name="flowControl" Margin="0,0,0,0" Width="600"> <local:CoverFlowControl.ItemTemplate> <DataTemplate> <Border CornerRadius="1"> <Image Source="{Binding modImageUrl}" CacheMode="BitmapCache" Grid.Row="0" Height="130" ToolTipService.ToolTip="{Binding modToolTip}"/> </Border> </DataTemplate> </local:CoverFlowControl.ItemTemplate> </local:CoverFlowControl>
3.
In the GreetingCardConsumer project, open the Views\Home.xaml file in code view. In the Solution Explorer window, in the GreetingCardConsumer project, expand Views, rightclick Home.xaml, and then click View Code.
3.
4.
Add the following code directly below the comment you have just located.
12
[Visual Basic] For Each xModule As XElement in xDoc.Descendants("CATEGORY") Dim cardCat As New CardCategory(xModule) categories.Add(cardCat) Next
[Visual C#] foreach (XElement xModule in xDoc.Descendants("CATEGORY")) { CardCategory cardCategory = new CardCategory(xModule); categories.Add(cardCategory); }
5.
6.
Press Ctrl+F5 to run and test the application. The application loads categories of cards in the CoverFlowControl that you created in this lab. Each category displays a number of different cards, as specified by the GreetingCardConfig.xml file.
7.
Click each of the card categories in the CoverFlowControl to see how the contents of each category are updated. The contents of each category are represented by the XML file, and you exposed the relevant data as properties of the CardCategory class to update the other components in the user interface.
8.
Module 6
Lab Answer Key: Using Local Assets
Contents:
Exercise 1: Printing from Silverlight Applications Exercise 2: Accessing the Clipboard in Silverlight Applications Exercise 3: Accessing Isolated Storage in Silverlight Applications Exercise 4: Accessing the File System in Silverlight Applications 2 5 7 9
Open the GreetingCardConsumer solution from the D:\Labfiles\Mod06\VB\Starter or D:\Labfiles\Mod06\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod06\VB\Starter\ GreetingCardConsumer.sln or D:\Labfiles\Mod06\CS\Starter\ GreetingCardConsumer.sln, and then click Open.
3.
4.
In the GreetingCardConsumer project, in the Views folder, open Home.xaml. In the Solution Explorer window, in the GreetingCardConsumer project, expand the Views folder, and then double-click Home.xaml.
5.
Create an event handler for the Print button. In the Design pane of the Home.xaml window, double-click the Print button.
Visual Studio creates an event handler for the Click event of the button. 6. In the Home.xaml code file, bring the System.Windows.Printing namespace into scope. Add the following statement at the top of the Home.xaml.vb or Home.xaml.cs file.
7.
Add the following class-level variable declaration directly below the declaration of the viewThumbs class-level variable declaration.
8.
Locate the print_Click event handler that Visual Studio created for you.
9.
Add code to create and instantiate a new PrintDocument class named docToPrint. Create a new generic EventHandler, constrained to PrintPageEventArgs objects, and assign it to the PrintPage event of the PrintDocument object, passing the docToPrint_PrintPage method (not yet created). Finally, call the Print method of the PrintDocument object, passing the text, Silverlight Document. Add the following code to the print_Click event handler.
[Visual Basic] Dim docToPrint As New PrintDocument() AddHandler docToPrint.PrintPage, New EventHandler(Of PrintPageEventArgs)(AddressOf docToPrint_PrintPage) docToPrint.Print("Silverlight Document")
[Visual C#] PrintDocument docToPrint = new PrintDocument(); docToPrint.PrintPage += new EventHandler<PrintPageEventArgs>(docToPrint_PrintPage); docToPrint.Print("Silverlight Document");
2.
To the docToPrint_PrintPage method, add code to perform the following operations: a. b. c. d. Increment the currentPage class-level variable by one. Check if the value of the currentPage variable is less than flowControl.Items.Count, and set the value of e.HasMorePages accordingly. Assign the value of currentPage - 1 to flowControl.SelectedIndex. Assign the LayoutRootUIElement to e.PageVisual. Add the following code to the docToPrint_PrintPage method.
[Visual Basic] currentPage += 1 If currentPage < flowControl.Items.Count Then e.HasMorePages = True Else e.HasMorePages = False End If
[Visual C#] currentPage++; if (currentPage < flowControl.Items.Count) { e.HasMorePages = true; } else { e.HasMorePages = false; } flowControl.SelectedIndex = currentPage 1; e.PageVisual = LayoutRoot;
3. 4.
5. 6.
In Internet Explorer, click Print. Print to OneNote 2010. In the Print dialog box, click Send to OneNote 2010, and then click Print.
After a few seconds, OneNote appears. 7. In the Select Location in OneNote dialog box, click OK. OneNote displays a page for each card category in the Silverlight application. 8. Close OneNote, and then close Internet Explorer.
3. 4.
In the webClient_DownloadStringCompleted event handler, locate the TODO comment, Populate contentsToCopy variable. Immediately below the comment, assign the e.Result property to the contentsToCopy class-level variable. Immediately below the comment, add the following code.
[Visual Basic] contentsToCopy = e.Result
5.
Create an event handler for the Copy button by using the Design view of Home.xaml. In the Home.xaml window, double-click the Copy button.
Visual Studio creates an event handler for the Click event of the button. 6. In the Click event handler for the Copy button, copy the value of the contentsToCopy class-level variable to the clipboard. Add the following code to copy_Click event handler.
[Visual Basic] Clipboard.SetText(contentsToCopy)
7.
Create an event handler for the Paste button by using the Design view of About.xaml. In the About.xaml window, double-click the Paste button.
Visual Studio creates an event handler for the Click event of the button. 3. In the Click event handler for the Paste button, assign current text content of the clipboard to the Text property of the imContent control. Add the following code to paste_Click event handler.
[Visual Basic] imContent.Text = Clipboard.GetText() [Visual C#] imContent.Text = Clipboard.GetText();
4.
In Internet Explorer, click Copy. In the Microsoft Silverlight dialog box, click Yes. Navigate to the About page. Internet Explorer, click About.
5.
In Internet Explorer, click Paste. The contents of the clipboard are pasted into the instant messaging text box.
6.
Open Notepad. On the Start menu, click All Programs, click Accessories, and then click Notepad.
7.
Paste the copied content into Notepad. On the Edit menu, click Paste.
The contents of the clipboard are pasted into Notepad. 8. Close Notepad, not saving the changes, and then close Internet Explorer.
3.
Create a class-level variable named storage, of type IsolatedStorageSettings, and instantiate it to the value of the IsolatedStorageSettings. ApplicationSettings property. Add the following class-level variable declaration immediately below the currentPage variable.
[Visual Basic] Dim storage As IsolatedStorageSettings = IsolatedStorageSettings.ApplicationSettings
4. 5.
Switch to the Home.xaml markup file. Create an event handler for the Set Default button by using the Design view of Home.xaml. In the Home.xaml window, double-click the Set Default button.
Visual Studio creates the snapshot_Click event handler for the Click event of the button. 6. Add code to the snapshot_Click event handler to check if the storage variable contains a setting named currentCategory. If it does, set the currentCategory setting to the value of flowControl.SelectedIndex; otherwise, add the currentCategory setting with a value of flowControl.SelectedIndex. Add the following code to the snapshot_Click event handler.
[Visual Basic] If storage.Contains("currentCategory") Then storage("currentCategory") = flowControl.SelectedIndex Else storage.Add("currentCategory", flowControl.SelectedIndex) End If
7.
3.
Use the coverflow control to navigate to the Greeting Cards for General Occasions category. Click Set Default. Close Internet Explorer. Run the application again. Press Ctrl+F5. Note that the Greeting Cards for General Occasions category is selected by default.
6.
Open the GreetingCardManagement solution from the D:\Labfiles\Mod06\VB\Starter or D:\Labfiles\Mod06\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod06\VB\Starter\ GreetingCardManagement.sln or D:\Labfiles\Mod06\CS\Starter\ GreetingCardManagement.sln, and then click Open.
3.
4.
In the GreetingCardManagement project, in the Views folder, open Home.xaml. In the Solution Explorer window, in the GreetingCardManagement project, expand the Views folder, and then double-click Home.xaml.
5.
Create an event handler for the Save Diagnostic File button. In the Design pane of the Home.xaml window, double-click the Save Diagnostic File button.
Visual Studio creates an event handler for the Click event of the button. 6. Add the following code to the save_Click event handler.
[Visual Basic] Try Dim filePathName As String = _ System.IO.Path.Combine(Environment.GetFolderPath( _ Environment.SpecialFolder.MyDocuments), _ "GreetingCardDiagnostics.txt") If (File.Exists(filePathName)) Then File.Delete(filePathName) End If Dim fileContents As StreamWriter = File.CreateText(filePathName) Dim displayName As String = "Your Display Name is: " & _ displayNameTextBox.Text Dim email As String = "Your Email Address is: " & _ emailTextBox.Text fileContents.WriteLine(displayName) fileContents.WriteLine(email) fileContents.Close() Catch secEx As System.Security.SecurityException ErrorWindow.CreateNew(New Exception( _ "Only out-of-browser applications can save files")) Catch ex As Exception ErrorWindow.CreateNew(ex) End Try
10
[Visual C#] try { string filePathName = System.IO.Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), "GreetingCardDiagnostics.txt"); if (File.Exists(filePathName)) File.Delete(filePathName); StreamWriter fileContents = File.CreateText(filePathName); string displayName = "Your Display Name is: " + displayNameTextBox.Text; string email = "Your Email Address is: " + emailTextBox.Text; fileContents.WriteLine(displayName); fileContents.WriteLine(email); fileContents.Close();
} catch (System.Security.SecurityException secEx) { ErrorWindow.CreateNew(new Exception ("Only out-of-browser applications can save files")); } catch (Exception ex) { ErrorWindow.CreateNew(ex); }
7.
Create an event handler for the Open Diagnostic File button. In the Design pane of the About.xaml window, double-click the Open Diagnostic File button.
Visual Studio creates an event handler for the Click event of the button. 3. Add the following code to the open_Click event handler.
[Visual Basic] Try Dim filePathName As String = System.IO.Path.Combine( _ Environment.GetFolderPath( _ Environment.SpecialFolder.MyDocuments), _ "GreetingCardDiagnostics.txt") If Not File.Exists(filePathName) Then ErrorWindow.CreateNew(New Exception("Diagnostic file does not exist")) Return End If Dim fileContents As StreamReader = New StreamReader(filePathName) imContent.Text = fileContents.ReadToEnd() fileContents.Close()
11
Catch secEx As System.Security.SecurityException ErrorWindow.CreateNew(New Exception( _ "Only out-of-browser applications can open files ")) Catch ex As Exception ErrorWindow.CreateNew(ex) End Try
[Visual C#] try { string filePathName = System.IO.Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), "GreetingCardDiagnostics.txt"); if (!File.Exists(filePathName)) { ErrorWindow.CreateNew(new Exception ("Diagnostic file does not exist")); } return;
} catch (System.Security.SecurityException secEx) { ErrorWindow.CreateNew(new Exception ("Only out-of-browser applications can open files")); }
4.
Ensure the application can be run out of the browser. Ensure the Enable running application out of the browser check box is selected.
3.
Open the Out-of-Browser Settings dialog box. In the Project Designer, click Out-of-Browser Settings.
4.
Ensure the application has elevated permissions when running outside the browser, and close the Out-of-Browser Settings dialog box. Ensure the Require elevated trust when running outside the browser check box is selected, and then click OK.
5.
6.
12
Note The error window displays your error message because you are not running the application outside the browser. 7. Close the error window. 8. In the error window, click OK.
Install the GreetingCardManagement application locally. Right-click the background of the Silverlight application, click Install GreetingCard Desktop Management onto this computer.
The Security Warning dialog box appears. 9. Accept the security warning and continue with the installation. In the Security Warning dialog box, click Install.
The application opens in its own window. 10. Close Internet Explorer. 11. In the GreetingCard management application, save the diagnostic file. Click Save Diagnostic File.
12. Open the GreetingCardDiagnostics.txt file saved to the Documents folder, in Notepad, and review the contents. a. b. c. On the Start menu, click Documents. Double-click GreetingCardDiagnostics.txt. Review the contents of the file, which should contain the user details for Hatim Aiad.
13. Close Notepad. 14. Switch back to the GreetingCardManagement application. 15. Navigate to the About page. In the GreetingCardManagement application, click About.
17. Review the content that is added to the instant messaging text box. It should be the same content you saw in the diagnostic file. 18. Close all open applications, including Visual Studio. You have now completed this lab.
Module 7
Lab Answer Key: Implementing Advanced Media Techniques in Silverlight
Contents:
Exercise 1: Adding a Deep Zoom Image Exercise 2: Adding a Media Player 2 6
Create a new Silverlight application named GreetingCardGreenfield in the D:\Labfiles\Mod07\VB\Starter or D:\Labfiles\Mod07\CS\Starter folder, by using the Silverlight Business Application template. a. b. c. d. e. f. On the File menu, point to New, and then click Project. In the Installed Templates section, expand Visual Basic or Visual C#, and then click Silverlight. In the list of project types, click Silverlight Business Application. In the Name box, type GreetingCardGreenfield. In the Location box, type D:\Labfiles\Mod07\VB\Starter or D:\Labfiles\Mod07\CS\Starter. Click OK.
A Silverlight project named GreetingCardGreenfield is created. A website named GreetingCardGreenfield.Web for hosting the Silverlight application and its services is also created.
Create a new project named DeepZoomImage in the D:\Labfiles\Mod07\VB\Starter or D:\Labfiles\Mod07\CS\Starter folder. a. b. In the dialog box, on the Projects tab, click New Project. In the New Project dialog box, in the Name box, type DeepZoomImage, in the Location box, type D:\Labfiles\Mod07\VB\Starter or D:\Labfiles\Mod07\CS\Starter, and then click OK.
3.
Add the images from the C:\Users\Public\Pictures\Sample Pictures folder to the solution. a. b. c. d. Click Add image. Navigate to the C:\Users\Public\Pictures\Sample Pictures folder. Select the first image. Press and hold down Shift, click the last image, and then click Open.
2. 3.
Drag all of the images from the Images pane onto the work space. Align the 8 images in two rows of 4 equally sized images.
Set the following settings. Output type: Silverlight Deep Zoom Name: DeepZoomImage Export options: Export as a collection (multiple images) Templates: Deep Zoom Navigation (Default)
3. 4.
Click Export. Close Deep Zoom Composer, saving the project. a. b. c. In the Export Completed dialog box, click Close. In the Deep Zoom Composer window, click the Close button. In the Deep Zoom Composer dialog box, click Yes.
c.
2. 3.
Close Windows Explorer and switch to Visual Studio. In the MainPage.xaml window, change the Grid control to a StackPanel control with no property values set explicitly. Change the following markup:
<Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}"> </Grid>
4.
Add a MultiScaleImage control named, dzMultiScaleImage, to the StackPanel control. Set the Source property to http://localhost:26982/GeneratedImages/dzc_output.xml. Add the following markup to the StackPanel control.
<MultiScaleImage Name="dzMultiScaleImage" Source="http://localhost:26982/GeneratedImages/dzc_output.xml" />
5.
The port number is most likely different on your setup, so use the Project Designer for the GreetingCardGreenfield.Web project to find the actual port number. a. b. c. d. In the Solution Explorer window, right-click GreetingCardGreenfield.Web, and then click Properties. In the Project Designer, click Web. On the Web page, in the Servers section, note the number displayed in the disabled Specific port box. Close the Project Designer.
6. 7.
If the port number is different than 26982, change the number in markup after localhost:. Build the solution. On the Build menu, click Build Solution.
The deep zoom image will display in the Design view. 8. Run the application to see the results. 9. Press Ctrl+F5.
2.
Create an event handler for the Click event for each of the two Button controls. a. b. c. In the Design view, double-click the zoomIn control. Switch back to the MainPage.xaml window. In the Design view, double-click the zoomOut control.
3.
Add code to the zoomIn_Click event handler, to zoom in on the image, by using the ZoomAboutLogicalPoint method, with a zoom parameter value of 3, a logicalPoint.X value of 0.5, and a logicalPoint.Y value of 0.5.
4.
Add code to the zoomOut_Click event handler, to zoom out of the image, by using the ZoomAboutLogicalPoint method, with a zoom parameter value of 0.3, a logicalPoint.X value of 0.5, and a logicalPoint.Y value of 0.5. Add the following code to the zoomOut_Click event handler.
[Visual Basic] dzMultiScaleImage.ZoomAboutLogicalPoint(0.3, 0.5, 0.5)
5.
Run the application to see the results, by pressing both buttons several times. Press Ctrl+F5.
6.
e. f.
In the Media.xaml window, add the following markup written with bold formatting.
<UserControl x:Class="Media.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="100" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <MediaElement Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Source="http://localhost:26982/Wildlife.wmv" Name="videoMediaElement" /> </Grid> </UserControl>
3.
If the port number is different than 26982, change the number in markup after localhost:. You checked the port number in the previous exercise.
In the App class constructor, set the VisualRoot property of the App class to a new instance of the MediaUserControl. Set the App.VisualRoot property to the following value.
[Visual Basic] Me.RootVisual = New Media()
3.
Run the application to see the results, and notice that the playback starts automatically (after the page and video has been loaded). Press Ctrl+F5.
4. 5.
2.
Create an event handler for the Click event for each of the three Button controls. a. b. c. d. e. In the Design view, double-click the stopButton control. Switch back to the Media.xaml window. In the Design view, double-click the pauseButton control. Switch back to the Media.xaml window. In the Design view, double-click the playButton control.
3.
Add code to the stopButton_Click event handler to call the Stop method of the MediaElement control. Add the following code to the stopButton_Click event handler.
[Visual Basic] videoMediaElement.Stop()
4.
Add code to the pauseButton_Click event handler to call the Pause method of the MediaElement control. Add the following code to the pauseButton_Click event handler.
[Visual Basic] videoMediaElement.Pause()
5.
Add code to the playButton_Click event handler to call the Play method of the MediaElement control. Add the following code to the playButton_Click event handler.
[Visual Basic] videoMediaElement.Play()
6.
Run the application and demonstrate the video controls. a. b. c. d. e. Press Ctrl+F5. After the video has loaded and started playing, click Pause. Click Play to resume the playback. Click Stop to stop playback. Click Play to start playback from the beginning.
7. 8.
Close Internet Explorer. Close Visual Studio. You have now completed this lab.
Module 8
Lab Answer Key: Developing Silverlight Media Framework Solutions
Contents:
Exercise 1: Adding Support for the Silverlight Media Framework Exercise 2: Configuring the Silverlight Media Framework Player 2 4
Open the GreetingCardConsumer solution from the D:\Labfiles\Mod08\VB\Starter or D:\Labfiles\Mod08\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod08\VB\Starter\GreetingCardConsumer.sln or D:\Labfiles\Mod08\CS\Starter\GreetingCardConsumer.sln, and then click Open.
3.
To access the SMF functionality, add a reference to the following assemblies, all located in the D:\microsoft-smf-bin-2.2010.1221.1 folder: a. b. c. d. e. Microsoft.SilverlightMediaFramework.Core.dll Microsoft.SilverlightMediaFramework.Plugins. ClearTextCaptions.dll Microsoft.SilverlightMediaFramework.Plugins.dll Microsoft.SilverlightMediaFramework.Plugins. Heuristics.dll Microsoft.SilverlightMediaFramework.Plugins. Progressive.dll Microsoft.SilverlightMediaFramework.Plugins. SmoothStreaming.dll Microsoft.SilverlightMediaFramework.Plugins. TimedText.dll Microsoft.SilverlightMediaFramework.Utilities.dll In Solution Explorer, right-click the GreetingCardConsumer project, and then click Add Reference. In the Add Reference dialog box, click Browse. Navigate to the D:\microsoft-smf-bin-2.2010.1221.1 folder. SelectMicrosoft.SilverlightMediaFramework.Core.dll. Press and hold down Ctrl, and then select the following files: Microsoft.SilverlightMediaFramework.Plugins. ClearTextCaptions.dll
f. 4.
Microsoft.SilverlightMediaFramework.Plugins.dll Microsoft.SilverlightMediaFramework.Plugins. Heuristics.dll Microsoft.SilverlightMediaFramework.Plugins. Progressive.dll Microsoft.SilverlightMediaFramework.Plugins. SmoothStreaming.dll Microsoft.SilverlightMediaFramework.Plugins. TimedText.dll Microsoft.SilverlightMediaFramework.Utilities.dll
Click OK.
In the XAML pane, locate the namespace declaration that reads as follows.
xmlns:navigation="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
3. 4.
Place the cursor at the end of the line you have just located, and then press Enter. On the empty line you have just created, add the following namespace declaration.
xmlns:SMF="clr-namespace:Microsoft.SilverlightMediaFramework.Core; assembly=Microsoft.SilverlightMediaFramework.Core"
Note 5.
Ensure you add the above namespace declaration all on a single line.
6.
Import the following namespaces in the code file: Microsoft.SilverlightMediaFramework.Core Microsoft.SilverlightMediaFramework.Core.Media Microsoft.SilverlightMediaFramework.Plugins. Primitives
7.
In the body section of the page, locate the object tag and review the initParams data. Note Note how the path to a video and a path to transcript file are provided as initiation parameters, in addition to the configXmlFileUri parameter that you have already worked with in previous labs.
3.
Open VideoTranscript.xml in the ClientBin folder, and review the transcript markup. a. b. Expand the ClientBin folder. Double-click VideoTranscript.xml.
3.
To add a media player to the About page, wrapped in a Canvas element, add the following markup directly beneath the comment that you have just located.
<Canvas x:Name="videoPlayer" Canvas.ZIndex="0" Width="350" Height="310"> <Canvas.RenderTransform> <TranslateTransform x:Name="videoPlayerPosition" X="10" Y="10"></TranslateTransform> </Canvas.RenderTransform> <SMF:SMFPlayer x:Name="xVideoPlayer" AutoLoad="False" AutoPlay="False" PlaylistVisibility="Disabled" ScriptableName="VideoPlayer" CaptionsVisibility="Visible" ChaptersVisibility="Disabled" LoggingConsoleVisibility="Disabled" PlayerGraphVisibility="Disabled" VersionInformationVisibility="Disabled" AllowFullScreenPinning="True" Width="350" Height="310"/> </Canvas>
4.
3.
Add the following code directly beneath the comment you have just located.
[Visual Basic] Public Shared vidToResize As SMFPlayer Public Shared vidToMove As TranslateTransform Public Shared vidContainer As Canvas Public Shared Sub PositionVideo() Dim thisVid As SMFPlayer = App.vidToResize Dim thisVidPos As TranslateTransform = App.vidToMove Dim thisVidContainer As Canvas = App.vidContainer If Application.Current.Host.Content.IsFullScreen = False Then ' Reset to default position thisVidContainer.Height = 310 thisVid.Height = 310 thisVidContainer.Width = 350 thisVid.Width = 350 thisVidPos.X = 10 thisVidPos.Y = 10 Return End If ' Make the video fill the screen thisVid.Height = _ Application.Current.Host.Content.ActualHeight - 100 thisVid.Width = _ Application.Current.Host.Content.ActualWidth - 5 thisVidContainer.Height = _ Application.Current.Host.Content.ActualHeight - 100 thisVidContainer.Width = _ Application.Current.Host.Content.ActualWidth - 5 thisVidPos.X = 0 thisVidPos.Y = 0 End Sub
[Visual C#] public static SMFPlayer vidToResize; public static TranslateTransform vidToMove; public static Canvas vidContainer; public static void PositionVideo() { SMFPlayer thisVid = App.vidToResize; TranslateTransform thisVidPos = App.vidToMove; Canvas thisVidContainer = App.vidContainer; if (!Application.Current.Host.Content.IsFullScreen) { // Reset to default position thisVidContainer.Height = 310; thisVid.Height = 310; thisVidContainer.Width = 350; thisVid.Width = 350; thisVidPos.X = 10; thisVidPos.Y = 10; return;
} // Make the video fill the screen thisVid.Height = Application.Current.Host.Content.ActualHeight - 100; thisVid.Width = Application.Current.Host.Content.ActualWidth - 5; thisVidContainer.Height = Application.Current.Host.Content.ActualHeight - 100; thisVidContainer.Width = Application.Current.Host.Content.ActualWidth - 5; thisVidPos.X = 0; thisVidPos.Y = 0;
4. 5.
Open About.xaml in Code view. Add the following code to the Page_Loaded or About_Loaded event handler.
[Visual Basic] App.vidToResize = xVideoPlayer App.vidContainer = videoPlayer App.vidToMove = videoPlayerPosition App.PositionVideo() AddHandler xVideoPlayer.FullScreenChanged, _ AddressOf xVideoPlayer_FullScreenChanged
[Visual C#] App.vidToResize = xVideoPlayer; App.vidContainer = videoPlayer; App.vidToMove = videoPlayerPosition; App.PositionVideo(); xVideoPlayer.FullScreenChanged += new EventHandler(xVideoPlayer_FullScreenChanged);
6.
7.
currentVid.DeliveryMethod = DeliveryMethods.ProgressiveDownload
[Visual C#] string videoUri = App.Current.Resources["videoPath"].ToString(); xVideoPlayer.CurrentPlaylistItem = null; xVideoPlayer.Playlist.Clear(); PlaylistItem currentVid = new PlaylistItem(); currentVid.MediaSource = new Uri(videoUri, UriKind.Absolute); currentVid.DeliveryMethod = DeliveryMethods.ProgressiveDownload;
2.
} catch { // Silent failure if captions are missing // Video will play without captions. }
MarkerResource vidMarkerResource = new MarkerResource(); vidMarkerResource.Format = "TTAF1-DFXP"; vidMarkerResource.Source = new Uri(captionUri, UriKind.Absolute); currentVid.MarkerResource = vidMarkerResource;
2.
xVideoPlayer.Play()
2. 3.
4.
Click About. Note The video plays and displays the captions from the caption file.
5.
Near the lower part of the video player, click CC. Note The captions are hidden.
6. 7. 8.
Click CC to turn the captions back on. Near the lower-right of the video player, click the full screen button. In the Microsoft Silverlight dialog box, click Yes. Note The application switches to full screen view and the video is resized and positioned to occupy most of the screen. You wrote the code to position and resize the video in this lab.
9.
Note The application switches out of full screen view and the video is resized and positioned to occupy its original location. You wrote the code to position and resize the video in this lab. 10. Close all applications.
Module 9
Lab Answer Key: Accessing Hardware in Silverlight Applications
Contents:
Exercise 1: Interacting with the Mouse Wheel Exercise 2: Interacting with the Keyboard 2 6
Open the GreetingCardConsumer solution from the D:\Labfiles\Mod09\VB\Starter or D:\Labfiles\Mod09\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod09\VB\Starter\ GreetingCardConsumer.sln or D:\Labfiles\Mod09\CS\Starter\ GreetingCardConsumer.sln, and then click Open.
3.
In the GreetingCardConsumer project, in the Views folder, open Home.xaml in Code view. In the Solution Explorer window, expand Views, right-click Home.xaml, and then click View Code.
4.
5.
Add code directly beneath the comment you have just located, to attach the following event handlers to the corresponding event of the specified object, by using the AttachEvent method. This will help ensure the mouse wheel works on different platforms than Windows, and different browsers than Internet Explorer. Event Name DOMMouseScroll onmousewheel onmousewheel Event Handler OnMouseWheel OnMouseWheel OnMouseWheel
Add the following code directly beneath the comment you have just located.
[Visual Basic] HtmlPage.Window.AttachEvent("DOMMouseScroll", AddressOf OnMouseWheel) HtmlPage.Window.AttachEvent("onmousewheel", AddressOf OnMouseWheel) HtmlPage.Document.AttachEvent("onmousewheel", AddressOf OnMouseWheel)
6.
Overload the private event handler named, OnMouseWheel, with the usual event handler signature, accepting the sender argument of type Object/object, and args argument of type HtmlEventArgs. Add code to suppress the events by calling the PreventDefault method of the args argument. This will help ensure the mouse wheel works on different platforms than Windows, and different browsers than Internet Explorer. Add the following code directly beneath the Page_Loaded or Home_Loaded event handler.
[Visual Basic] Private Overloads Sub OnMouseWheel(ByVal sender As object, ByVal args As HtmlEventArgs) args.PreventDefault() End Sub
7.
2.
Append code to the Page_Loaded or Home_Loaded event handler, to add an event handler for the MouseWheel event of the flowControl object. Append the following code to the Page_Loaded or Home_Loaded event handler.
[Visual Basic] AddHandler flowControl.MouseWheel, AddressOf flowControl_MouseWheel
3.
Add an event handler named, flowControl_MouseWheel, with the usual event handler signature, accepting the sender argument of type Object/object, and e argument of type MouseWheelEventArgs. Add the event handler directly beneath the Page_Loaded or Home_Loaded event handler. Add the following code directly beneath the Page_Loaded or Home_Loaded event handler.
[Visual Basic] Sub flowControl_MouseWheel(ByVal sender As object, ByVal e As MouseWheelEventArgs) End Sub
4.
Add code to the flowControl_MouseWheel event handler to perform the following operations: a. Create a variable named, i, of type Integer/int, and assign the value of the Delta property of the MouseWheelEventArgs parameter, divided by 120. Negate the result before assigning. This means the control will navigate one cover for each mouse scroll. Create a variable named, index, of type Integer/int, and assign the value of the flowControl.SelectedIndex property, plus the value of the variable i. This means the control will navigate one cover for each mouse scroll. Check if the value of index is less than 0, and if it is, call the First method of the flowControl object. If not, check if index is larger than the value of the flowControl.Items.Count property, minus 1. If it is, call the Last method of the flowControl object. If not, set the flowControl.SelectedIndex property to the value of the index variable. This ensures the selected cover will never be less than 0 or higher than the last cover. Add the following code to the flowControl_MouseWheel event handler.
[Visual Basic] Dim i As Integer = -(e.Delta \ 120) Dim index As Integer = flowControl.SelectedIndex + i If index < 0 Then flowControl.First() ElseIf index > flowControl.Items.Count 1 Then flowControl.Last() Else flowControl.SelectedIndex = index End If
b.
c.
[Visual C#] int i = -(e.Delta / 120); int index = flowControl.SelectedIndex + i; if (index < 0) { flowControl.First(); } else if (index > flowControl.Items.Count - 1) { flowControl.Last(); } else { flowControl.SelectedIndex = index; }
5.
2. 3.
Press Ctrl+F5.
Click the flow control, point to the categories it contains, and then use the mouse wheel to navigate back and forth through the categories. Close Internet Explorer.
2.
In the GreetingCardConsumer project, open the CoverFlowControl code file. In the Solution Explorer window, double-click CoverFlowControl.vb or CoverFlowControl.cs.
3.
Locate the OnKeyDown event handler. Notice the code in the OnKeyDown event handler, which handles the cursor keys for navigating through the covers.
[Visual Basic] Public Shadows Sub OnKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) If e.Key = Key.Right OrElse e.Key = Key.Down OrElse e.Key = Key.PageDown Then NextItem() e.Handled = True ElseIf e.Key = Key.Left OrElse e.Key = Key.Up OrElse e.Key = Key.PageUp Then PreviousItem() e.Handled = True ElseIf e.Key = Key.Home OrElse e.Key = Key.Q Then First() e.Handled = True ElseIf e.Key = Key.End OrElse e.Key = Key.E Then Last() e.Handled = True End If End Sub
[Visual C#] public void OnKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Right || e.Key == Key.Down || e.Key == Key.PageDown) { NextItem(); e.Handled = true; } else if (e.Key == Key.Left || e.Key == Key.Up || e.Key == Key.PageUp) { PreviousItem(); e.Handled = true; } else if (e.Key == Key.Home || e.Key == Key.Q) { First(); e.Handled = true; } else if (e.Key == Key.End || e.Key == Key.E)
{ }
4.
Locate the IndexSelected method that accepts three arguments, and add code at the top of the method to set focus to the current object. Add the following code at the top of the IndexSelected method.
[Visual Basic] ' Set focus so it receives key events Me.Focus()
5.
Use the Down, Up, Right, and Left cursor keys to navigate through the card categories in the coverflow control. The Page control cannot receive focus, so it does not receive the keyboard input. The Home control is of type Page.
3.
Click the coverflow control. The coverflow control can receive focus, so it receives the keyboard input.
4. 5.
Use the Down, Up, Right, and Left cursor keys to navigate through the card categories in the coverflow control. Click the Set Default button. The Button control can receive focus, so it receives the keyboard input.
6. 7. 8. 9.
Use the Page Up and Page Down keys to navigate through the card categories in the coverflow control. Use the Home and End keys to navigate through the card categories in the coverflow control. Close Internet Explorer. Close Visual Studio. You have now completed this lab.
Module 10
Lab Answer Key: Globalization and Localization
Contents:
Exercise 1: Globalizing an Application Exercise 2: Localizing an Application 2 6
Open the GreetingCardConsumer solution from the D:\Labfiles\Mod10\VB\Starter or D:\Labfiles\Mod10\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod10\VB\Starter\ GreetingCardConsumer.sln or D:\Labfiles\Mod10\CS\Starter\ GreetingCardConsumer.sln, and then click Open.
3.
4.
In the GreetingCardConsumer project, open App.xaml. In the Solution Explorer window, double-click App.xaml.
5.
The sealed ResourceWrapper class is defined in the ResourceWrapper.vb or ResourceWrapper.cs file, located in the Helpers folder. The file is automatically generated when you create a Silverlight by using the Silverlight Business Application project template. 6. In the GreetingCardConsumer project, in the Helpers folder, open ResourceWrapper.vb or ResourceWrapper.cs. 7. In the Solution Explorer window, expand Helpers, and then double-click ResourceWrapper.vb or ResourceWrapper.cs.
In the ResourceWrapper.vb or ResourceWrapper.cs file, notice that two read-only properties exist, ApplicationStrings and SecurityQuestions. These two properties return the following shared/static member fields: _applicationStrings or applicationStrings, and _securityQuestions or securityQuestions. They are of type ApplicationStrings and SecurityQuestions. In the GreetingCardConsumer project, in the Assets\Resources folder, open ApplicationStrings.resx. In the Solution Explorer window, expand Assets, expand Resources, and then double-click ApplicationStrings.resx.
8.
The Resource Designer opens. 9. In the Resource Designer, notice the Name column, which holds the resource key, used for looking up a specific value, which is shown in the Value column. One example is the ApplicationName key, which has a current value of Greeting Card Consumer.
10. In the GreetingCardConsumer project, open MainPage.xaml. In the Solution Explorer window, double-click MainPage.xaml.
11. In the MainPage.xaml window, in the XAML view, locate the TextBlock control named ApplicationNameTextBlock.
<TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}" Text="{Binding ApplicationStrings.ApplicationName, Source={StaticResource ResourceWrapper}}"/>
12. Notice that the Text property is bound to the ApplicationStrings.ApplicationName property, found in the ResourceWrapper resource. When you build the application, the ApplicationStrings class is automatically generated by the resource compiler, from the resource files, .resx, making it accessible from within your application. 13. The two HyperlinkButton controls have also been bound to properties in the ApplicationStrings class, so at this stage, MainPage.xaml needs no further attention. 14. Close MainPage.xaml.
3.
In the StackPanel control, locate the copyButton control, and notice the Content property holds a static or hardcoded string, Copy. The same is true for the print and snapshotButton controls.
<Button Grid.Row="1" Grid.Column="1" Content="Copy" Height="23" HorizontalAlignment="Left" Name="copy" VerticalAlignment="Top" Width="75" />
4.
Add a resource file named HomeStrings.resx to the Assets/Resources folder. In the Solution Explorer window, right-click Resources, point to Add, and then click New Item. In the Add New Item GreetingCardConsumer dialog box, in the Installed Templates section, click General. In the middle pane, click Resources File. In the Name box, type HomeStrings.resx, and then click Add.
5.
Make the resources publicly available. In the Resource Designer, in the Access Modifier list, click Public.
6.
Set the custom tool namespace for the HomeStrings.resx file to an empty string (Visual Basic) or GreetingCardConsumer (Visual C#), by using the Properties window. In the Solution Explorer window, click HomeStrings.resx. In the Properties window, in the Custom Tool Namespace box, type an empty string or GreetingCardConsumer, and then press Enter.
7.
8. 9.
Switch back to the Resource Designer. Add a new resource string named CopyButton with a value of Copy. In the Resource Designer, in the Name column for the first and only row, type CopyButton, and in the Value column, type Copy.
10. Add a new resource string named PrintButton with a value of Print. In the Resource Designer, in the Name column for the second row, type PrintButton, and in the Value column, type Print.
11. Add a new resource string named SnapShotButton with a value of Set Default. In the Resource Designer, in the Name column for the third row, type SnapShotButton, and in the Value column, type Set Default.
12. Build the solution. On the Build menu, click Build Solution.
13. Switch to ResourceWrapper.vb or ResourceWrapper.cs. 14. Create a new shared/static member level field named _homeStrings or homeStrings of type HomeStrings. In the ResourceWrapper class, add the following code, immediately after the _securityQuestions or securityQuestions definition.
[Visual Basic] Private Shared _homeStrings = New HomeStrings()
15. Create a new read-only property named HomeStrings of type HomeStrings. The property must return the shared/static member variable _homeStrings or homeStrings. In the ResourceWrapper class, add the following code, immediately after the _securityQuestions or securityQuestions definition.
[Visual Basic] Public ReadOnly Property HomeStrings() As HomeStrings Get Return _homeStrings End Get End Property
16. Switch to Home.xaml. 17. In the XAML view, modify the Content property of the copyButton control, so it contains the following value:
{Binding HomeStrings.CopyButton, Source={StaticResource ResourceWrapper}}
18. In the XAML view, modify the Content property of the printButton control, so it contains the following value:
{Binding HomeStrings.PrintButton, Source={StaticResource ResourceWrapper}}
19. In the XAML view, modify the Content property of the snapshotButton control, so it contains the following value:
{Binding HomeStrings.SnapShotButton, Source={StaticResource ResourceWrapper}}
21. Check to see if the button captions contain the text from the resource files. 22. Close Internet Explorer.
Make the resources publicly available. In the Resource Designer, in the Access Modifier list, click Public.
3.
Set the custom tool namespace for the HomeStrings.de-DE.resx file to an empty string (Visual Basic) or GreetingCardConsumer (Visual C#) by using the Properties window. In the Solution Explorer window, click HomeStrings.de-DE.resx. In the Properties window, in the Custom Tool Namespace box, type an empty string or GreetingCardConsumer, and then press Enter.
4.
5. 6.
Switch back to the Resource Designer. Add a new resource string named CopyButton with a value of Kopieren. In the Resource Designer, in the Name column for the first and only row, type CopyButton, and in the Value column, type Kopieren.
7.
Add a new resource string named PrintButton with a value of Ausdrucken. In the Resource Designer, in the Name column for the second row, type PrintButton, and in the Value column, type Ausdrucken.
8.
Add a new resource string named SnapShotButton with a value of Standard. In the Resource Designer, in the Name column for the third row, type SnapShotButton, and in the Value column, type Standard.
9.
Set the default or neutral culture to English. a. b. On the Silverlight page, click Assembly Information. In the Assembly Information dialog box, in the Neutral Language list, click English, and then click OK.
3.
In the constructor for the MainPage class, after the call to the InitializeComponent method add code to set the CurrentUICulture property of the current thread to a new CultureInfo object, based on the de-DE culture. In the MainPage.xaml.vb or MainPage.xaml.cs window, to the constructor, add the following code after the call to the InitializeComponent method.
[Visual Basic] System.Threading.Thread.CurrentThread.CurrentUICulture = New System.Globalization.CultureInfo("de-DE")
3.
Run the application and notice that the resource values displayed in the Button controls have been localized. Press Ctrl+F5.
4. 5.
Close Internet Explorer. Close Visual Studio. You have now completed this lab.
Module 11
Lab Answer Key: Implementing Network Communications
Contents:
Exercise 1: Consuming Initiation Parameters Exercise 2: Consuming Data by Using HTTPWebRequest Objects Exercise 3: Consuming Data by Using WebClient Objects Exercise 4: Sending and Receiving Data by Using Local Connections 2 5 8 10
Open the GreetingCardConsumer solution from the D:\Labfiles\Mod11\VB\Starter or D:\Labfiles\Mod11\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod11\VB\Starter\ GreetingCardConsumer.sln or D:\Labfiles\Mod11\CS\Starter\ GreetingCardConsumer.sln, and then click Open.
3.
In the GreetingCardConsumer.Web project, open GreetingCardConsumerTestPage.aspx. In the Solution Explorer window, in the GreetingCardConsumer.Web project, double-click GreetingCardConsumerTestPage.aspx.
4.
5.
Add a new line immediately after the located markup. Place the cursor at the end of the line you have just located, and then press Enter.
6.
Add the following markup to the empty line you have just created.
Note 7. 8.
Save the changes. In the GreetingCardConsumer.Web project, in the ClientBin folder, open GreetingCardColors.xml. a. b. In the GreetingCardConsumer.Web project, expand ClientBin. Double-click GreetingCardColors.xml and briefly review the contents of the file.
You will consume this data in Exercise 2. 9. In the GreetingCardConsumer.Web project, in the ClientBin folder, open GreetingCardConfig.xml. Double-click GreetingCardConfig.xml and briefly review the contents of the file.
Task 2: Retrieve the initiation parameters at run time and add them to the application Resources collection.
1. In the GreetingCardConsumer project, open App.xaml in Code view. 2. In the GreetingCardConsumer project, right-click App.xaml, and then click View Code.
TODO: Read Init Params and add them to the Resources Collection
3.
Add code directly beneath the comment that checks if e.InitParams is not null, and if so, creates a new local variable name item of type Object/object. Subsequently, loop through the items in the e.InitParams collection and add them to the Resources collection by using the Add method, passing the Key and Value properties of the current item. Add the following code directly beneath the comment.
[Visual Basic] If Not IsNothing(e.InitParams) Then Dim item As Object For Each item In e.InitParams Me.Resources.Add(item.Key, item.Value) Next End If
[Visual C#] if (e.InitParams != null) { foreach (var item in e.InitParams) { this.Resources.Add(item.Key, item.Value); } }
4.
3.
Directly beneath the comment you have just located, add code to create the following local variables, assigned specific initial values. Variable Type String/string Initial Value App.Current.Resources("colorXmlFileUri").
Variable Name
Variable Type
categorySettingsUri
String/string
[Visual Basic] Dim colorSettingsUri As String = _ App.Current.Resources("colorXmlFileUri").ToString() Dim categorySettingsUri As String = _ App.Current.Resources("configXmlFileUri").ToString()
4.
Add code immediately after the previously added code, to show the value of the colorSettingsUri and categorySettingsUri variables, each in a separate message box. Add the following code immediately after the previously added code.
5. 6.
Two message boxes should be displayed showing the URLs you will use later in the lab. Take a note of these URLs. 7. Close Internet Explorer and switch back to Visual Studio 2010.
In the Home_Loaded or Page_Loaded event handler, select the two calls to the MessageBox.Show method, and replace with the following code.
2.
In the Home_Loaded or Page_Loaded event handler, append code to call the BeginGetResponse method of the httpRequest object. Pass in a new AsyncCallback object with the httpRequest_CallBack method and the httpRequest object. In the Home_Loaded or Page_Loaded event handler, append the following code.
2.
Add the following method after the variable declaration you have just added.
[Visual Basic] Sub httpRequest_CallBack(ByVal result As IAsyncResult) Dim httpRequest As HttpWebRequest = result.AsyncState Dim httpResponse As HttpWebResponse = _ httpRequest.EndGetResponse(result) Dim strmReader As StreamReader = _ New StreamReader(httpResponse.GetResponseStream()) Using (strmReader) settings = strmReader.ReadToEnd() End Using Dispatcher.BeginInvoke(AddressOf setColors) End Sub
[Visual C#] void httpRequest_CallBack(IAsyncResult result) { HttpWebRequest httpRequest = (HttpWebRequest) result.AsyncState; HttpWebResponse httpResponse = (HttpWebResponse) httpRequest.EndGetResponse(result); using (StreamReader strmReader = new StreamReader(httpResponse.GetResponseStream())) { settings = strmReader.ReadToEnd(); } } Dispatcher.BeginInvoke(setColors);
Task 3: Create a function that updates the user interface from the main UI thread.
1. Add the following method after the method you have just added.
[Visual Basic] Sub setColors() Dim xDoc As XDocument = XDocument.Parse(settings) Dim root As XElement = xDoc.Root Dim bannerColor As String = String.Empty Dim backgroundColor As String = String.Empty Dim playerSettings As XElement = root.Element("PLAYERSETTINGS") Dim colorSettings As XElement = _ playerSettings.Element("COLORSETTINGS") If Not IsNothing(colorSettings.Attribute("bannerColor")) Then bannerColor = colorSettings.Attribute("bannerColor").Value End If If Not IsNothing(colorSettings.Attribute("backgroundColor")) Then backgroundColor = _ colorSettings.Attribute("backgroundColor").Value End If Dim bannerCol As SolidColorBrush = _ New SolidColorBrush(makeColor(bannerColor))
Dim backgroundCol As SolidColorBrush = _ New SolidColorBrush(makeColor(backgroundColor)) App.NavigationGrid.Background = bannerCol App.LayoutRoot.Background = backgroundCol End Sub
[Visual C#] void setColors() { XDocument xDoc = XDocument.Parse(settings); XElement root = xDoc.Root; string bannerColor = string.Empty; string backgroundColor = string.Empty; XElement playerSettings = root.Element("PLAYERSETTINGS"); XElement colorSettings = playerSettings.Element("COLORSETTINGS"); if (colorSettings.Attribute("bannerColor") != null) { bannerColor = colorSettings.Attribute("bannerColor").Value; } if (colorSettings.Attribute("backgroundColor") != null) { backgroundColor = colorSettings.Attribute("backgroundColor").Value; } SolidColorBrush bannerCol = new SolidColorBrush(makeColor(bannerColor)); SolidColorBrush backgroundCol = new SolidColorBrush(makeColor(backgroundColor)); App.NavigationGrid.Background = bannerCol; App.LayoutRoot.Background = backgroundCol;
2.
Review the function named makeColor that already exists in the class in which you have been working. Your code calls this function to convert the hexadecimal string data into a .NET color. Silverlight does not have methods for converting hexadecimal string data into a .NET color, so this function has been provided for you as part of the starter solution.
3. 4.
Save the changes. Run and test the application. Press Ctrl+F5.
The section at the top of the Silverlight application is now orange and the background of the main section is grey. 5. Close Internet Explorer and switch back to Visual Studio 2010.
[Visual Basic] Dim webClient As WebClient = New WebClient() AddHandler webClient.DownloadStringCompleted, _ AddressOf webClient_DownloadStringCompleted Dim xmlUri As Uri = New Uri(categorySettingsUri, UriKind.Absolute) webClient.DownloadStringAsync(xmlUri)
[Visual C#] WebClient webClient = new WebClient(); webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted); Uri xmlUri = new Uri(categorySettingsUri, UriKind.Absolute); webClient.DownloadStringAsync(xmlUri);
2.
[Visual C#] void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show("There was an error retrieving xml"); } return;
XDocument xDoc = XDocument.Parse(e.Result); XElement root = xDoc.Root; string appTitle = string.Empty; if (root.Attribute("title") != null) { appTitle = root.Attribute("title").Value; App.ApplicationNameTextBlock.Text = appTitle; }
2. 3.
Save the changes. Run and test the application. Press Ctrl+F5.
The application displays the text Greeting Card! This text was retrieved from the GreetingCardConfig.xml file. You will work with the rest of the XML data that was retrieved in subsequent labs. 4. Close Internet Explorer and switch back to Visual Studio 2010.
10
In the XAML pane, locate the StackPanel element. Add the following markup between the opening and closing tags of the StackPanel element.
<TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}" Text="Instant Communications"/> <TextBox x:Name="imContent" Width="400" Height="200" HorizontalAlignment="Left"></TextBox> <TextBlock x:Name="imReceived" Width="400" Height="200" HorizontalAlignment="Left"></TextBlock>
4.
Create an event handler for the KeyUp event of the imContent TextBox control. a. b. Place the cursor in the markup for the imContent TextBox element. In the Properties window, click Events, and then double-click KeyUp.
Visual Studio creates an event handler stub for the KeyUp event. 5. 6. Leave the current instance of Visual Studio running. Open a second instance of Visual Studio. On the Start menu, click All Programs, click Microsoft Visual Studio 2010, and then click Microsoft Visual Studio 2010.
A second instance of Visual Studio appears. 7. Open the GreetingCardManagement solution from the D:\Labfiles\Mod11\VB\Starter or D:\Labfiles\Mod11\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod11\VB\Starter\ GreetingCardManagement.sln or D:\Labfiles\Mod11\CS\Starter\ GreetingCardManagement.sln, and then click Open.
8.
In the GreetingCardManagement project, in the Views folder, open About.xaml. In the GreetingCardManagement project, expand Views, and then double-click About.xaml.
9.
10. Add the following markup between the opening and closing tags of the StackPanel element, replacing the existing content.
<TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}" Text="Instant Communications"/> <TextBox x:Name="imContent" Width="400" Height="200" HorizontalAlignment="Left" KeyUp="imContent_KeyUp"></TextBox> <TextBlock x:Name="imReceived" Width="400" Height="200" HorizontalAlignment="Left"></TextBlock>
11
Task 2: Add code for sending and receiving messages to the GreetingCardConsumer application.
1. 2. Switch to the instance of Visual Studio that has the GreetingCardConsumer solution open. Ensure that you are viewing the code file for About.xaml. Bring the System.Windows.Messaging namespace into scope. Add the following code at the top of the About.xaml.vb or About.xaml.cs file.
3.
Above the imContent_KeyUp event handler, declare the following member variables and instantiate them by passing the constructor values indicated. Type LocalMessageSender LocalMessageReceiver Constructor Value GreetingCardConsumer GreetingCardManagement
Add the following code immediately above the imContent_KeyUp event handler.
[Visual Basic] Private msgSender As LocalMessageSender = New LocalMessageSender("GreetingCardConsumer") Private msgReceiver As LocalMessageReceiver = New LocalMessageReceiver("GreetingCardManagement")
4.
Create a new event handler named msgReceiver_MessageReceived, with the standard event handler signature, accepting an argument named sender of type Object/object, and an argument named e of type MessageReceivedEventArgs. Place the event handler immediately below the imContent_KeyUp event handler. Add the following code immediately below the imContent_KeyUp event handler.
[Visual Basic] Sub msgReceiver_MessageReceived(sender As Object, e As MessageReceivedEventArgs) imReceived.Text = e.Message End Sub
12
{ }
imReceived.Text = e.Message;
5.
Add code to the imContent_KeyUp event handler to call the SendAsync method of the msgSender object, passing the value of the imContent.Text property. Add the following code to the imContent_KeyUp event handler.
6.
Add code to the OnNavigatedTo event handler to map an event handler named msgReceiver_MessageReceived to the MessageReceived method of the msgReceiver, and then call the Listen method of the msgReceiver object. Add the following code to the OnNavigatedTo event handler.
7.
Task 3: Add code for sending and receiving messages to the GreetingCardManagement application.
1. 2. Switch to the instance of Visual Studio that has the GreetingCardManagement solution open. Ensure that you are viewing the code file for About.xaml. Above the imContent_KeyUp event handler, declare the following member variables and instantiate them by passing the constructor values indicated. Type LocalMessageSender LocalMessageReceiver Constructor Value GreetingCardManagement GreetingCardConsumer
Add the following code immediately above the imContent_KeyUp event handler.
[Visual Basic] Private msgSender As LocalMessageSender = New LocalMessageSender("GreetingCardManagement") Private msgReceiver As LocalMessageReceiver = New LocalMessageReceiver("GreetingCardConsumer")
13
Note that these names are the opposite of the messages you defined in Task 2. This enables the two applications to communicate with each other over two well-defined connections. 3. Build the solution. On the Build menu, click Build Solution.
Internet Explorer starts and displays the application. 3. 4. 5. 6. Click About. Leave Internet Explorer running. Switch to the instance of Visual Studio that has the GreetingCardConsumer solution open. Run the application. Press Ctrl+F5.
Internet Explorer starts and displays the application. 7. 8. 9. Click About. Arrange the two Internet Explorer windows so that they are side-by-side. In the instance of Internet Explorer that has the Greeting Card Consumer application running, in the text box, type Help! Note that the text Help! is displayed in the GreetingCard Management application. 10. In the GreetingCard Management application, in the text box, type OK! Note that the text OK! is displayed in the GreetingCard Consumer application. 11. Close all applications. You have now completed this lab.
Module 12
Lab Answer Key: Deploying Silverlight Applications
Contents:
Exercise 1: Adding Application Features Exercise 2: Loading Resources Dynamically 2 10
Open the GreetingCardConsumer solution from the D:\Labfiles\Mod12\VB\Starter or D:\Labfiles\Mod12\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod12\VB\Starter\ GreetingCardConsumer.sln or D:\Labfiles\Mod12\CS\Starter\ GreetingCardConsumer.sln, and then click Open.
3.
Open the Project Designer for the GreetingCardConsumer project. In the Solution Explorer window, right-click GreetingCardConsumer, and then click Properties.
4.
Enable the application to run outside the browser. In the Project Designer, on the Silverlight page, ensure that the Enable running application out of the browser check box is selected.
To the MainPage class, add a private shared/static event handler named OnNetworkChange, with the standard .NET Framework event handler signature. To the MainPage class, add the following code.
[Visual Basic] Private Shared Sub OnNetworkChange(ByVal sender As Object, ByVal e As EventArgs) End Sub
3.
Bring the System.Net.NetworkInformation namespace into scope. To the MainPage.xaml.vb or MainPage.xaml.cs file, add the following code at the top of the file, below other Imports or using statements.
[Visual Basic] Imports System.Net.NetworkInformation
4.
To the MainPage class constructor, append code to map the event handler method named OnNetworkChange to the NetworkChange.NetworkAddressChanged event. To the MainPage class constructor, append the following code.
[Visual Basic] AddHandler NetworkChange.NetworkAddressChanged, AddressOf OnNetworkChange
2.
Add code to the MainPage class constructor in an If...Then/if construct to check if the application is currently out-of-browser. If the application is currently running out-of-browser, add a check to see if the notificationWnd object is null, and if it is, instantiate it as a NotificationWindow object. Add the code before the event handler is added. To the MainPage class constructor, add the following code, before adding the event handler.
[Visual Basic] ' Are we running outside browser? If App.Current.IsRunningOutOfBrowser Then If notificationWnd Is Nothing Then notificationWnd = New NotificationWindow End If End If
[Visual C#] // Are we running outside browser? if (App.Current.IsRunningOutOfBrowser) { if (notificationWnd == null) notificationWnd = new NotificationWindow(); }
3.
To the OnNetworkChange method in an If...Then/if construct, add a local variable named message, of type String/string, and add a check to see if the application is currently running out-of-browser. To the OnNetworkChange method, add the following code.
[Visual Basic] Dim message As String ' Are we running outside browser? If App.Current.IsRunningOutOfBrowser Then End If
[Visual C#] string message = string.Empty; // Are we running outside browser? if (App.Current.IsRunningOutOfBrowser) { }
4.
To the OnNetworkChange method in the If...Then/if construct, add code to check if the notificationWnd object is currently visible by comparing the Visibility property with the System.Windows.Visibility.Visible enum member value. If it is visible, close the notificationWnd object to prevent an exception being thrown. To the OnNetworkChange method in the If...Then/if construct, add the following code.
[Visual Basic] ' Is the notification window visible? If notificationWnd.Visibility = Visibility.Visible Then ' Close notification window notificationWnd.Close() End If
[Visual C#] // Is the notification window visible? if (notificationWnd.Visibility == Visibility.Visible) { // Close notification window notificationWnd.Close(); }
5.
To the OnNetworkChange method in an If...Then...Else/if...else construct, add code to check if a network is available. Append the code to the existing If...Then/if construct that checks if the application is running out-of-browser. Set the value of the message variable to indicate if a network is available. To the OnNetworkChange method, append the following code to the existing If...Then/if construct that checks if the application is running out-of-browser.
[Visual Basic] If NetworkInterface.GetIsNetworkAvailable Then ' Set text to display message = "Network is available." Else ' Set text to display message = "Network is unavailable." End If
[Visual C#] if (NetworkInterface.GetIsNetworkAvailable()) { // Set text to display message = "Network is available."; } else { // Set text to display message = "Network is unavailable."; }
6.
In the NetworkChangeNotification.xaml window, set the Width property of the Grid control to 200, and the Height property to 50. To the opening Grid tag, add the following markup, to ensure that the size of the notification window is within the allowed maximum size.
Width="200" Height="50"
3.
In the NetworkChangeNotification.xaml window, to the Grid control, add a self-closing TextBlock element, which is centered both horizontally and vertically. Bind the Text property, without specifying the exact binding. In the Grid element, add the following markup.
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding}" />
4. 5. 6.
Save the changes. Switch to MainPage.xaml in Code view. [Visual C# only] Bring the GreetingCardConsumer.Views namespace into scope. Add the following code at the beginning of the namespace declaration.
[Visual C#] using GreetingCardConsumer.Views;
7.
8.
To the OnNetworkChange method, append code to the If...Then/if construct that checks if the application is running out-of-browser to instantiate a new object of type NetworkChangeNotification, named nwWindow. Set the Content property of the notificationWnd object to the nwWindow object. To the OnNetworkChange method, append the following code to the existing If...Then/if construct that checks if the application is running out-of-browser.
[Visual Basic] ' Create and instantiate new window Dim nwWindow As New NetworkChangeNotification() notificationWnd.Content = nwWindow
[Visual C#] // Create and instantiate new window NetworkChangeNotification nwWindow = new NetworkChangeNotification(); notificationWnd.Content = nwWindow;
9.
To the OnNetworkChange method, append code to the If...Then/if construct that checks if the application is running out-of-browser to set the Content.DataContext property of the notificationWnd object to the message variable. This way, you can programmatically set the correct message to appear, without using different windows/user controls. In addition, set the Height and Width properties to the corresponding values of the LayoutRoot object of the nwWindow object. This will ensure that your notification window has the same size as the UserControl you just created. Note that you need to use the Height and Width of the LayoutRoot, which in this case is the Grid control in the UserControl, because the Height and Width of the UserControl is not accessible at this stage because the UserControl is not displayed.
To the OnNetworkChange method, append the following code to the existing If...Then/if construct that checks if the application is running out-of-browser.
[Visual Basic] ' Set notification message notificationWnd.Content.DataContext = message ' Set notification window size notificationWnd.Height = nwWindow.LayoutRoot.Height notificationWnd.Width = nwWindow.LayoutRoot.Width
[Visual C#] // Set notification message notificationWnd.Content.DataContext = message; // Set notification window size notificationWnd.Height = nwWindow.LayoutRoot.Height; notificationWnd.Width = nwWindow.LayoutRoot.Width;
10. To the OnNetworkChange method, append code to the If...Then/if construct that checks if the application is running out-of-browser to show the notificationWnd object for 5 seconds, and then activate the MainWindow of the current application. To the OnNetworkChange method, append the following code to the existing If...Then/if construct that checks if the application is running out-of-browser.
[Visual Basic] ' Show for 5 seconds notificationWnd.Show(5000) ' Keep focus to the application window App.Current.MainWindow.Activate()
[Visual C#] // Show for 5 seconds notificationWnd.Show(5000); // Keep focus to the application window App.Current.MainWindow.Activate();
Install the application with default settings. a. b. In Internet Explorer, right-click the application background, and then click Install GreetingCardConsumer Application onto this computer. In the Security Warning dialog box, click Install.
3. 4.
Close Internet Explorer. Open the Settings window for the Hyper-V guest. In the 10554A-SEA-DEV on localhost Virtual Machine Connection window, on the File menu, click Settings.
5.
Arrange the Hyper-V guest and the Settings windows so that you can see the system tray of Hyper-V guest in the right lower-most corner of your screen, and the Settings window in the left upper-most corner. If the windows overlap, arrange the Settings window on top of the Hyper-V guest window. In the Settings window, change the network adapter from Private Network to Not connected. a. b. In the Settings for 10554A-SEA-DEV window, in the Hardware list, click Network Adapter. In the Network Adapter area, in the Network list, click Not connected, and then click Apply.
6.
7. 8.
Notice the toast notification being displayed for 5 seconds as the network connection is now unavailable. In the Settings window, change the network adapter from Not connected to Private Network. a. b. In the Settings for 10554A-SEA-DEV window, in the Hardware list, click Network Adapter. In the Network Adapter area, in the Network list, click Private Network, and then click Apply.
9.
Notice the toast notification being displayed for 5 seconds as the network connection is available again.
10. Close the Settings window. In the Settings for 10554A-SEA-DEV window, click OK.
11. Uninstall the application. a. b. In the GreetingCardConsumer Application window, right-click the application background, and then click Remove this application. In the Remove this application dialog box, click Yes.
10
Disable the application to run outside the browser. In the Project Designer, on the Silverlight page, clear the Enable running application out of the browser check box.
3.
Enable assembly caching. In the Project Designer, on the Silverlight page, select the Reduce XAP size by using application library caching check box.
4. 5.
Delete the default code file Class1. a. b. In the Solution Explorer window, under FabrikamLibrary, right-click Class1, and then click Delete. In the Microsoft Visual Studio message box, click OK.
3.
Add the existing DynamicPage.xaml file to the FabrikamLibrary project. The file is located in the Starter folder. a. b. In the Solution Explorer window, right-click FabrikamLibrary, point to Add, and then click Existing Item. In the Add Existing Item - FabrikamLibrary dialog box, in the File name box type D:\Labfiles\Mod12\VB\Starter\DynamicPage.xaml or D:\Labfiles\Mod12\CS\Starter\DynamicPage.xaml, and then click Add.
4.
In the FabrikamLibrary project, add a reference to the System.Windows.Controls.Navigation assembly. a. In the Solution Explorer window, right-click FabrikamLibrary, and then click Add Reference.
11
b. 5.
In the Add Reference dialog box, in the list, select System.Windows.Controls.Navigation, and then click OK.
Open the DynamicPage.xaml page. In the Solution Explorer window, under FabrikamLibrary, double-click DynamicPage.xaml.
6.
Examine the DynamicPage.xaml page content. Notice the value of the Text property of the TextBlock element named ContentText. Notice the use of various styles that do not exist in the current project. Build the FabrikamLibrary project. In the Solution Explorer window, right-click FabrikamLibrary, and then click Build.
7.
8.
Copy the output assembly, FabrikamLibrary.dll, from the D:\Labfiles\Mod12\VB\Starter\FabrikamLibrary\Bin\Debug or D:\Labfiles\Mod12\CS\Starter\FabrikamLibrary\Bin\Debug folder to the ClientBin folder in the root of the GreetingCardConsumer.Web project by using Windows Explorer. a. b. Open Windows Explorer. Navigate to the D:\Labfiles\Mod12\VB\Starter\FabrikamLibrary\Bin\Debug or D:\Labfiles\Mod12\CS\Starter\FabrikamLibrary\Bin\Debug folder, select FabrikamLibrary.dll, and then press Ctrl+C. Navigate to the D:\Labfiles\Mod12\VB\Starter\GreetingCardConsumer.Web\ClientBin or D:\Labfiles\Mod12\CS\Starter\GreetingCardConsumer.Web\ClientBin folder, and then press Ctrl+V.
c.
3.
To the Home class, append a private event handler named wClient_OpenReadCompleted, with the standard .NET Framework event handler signature; an argument named sender, of type Object/object, and an argument named e, of type OpenReadCompletedEventArgs. The event handler will handle the download of the assembly from the hosting web application. To the Home class, append the following code.
[Visual Basic] Private Sub wClient_OpenReadCompleted(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs) End Sub
12
4.
To the wClient_OpenReadCompleted method in an If...Then/if construct, check if the user did not cancel the download by examining the e.Cancelled property. To the wClient_OpenReadCompleted method, add the following code.
[Visual Basic] If Not e.Cancelled Then End If
5.
To the wClient_OpenReadCompleted method in the If...Then/if construct, add code to create and instantiate a variable named assmPart, of type AssemblyPart, and create a variable named assmbly, of type Assembly. Use the Load method of the assmPart object to assign the downloaded assembly to assmbly. Pass e.Result to the Load method. To the wClient_OpenReadCompleted method in the If...Then/if construct, add the following code.
[Visual Basic] ' Load assembly Dim assmPart As New AssemblyPart() Dim assmbly As Assembly = assmPart.Load(e.Result)
[Visual C#] // Load assembly AssemblyPart assmPart = new AssemblyPart(); Assembly assmbly = assmPart.Load(e.Result);
6.
To the wClient_OpenReadCompleted method in the If...Then/if construct, append code to load the DynamicPage.xaml file from the loaded assembly. Use the Application.LoadComponent method and pass the current object and a new System.Uri object with the following relative URI: /FabrikamLibrary;component/DynamicPage.xaml To the wClient_OpenReadCompleted method in the If...Then/if construct, append the following code.
[Visual Basic] ' Load component from assembly in current app domain Application.LoadComponent(Me, New System.Uri( "/FabrikamLibrary;component/DynamicPage.xaml", System.UriKind.Relative)) Me.LayoutRoot = CType(Me.FindName("LayoutRoot"), System.Windows.Controls.Grid)
13
[Visual C#] // Load component from assembly in current app domain Application.LoadComponent(this, new System.Uri( "/FabrikamLibrary;component/DynamicPage.xaml", System.UriKind.Relative)); this.LayoutRoot = ((System.Windows.Controls.Grid) (this.FindName("LayoutRoot")));
7. 8.
Locate the Home_Loaded or Page_Loaded event handler method. Append code to the Home_Loaded or Page_Loaded method, to create and instantiate a variable named wClient, of type WebClient. Append the code to the existing If...Then/if construct. To the Home_Loaded or Page_Laded method, append the following code. Append the code to the existing If...Then/if construct.
[Visual
Basic] ' Create and instantiate WebClient object Dim wClient As New WebClient()
[Visual C#] // Create and instantiate WebClient object WebClient wClient = new WebClient();
9.
Append code to the Home_Loaded or Page_Loaded method to map the OpenReadCompleted event of the existing wClient object to the wClient_OpenReadCompleted method. Append the code to the existing If...Then/if construct. To the Home_Loaded or Page_Loaded method, append the following code. Append the code to the existing If...Then/if construct.
[Visual Basic] ' Add event handler for download complete AddHandler wClient.OpenReadCompleted, AddressOf wClient_OpenReadCompleted
[Visual C#] // Add event handler for download complete wClient.OpenReadCompleted += wClient_OpenReadCompleted;
10. Append code to the Home_Loaded or Page_Loaded method to start the asynchronous download of the FabrikamLibrary.dll assembly by calling the OpenReadAsync method of the wClient object, passing a relative uri for the FabrikamLibrary.dll assembly. To the Home_Loaded or Page_Loaded method, append the following code. Append the code to the existing If...Then/if construct.
[Visual Basic] ' Start asynchronous download wClient.OpenReadAsync(New Uri("FabrikamLibrary.dll", UriKind.Relative))
14
Notice how the content of the Home page is replaced with the content loaded from the DynamicPage.xaml file, and how the styles are merged with the current project. 12. Close all applications. You have now completed this lab.
Module 13
Lab Answer Key: Application Guidance
Contents:
Exercise 1: Installing the Simple MVVM Toolkit Exercise 2: Examining a Silverlight MVVM project Exercise 3: Implementing MVVM Items 2 4 9
Get the Simple MVVM Toolkit installer from your instructor, located in an ISO file, and attach it to the virtual machine. 1. 2. 3. Ask your instructor for the installer, located in an ISO file. The instructor may have a USB stick, a CD/DVD, or on a network share. After you have access to the ISO file, in the 10554A-SEA-DEV on localhost Virtual Machine Connection window, on the Media menu, point to DVD Drive, and then click Insert Disk. In the Open dialog box, browse to the location of the ISO file, select it, and then click Open.
6.
On the InstallShield Wizard Completed page, clear the Show the readme file check box, and then click Finish.
Task 3: Register the Simple MVVM Toolkit project and item templates.
1. 2. 3. 4. 5. 6. 7. On the Start menu, click All Programs, expand Accessories, right-click Command Prompt, and then click Run as administrator. In the User Account Control dialog box, in the Password box, type Pa$$w0rd, and then click Yes. In the Administrator: Command Prompt box, at the prompt, type cd C:\Program Files\SimpleMvvmToolkit, and then press Enter. At the prompt, type SimpleMvvm-32wp.reg, and then press Enter. In the Registry Editor dialog box, click Yes. In the Registry Editor dialog box, click OK. Close the Administrator: Command Prompt box.
Open the MVVMGreetingCardManagement solution from the D:\Labfiles\Mod13\VB\Starter or D:\Labfiles\Mod13\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod13\VB\Starter\ MVVMGreetingCardManagement.sln or D:\Labfiles\Mod13\CS\Starter\ MVVMGreetingCardManagement.sln, and then click Open.
The Visual C# solution was created by using the SimpleMvvmRiaServices project template, and then customizing it slightly. The Visual Basic solution was created by using the Silverlight Business Application project template, and then adding the missing pieces, to make the two solutions as identical as possible. The two project templates are quite similar, with the SimpleMvvmRiaServices project template being a customization of the Silverlight Business Application project template. 3. Notice the hosting ASP.NET Web Application project named MVVMGreetingCardManagement.Web, which is the same type of hosting application you have been using throughout this course.
Notice how the Models folder only contains the standard client model files created by the Silverlight Business Application project template. Currently, no application specific data models have been created. In the MVVMGreetingCardManagement.Web project, expand the Models folder. In Solution Explorer, under MVVMGreetingCardManagement.Web, expand Models.
3.
4.
Notice how the Models folder contains the GreetingCardModel.edmx file, with which you have worked previously. The server data model has been created for you.
Notice how the HomeViewModel inherits from the ViewModelBase class, which is one of two view-model base classes used with the Simple MVVM Toolkit. This one can be used for displaying a collection of objects, or used as a default view-model, if you have not planned which data will be displayed yet.
3.
Notice how the non-default constructor accepts an argument of type, IHomeServiceAgent, which is saved to the private serviceAgent member variable. The service agent is responsible for retrieving the data for the view-model to expose. Expand the collapsed regions. Press Ctrl+M, Ctrl+L.
4.
5. 6.
There is currently no code in the Properties, Methods, and Commands regions, because it is not needed for this view-model. Notice the ErrorNotice event declaration, located in the Notifications regions, which is used by the NotifyError method, located in the Helpers region to inform the view of an error. The NotifyError method calls the Notify method of the base class, ViewModelBase. Close the HomeViewModel view-model. Open and examine the MainPageViewModel view-model, located in the ViewModels folder. In Solution Explorer, under ViewModels, double-click MainPageViewModel.vb or MainPageViewModel.cs.
7. 8.
9.
Notice how the default constructor calls the RegisterToReceiveMessages method, inherited from the base class, to register or subscribe to messages that arrive on the MessageBus implementation. Only navigation token messages are subscribed to, by specifying the MessageTokens.Navigation constant value. This value is defined in the MessageTokens class, located in the Others folder. The Navigation value is currently the only value defined, but to extend the messaging system, you can add new constant values to this class.
11. Notice the SelectedPage property declaration, located in the Properties region. This property and the corresponding backing member field are used to keep track of the current page in the navigation system implemented, and the view can bind to this property. You will see more of how navigation is implemented by using an event handler, methods, and the MainPage view later. 12. Notice the Navigate and OnNavigationRequested methods, both located in the Methods region. The latter should be familiar because it is the same event handler method you have seen throughout the implementations of the Silverlight Business Application and Silverlight Navigation Application. However, unlike the previous implementations, the OnNavigationRequested method calls the Navigate method, passing the page name, stored in the Message property of the NotificationEventArgs argument. This class is custom to the Simple MVVM Toolkit, and part of the messaging implementation. 13. Notice the NavigateCommand property, located in the Commands region. This property is used by the HyperlinkButton controls, used as part of the menu and user exposed navigation implementation, by way of binding. The DelegateCommand is custom to the Simple MVVM Toolkit, and used as the type for the NavigateCommand property. 14. Close the MainPageViewModel view-model.
Notice how the designer and the XAML view look more or less the same as the Home.xaml page used in the previous implementations of the Silverlight Business Application project template. One
exception is how the DataContext property of the Page control is bound to the HomeViewModel by using a static resource that locates the view-model. This is bound the same way as you saw during the demonstrations. 3. 4. Close the HomeView view. Open and examine the MainPage view, located in the root folder. 5. In Solution Explorer, double-click MainPage.xaml.
Notice how the designer and the XAML view look similar to the MainPage.xaml UserControl used in the previous implementations of the Silverlight Business Application project template. There are a few differences though, as detailed in the next few steps. The DataContext property of the UserControl control is bound to the MainPageViewModel just like the HomeView view. The Source property of the Frame control used by the Silverlight navigation system is bound to the SelectedPage property of the MainPageViewModel. The HyperlinkButton control named, Link1, uses the Command and CommandParameter properties for navigation, instead of the NavigateUri property used by previous implementations of the MainPage.xaml view. The Command property binds to the NavigateCommand property of the MainPageViewModel property, passing the string Home, in the CommandParameter property. Close the MainPage view.
6. 7. 8.
9.
Notice how the interface is empty, and unlike the implementation you saw in the demonstration, does not define any methods. Close the IHomeServiceAgent code file. Examine the HomeServiceAgent class. In Solution Explorer, under Services, double-click HomeServiceAgent.vb or HomeServiceAgent.cs.
5.
Notice how the class is empty. The interface and the class are currently empty, awaiting any data methods to be added, if the view-model needs to expose properties and methods for the view to consume. Notice how there is no service agent for the MainPageViewModel in the Services folder; the MainPageViewModel does not need any because it really only serves the purpose of a master page or main page, enabling and facilitating navigation, and exposing the frame, in which Page controls can be shown. The HomeView currently does not need a service agent either, but it has been implemented with a service agent, to ensure it is easy to add at a later date. Close the HomeServiceAgent code file. In the MVVMGreetingCardManagement.Web project, expand the Services folder. In Solution Explorer, under MVVMGreetingCardManagement.Web, expand Services.
6.
7. 8.
9.
Notice how the Services folder contains the GreetingCardDomainService.vb or GreetingCardDomainService.cs and the GreetingCardDomainService.metadata.vb or
GreetingCardDomainService.metadata.cs files, with which you have worked previously. The server service layer has been created for you.
In the XAML view, notice how an instance of the InjectedViewModelLocator class is added to the Application.Resources resource dictionary, ensuring its availability throughout the lifetime of the application. Close the App.xaml file. Examine the InjectedViewModelLocator class, located in the Locators folder. In Solution Explorer, expand Locators, and then double-click InjectedViewModelLocator.vb or InjectedViewModelLocator.cs.
3. 4.
5.
In the InjectedViewModelLocator class, notice the shared/static constructor that checks if the element or controls is currently running in the context of a designer, such as Visual Studio or Expression Blend. If not, then a new DeploymentCatalog object is created and passed to the CompositionHost.Initialize method. This is used with the Microsoft Extensibility Framework (MEF) for supporting dependency injection of the service agents. Without going into details beyond the scope of this course, it basically means that you can "inject" any type of service agent. Notice how a public property named, AgentType, and the corresponding backing member variable, get or set the type of service agents used. The property returns a member of the AgentType enumeration, which contains three members: Mock, Real, and Unspecified. You use the values to specify whether you want to use the Mock or the Real service agents, allowing you to dynamically select if mock data or real data should be used. In the InjectedViewModelLocator class, notice the default constructor that also checks if the element or controls is currently running in the context of a designer. If not, the CompositionInitializer.SatisfyImports MEF method is used inject the current service agent object. In addition, the Debug.Assert method is used to throw an exception, if the HomeViewModel is not created correctly, and thus, the corresponding service agent and the correct agent type. The actual instantiation of the both of these objects happens when the Debug.Assert method is called, or when the HomeViewModel property, covered later, is bound to from the view. Notice the public and read-only property named, HomeViewModel, which creates and returns an instance of the HomeViewModel. If a corresponding service agent is found, it is created, and passed to the HomeViewModel constructor. Notice the public and read-only property named, MainPageViewModel, which returns an instance of the MainPageViewModel. The MainPageViewModel does not need a service agent, so the property code is straight-forward.
6.
7.
8.
9.
Notice how the application looks like the GreetingCard applications you have created previously in this course. The logon functionality also works, using the Student user credentials. Close Internet Explorer.
Change the added project item from a class to an interface by modifying the generated code to resemble to following code.
[Visual Basic] Public Interface ISendersServiceAgent End Interface
3.
[Visual C# only] Bring the System.Collections.Generic and MVVMGreetingCardManagement.Web.Models namespaces into scope in the ISendersServiceAgent.cs code file. These namespaces contain the argument types used in the method added next. Add a method named, GetSenders, to the interface. The method should not return a value, but accept one argument named, completed. The argument must be of delegate type, Action, which accepts a generic List of type, Sender, and an Exception. To the ISendersServiceAgent interface, add the following method.
[Visual Basic] Sub GetSenders(ByVal completed As Action(Of List(Of Sender), Exception))
4.
5. 6.
Save and close the ISendersServiceAgent code file. Implement the ISendersServiceAgent service agent interface in a new class named, SendersServiceAgent. a. b. c. In Solution Explorer, under MVVMGreetingCardManagement, right-click Services, point to Add, and then click Class. In the Add New Item MVVMGreetingCardManagement dialog box, in the Name box, type SendersServiceAgent, and then click Add. To the class declaration, add the following code in bold.
10
d.
Autogenerate the interface members. In Visual Basic, place the cursor after ISendersServiceAgent, and then press Enter. In Visual C#, right-click ISendersServiceAgent, point to Implement Interface, and then click Implement Interface.
7.
[Visual C# only] Bring the MVVMGreetingCardManagement.Web.Services namespace into scope in the SendersServiceAgent.cs code file. This namespace contains the argument types used in the variable added next. Bring the SimpleMvvmToolkit namespace into scope in the SendersServiceAgent code file. This namespace contains the attribute type applied to the class next. To the SendersServiceAgent class, apply the ServiceAgentExport attribute. This attribute is used to specify which type of service agents are exported, and the type of agent. The attribute should resemble the following code.
[Visual Basic] <ServiceAgentExport(GetType(ISendersServiceAgent), AgentType:=AgentType.Real)>
8. 9.
10. In the SendersServiceAgent class, add and instantiate a new private member variable named, gcDomainContext, of type, GreetingCardDomainContext. This variable will be used for accessing the Entity Data Model on the hosting server, by using the generated RIA Service code, available on the client. To the SendersServiceAgent class, add the following code, after the class declaration.
[Visual Basic] Private gcDomainContext As New GreetingCardDomainContext
11
11. Bring the System.ServiceModel.DomainServices.Client namespace into scope in the SendersServiceAgent code file. This namespace contains the argument types used in the method implemented next. 12. [Visual C# only] Bring the MVVMGreetingCardManagement.Web.Models, System.Linq, and System.Collections.Generic namespaces into scope in the SendersServiceAgent.cs code file. These namespaces contain the argument types used in the method implemented next. 13. In the SendersServiceAgent class, to the GetSenders method, add the following code (overwriting any existing code). The code creates a new entity query that calls the GetSenders query on the server (named GetSendersQuery on the client), by using the private gcDomainContext variable. Subsequently, the load operation is initiated and executed. If everything goes well, the retrieved collection of Sender objects is assigned to the completed argument callback.
[Visual Basic] Dim query As EntityQuery(Of Sender) = gcDomainContext.GetSendersQuery() ' Load GetSendersQuery gcDomainContext.Load(query, Sub(load) ' Declare error and result Dim err As Exception = Nothing Dim senders As IEnumerable(Of Sender) = Nothing ' Set error or result If load.HasError Then err = load.Error Else senders = load.Entities End If ' Invoke completion callback completed(senders.ToList, err) End Sub, Nothing)
[Visual C#] EntityQuery<Sender> query = gcDomainContext.GetSendersQuery(); // Load GetSendersQuery gcDomainContext.Load(query, load => { // Declare error and result Exception err = null; IEnumerable<Sender> senders = null; // Set error or result if (load.HasError) { err = load.Error; } else { senders = load.Entities; } // Invoke completion callback
12
[Visual C# only] Change the namespace for the SendersViewModel class from MVVMGreetingCardManagement.ViewModels to MVVMGreetingCardManagement. This will ensure the namespace is the same as the existing view-model classes. Bring the SimpleMvvmToolkit and System.ServiceModel.DomainServices.Client namespaces into scope in the SendersViewModel code file. These namespaces contain the argument types used in the class modification next. Derive the SendersViewModel class from the generic ViewModelBase class, of SendersViewModel. To the class declaration, add the following code in bold.
[Visual Basic] Public Class SendersViewModel Inherits ViewModelBase(Of SendersViewModel)
3.
4.
5.
In the SendersViewModel class, declare a new private member variable named, serviceAgent, of type, ISendersServiceAgent. This variable will be used for storing a reference to the service agent used for retrieving data from the model To the SendersViewModel class, add the following code, after the class declaration.
[Visual Basic] Private serviceAgent As ISendersServiceAgent
6.
Bring the System.Collections.ObjectModel namespace into scope in the SendersViewModel code file. This namespace contain the argument types used by the private member variables declared next. [Visual C# only] Bring the MVVMGreetingCardManagement.Web.Models namespace into scope in the SendersViewModel.cs code file. This namespace contain the argument types used by the private member variables declared next.
7.
13
8.
Add the following private member variables. The sndrs variable is used for storing an ObservableCollection of the retrieved Sender objects from the model. The selSender variable is used to point to the currently selected Sender in the UI. The two Boolean member variables are used to set and check if the view-model is busy with an operation, or if the senders can be loaded.
[Visual Private Private Private Private Basic] sndrs As ObservableCollection(Of Sender) selSender As Sender busy As Boolean ableToLoad As Boolean = True
C#] ObservableCollection<Sender> sndrs; Sender selSender; bool busy; bool ableToLoad = true;
9.
Create an empty default constructor. To the SendersViewModel class, append the following code. It is used by the view-model locator.
[Visual Basic] Public Sub New() End Sub
10. [Visual C# only] Bring the MVVMGreetingCardManagement.Services namespace into scope in the SendersViewModel.cs code file. This namespace contains the argument types used in the nondefault constructor implemented next. 11. Create a non-default constructor that accepts a single argument of type, ISendersServiceAgent. The constructor must assign the passed service agent argument to the private member variable, serviceAgent, and call the LoadSenders method to be created later. The constructor is used by the view-model locator. To the SendersViewModel class, append the following code.
[Visual Basic] Public Sub New(ByVal serviceAgnt As ISendersServiceAgent) Me.serviceAgent = serviceAgnt LoadSenders() End Sub
14
12. Append the following code to the SendersViewModel class. The Notifications region contains two public events, used to make the view aware of an error, or when the sender objects have been loaded from the model.
[Visual Basic] #Region "Notifications" Public Event ErrorNotice As EventHandler(Of NotificationEventArgs(Of Exception)) Public Event SendersLoadedNotice As EventHandler(Of NotificationEventArgs) #End Region
[Visual C#] #region Notifications public event EventHandler<NotificationEventArgs<Exception>> ErrorNotice; public event EventHandler<NotificationEventArgs> SendersLoadedNotice; #endregion
13. Append the following code to the SendersViewModel class. The Properties region contains several public properties, all used by the view and the view-model for binding the two objects and managing when an operation can be performed. All properties ensure that any change to the property value is notified to those binding to the property, such as controls in the view.
[Visual Basic] #Region "Properties" Public Property Senders As ObservableCollection(Of Sender) Get Return Me.sndrs End Get Set(ByVal value As ObservableCollection(Of Sender)) Me.sndrs = value NotifyPropertyChanged(Function(m) m.Senders) End Set End Property Public Property SelectedSender As Sender Get Return Me.selSender End Get Set(ByVal value As Sender) Me.selSender = value Me.CanLoad = Not IsBusy NotifyPropertyChanged(Function(m) m.SelectedSender) End Set End Property Public Property IsBusy As Boolean Get Return Me.busy End Get Set(ByVal value As Boolean) Me.busy = value Me.CanLoad = Not IsBusy NotifyPropertyChanged(Function(m) m.IsBusy) End Set End Property
15
Public Property CanLoad As Boolean Get Return Me.ableToLoad End Get Set(ByVal value As Boolean) Me.ableToLoad = value NotifyPropertyChanged(Function(m) m.CanLoad) End Set End Property #End Region
[Visual C#] #region Notifications public event EventHandler<NotificationEventArgs<Exception>> ErrorNotice; public event EventHandler<NotificationEventArgs> SendersLoadedNotice; #endregion #region Properties public ObservableCollection<Sender> Senders { get { return this.sndrs; } set { this.sndrs = value; NotifyPropertyChanged(m => m.Senders); } } public Sender SelectedSender { get { return this.selSender; } set { this.selSender = value; this.CanLoad = !IsBusy; NotifyPropertyChanged(m => m.SelectedSender); } } public bool IsBusy { get { return this.busy; } set { this.busy = value; this.CanLoad = !IsBusy; NotifyPropertyChanged(m => m.IsBusy); } }
16
public bool CanLoad { get { return this.ableToLoad; } set { this.ableToLoad = value; NotifyPropertyChanged(m => m.CanLoad); } } #endregion
14. Append the following code to the SendersViewModel class. The Methods region contains a single method named, LoadSenders, which does as the name indicates; it uses the GetSenders method of the service agent to initiate and execute the loading of the senders from the model.
[Visual Basic] #Region "Methods" Public Sub LoadSenders() serviceAgent.GetSenders( _ Sub(entities, err) SendersLoaded(entities, err) End Sub ) ' Reset property Me.Senders = Nothing ' Flip busy flag Me.IsBusy = True End Sub #End Region
[Visual C#] #region Methods public void LoadSenders() { serviceAgent.GetSenders( (entities, err) => { SendersLoaded(entities, err); }); // Reset property this.Senders = null; // Flip busy flag this.IsBusy = true;
#endregion
17
15. [Visual C# only] Bring the System.Collections.Generic namespace into scope in the SendersViewModel.cs code file. This namespace contains the types used in the callback methods implemented next. 16. Append the following code to the SendersViewModel class. The Completion Callbacks region contains a single method named, SendersLoaded, which is asynchronously invoked from the LoadSenders method, described previously. The SendersLoaded method checks for errors, and notifies the view if an error occurred. If no error occurred, the Senders property is set, the SelectedSender is set to the first Sender object returned, and finally, the view is notified that the loading of the sender objects has completed.
[Visual Basic] #Region "Completion Callbacks" Private Sub SendersLoaded(ByVal entities As List(Of Sender), ByVal err As Exception) ' If no error is returned, set the model to entities If err Is Nothing Then Senders = New ObservableCollection(Of Sender)(entities) Else NotifyError("Unable to retrieve sender", err) End If ' Set SelectedSender to the first item If Senders.Count > 0 Then SelectedSender = Senders(0) End If ' Notify view Notify(SendersLoadedNoticeEvent, New NotificationEventArgs("Senders were successfully loaded")) ' Finished IsBusy = False End Sub #End Region
[Visual C#] #region Completion Callbacks private void SendersLoaded(List<Sender> entities, Exception err) { // If no error is returned, set the model to entities if (err == null) { Senders = new ObservableCollection<Sender>(entities); } else { NotifyError("Unable to retrieve sender", err); } // Set SelectedSender to the first item if (Senders.Count > 0) { SelectedSender = Senders[0]; } // Notify view
18
Notify(SendersLoadedNotice, new NotificationEventArgs("Senders were successfully loaded")); // Finished IsBusy = false; } #endregion
17. Append the following code to the SendersViewModel class. The Helpers region contains a single method, NotifyError, which was described previously in Task 3 of Exercise 2.
[Visual Basic] #Region "Helpers" ' Helper method to notify View of an error Private Sub NotifyError(ByVal message As String, ByVal err As Exception) ' Notify view of an error Notify(Of Exception)(ErrorNoticeEvent, New NotificationEventArgs(Of Exception)(message, err)) End Sub #End Region
[Visual C#] #region Helpers // Helper method to notify View of an error private void NotifyError(string message, Exception err) { // Notify view of an error Notify<Exception>(ErrorNotice, new NotificationEventArgs<Exception>(message, err)); } #endregion
2.
Open the SendersView view. In Solution Explorer, under MVVMGreetingCardManagement, under Views, double-click SendersView.xaml.
3.
Notice how the designer and the XAML view look more or less the same as the Home.xaml page used in the lab exercises for Module 2. In the designer, you can see the Label and TextBox controls for displaying the display name and email fields for a Sender entity. Then, there is a DataPager
19
control for moving back and forth between the Sender entities, and below that, a DataGrid control for displaying the Card entities for the selected Sender entity, in true master-detail style. 4. In the XAML view, you can see how the DataContext property of the Page control is bound to the SendersViewModel by using a static resource that locates the view-model. The two TextBox controls are bound to the SelectedSender.DisplayName and SelectedSender.Email properties of the SendersViewModel view-model. The DataGrid control is bound to the SelectedSender.Cards property, whereas the DataPager control is not bound declaratively. Close the SendersView view. Open the SendersView view, in Code view. In the SendersView class, in the default constructor, you can see how the private model variable is bound to the SendersViewModel through the DataContext property. The ErrorNotice and SendersLoadedNotice events of the SendersViewModel view-model are handled by the corresponding event handlers, OnErrorNotice and OnSendersLoadedNotice. This way, the viewmodel can communicate with the view. Notice the OnErrorNotice event handler method, which shows an error message and outputs trace information. Notice the OnSendersLoadedNotice event handler method, which sets the Source property of the DataPager control to a new PagedCollectionView collection, which wraps the Senders collection from the view-model. It is wrapped in a PagedCollectionView collection to ensure the DataPager control can properly enumerate the items.
5. 6. 7.
8. 9.
10. Notice the SendersDataPager_PageIndexChanged event handler method, which sets the SelectedSender property of the view-model to the currently selected page index. The DataPager page index is set to 1, which means this index matches the order of the Senders collection of the view-model. 11. Close the SendersView code file. 12. Open MainPage.xaml. 13. In MainPage.xaml, in the XAML view, locate the following markup.
<Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>
14. Immediately below the located markup, but above the closing StackPanel tag, add a new selfclosing HyperlinkButton control element named, SendersHyperlinkButton. The HyperlinkButton control is bound to the NavigateCommand property of the MainPageViewModel, which you examined in Exercise 2. The name of the view to which to navigate is passed to the NavigateCommand property by setting the CommandParameter property of the HyperlinkButton. Set the following properties.
Property Name Content Command CommandParameter Style TargetName Property Value {Binding Path=ApplicationStrings.SendersPageTitle, Source={StaticResource ResourceWrapper}} {Binding NavigateCommand} SendersView {StaticResource LinkStyle} ContentFrame
20
<HyperlinkButton x:Name="SendersHyperlinkButton" Style="{StaticResource LinkStyle}" Command="{Binding NavigateCommand}" CommandParameter="SendersView" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.SendersPageTitle, Source={StaticResource ResourceWrapper}}"/>
15. Save and close MainPage.xaml. 16. Build the solution. On the Build menu, click Build Solution.
The Resource Designer opens displaying a three-column grid containing the resource strings in the ApplicationStrings.resx resource file. 2. Add a new key named, SendersPageTitle, with a value, Senders. 3. 4. In the empty bottom row, in the Name column, type SendersPageTitle, in the Value column, type Senders, and then press Enter.
In the default non-shared/non-static constructor, append the following code to the If/if construct.
[Visual Basic] Debug.Assert(Not Me.SendersViewModel Is Nothing, String.Format(ApplicationStrings.ServiceAgentNotFound, "ISendersServiceAgent", AgentType))
3.
[Visual C# only] Bring the MVVMGreetingCardManagement.Services namespace into scope in the InjectedViewModelLocator.cs code file. The namespace contain the types used in the properties added next. To the InjectedViewModelLocator class, append the following code.
[Visual Basic] <ImportMany()>
4.
21
Public Property SendersServiceAgents As Lazy(Of ISendersServiceAgent, IServiceAgentMetadata)() Public ReadOnly Property SendersViewModel As SendersViewModel Get Dim serviceAgent = SendersServiceAgents _ .Where(Function(sa) sa.Metadata.AgentType = AgentType).FirstOrDefault() Dim viewModel As SendersViewModel = Nothing If Not serviceAgent Is Nothing Then viewModel = New SendersViewModel(serviceAgent.Value) ElseIf DesignerProperties.IsInDesignTool Then viewModel = New SendersViewModel() End If Return viewModel End Get End Property
[Visual C#] [ImportMany] public Lazy<ISendersServiceAgent, IServiceAgentMetadata>[] SendersServiceAgents { get; set; } public SendersViewModel SendersViewModel { get { var serviceAgent = SendersServiceAgents .Where(sa => sa.Metadata.AgentType == agentType).FirstOrDefault(); SendersViewModel viewModel = null; if (serviceAgent != null) viewModel = new SendersViewModel(serviceAgent.Value); else if (DesignerProperties.IsInDesignTool) viewModel = new SendersViewModel(); } return viewModel;
22
In Internet Explorer, click Senders. Notice how the Sender data is displayed. Navigate back and forth with the DataPager control to see all the Sender entities. Close all applications. You have now completed this lab.
Module 14
Lab Answer Key: Windows Phone DevelopmentFirst Look
Contents:
Exercise 1: Capturing User Input Exercise 2: Responding to Orientation Changes Exercise 3: Adding Page Navigation Exercise 4: Monitoring Game Status Exercise 5: Sharing Data Between Pages 2 6 9 10 13
Open the TicTacToe solution from the D:\Labfiles\Mod14\VB\Starter or D:\Labfiles\Mod14\CS\Starter folder. a. b. On the File menu, point to Open, and then click Project/Solution. In the Open Project dialog box, in the File name box, type D:\Labfiles\Mod14\VB\Starter\ TicTacToe.sln or D:\Labfiles\Mod14\CS\Starter\ TicTacToe.sln, and then click Open.
3.
Set the Windows Phone 7 emulator as the target for Silverlight for Windows Phone projects by using the list on the Standard toolbar. In the TicTacToe Microsoft Visual Studio window, on the Standard toolbar, in the Select target for Silverlight for Windows Phone projects list, click Windows Phone 7 Emulator.
4.
The Windows Phone emulator opens. It may take a while before your application is deployed, loaded, and displayed. 5. 6. 7. Click the Player 1 text box, and notice how the Software Input Panel (SIP) keyboard automatically slides open. In the Player 1 text box, type Student by using the SIP keyboard. In the Player 1 text box, click the text Student. Notice how the text is selected, and the copy icon is displayed above the selected text.
8.
Copy the text by using the copy icon. Notice how the paste icon is displayed just above the SIP keyboard.
9.
2. 3. 4.
Keep playing till you win, or till you have three X symbols on a single line. This can be horizontal, vertical, or diagonal. Notice the message box that pops up with the winning message. Close the message box. Notice the new message box, prompting you to play another game. Cancel a new game. In the message box, click cancel.
5.
Close the Windows Phone 7 emulator. In the Windows Phone 7 emulator toolbar, click the Close button.
In the MainPage.xaml window, locate the control named player1TextBox. Examine the player1TextBox control, which is a TextBox control.
[Visual Basic] <TextBox Height="73" HorizontalAlignment="Left" Margin="26,60,0,0" Name="player1TextBox" Text="enter name to begin" VerticalAlignment="Top" Width="272" Grid.Row="1" />
[Visual C#] <TextBox Height="73" HorizontalAlignment="Left" Margin="26,60,0,0" Name="player1TextBox" Text="enter name to begin" VerticalAlignment="Top" Width="272" GotFocus="player1TextBox_GotFocus" Grid.Row="1" />
4. 5.
In the MainPage.xaml window, locate the control named textBlock1. Examine the textBlock1 control, which is a TextBlock control. In addition, notice the player2TextBox and textBlock2 controls, which are used for player display and user input.
[Visual Basic] <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,24,0,0" Name="textBlock1" Text="Player 1" VerticalAlignment="Top" Grid.Row="1" /> <TextBox Height="73" HorizontalAlignment="Left" Margin="26,172,0,0" Name="player2TextBox" Text="Computer" VerticalAlignment="Top" Width="269" Grid.Row="1" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,136,0,0" Name="textBlock2" Text="Player 2" VerticalAlignment="Top" Grid.Row="1" />
[Visual C#] <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,24,0,0" Name="textBlock1" Text="Player 1" VerticalAlignment="Top" Grid.Row="1" /> <TextBox Height="73" HorizontalAlignment="Left" Margin="26,172,0,0" Name="player2TextBox" Text="Computer" VerticalAlignment="Top" Width="269" Grid.Row="1" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="26,136,0,0" Name="textBlock2" Text="Player 2" VerticalAlignment="Top" Grid.Row="1" />
6.
7.
In the Solution Explorer window, in the TicTacToe project, right-click MainPage.xaml, and then click View Code.
Examine the GotFocus event handler for the player1TextBox control. Notice that the current text in the TextBox control is selected, when the TextBox control receives focus by calling the SelectAll method of the TextBox class.
[Visual Basic] ' select the text name of player 1 when the TextBox gets focus - makes a name change easier Private Sub player1TextBox_GotFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles player1TextBox.GotFocus player1TextBox.SelectAll() End Sub
[Visual C#] // select the text name of player 1 when the TextBox gets focus - makes a name change easier private void player1TextBox_GotFocus(object sender, RoutedEventArgs e) { player1TextBox.SelectAll(); }
Locate the opening tag for the main PhoneApplicationPage control or element.
<phone:PhoneApplicationPage x:Class="TicTacToe.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True">
3.
SupportedOrientations="Portrait"
4.
Modify the value for the SupportedOrientations property, to support both portrait and landscape orientations. In the MainPage.xaml window, modify the value of the SupportedOrientations property, to resemble the following markup.
SupportedOrientations="PortraitOrLandscape"
5.
Task 2: Modify MainPage.xaml to handle the OrientationChanged event and support the Grid layout technique.
1. Switch to the MainPage.xaml code file. 2. In the TicTacToe Microsoft Visual Studio window, click MainPage.xaml.vb or MainPage.xaml.cs.
Locate the TODO comment, Call the PositionControls method, in the MainPage_OrientationChanged or OnOrientationChanged event handler.
[Visual Basic] ' this method responds to a phone orientation change Private Sub MainPage_OrientationChanged(ByVal sender As Object, ByVal e As Microsoft.Phone.Controls.OrientationChangedEventArgs) Handles MyBase.OrientationChanged MyBase.OnOrientationChanged(e) ' TODO: Call the PositionControls method End Sub
[Visual C#] // this method responds to a phone orientation change protected override void OnOrientationChanged(OrientationChangedEventArgs args) { base.OnOrientationChanged(args); // TODO: Call the PositionControls method }
3.
To the MainPage_OrientationChanged or OnOrientationChanged event handler, append code to call the PositionControls method, passing the event arguments received. In the MainPage class, in the MainPage_OrientationChanged or OnOrientationChanged event handler method, append the following code.
[Visual Basic] PositionControls(e)
4.
Locate the TODO comment, Change the display location of controls, in the PositionControls method.
[Visual Basic] ... ' If not in portrait, move buttonList content to visible row and column. Else ' TODO: Change the display location of controls End If
[Visual C#] ... // If not in portrait, move buttonList content to visible row and column. else { // TODO: Change the display location of controls }
5.
In the PositionControls method, append code to the Else/else part of the If...Then...Else/if...else structure, to place the textBlock1, player1TextBox, textBlock2, player2TextBox, and textBlock3 controls, in row 1, column 2, by using the grid layout technique, and the Grid.SetRow and Grid.SetColumn methods. Remember that the row and column numbers are zero based. In the PositionControls method, in the Else/else part of the If...Then...Else/if...else structure, append the following code.
[Visual Basic] Grid.SetRow(textBlock1, 0) Grid.SetColumn(textBlock1, 1) Grid.SetRow(player1TextBox, 0)
[Visual C#] Grid.SetRow(textBlock1, 0); Grid.SetColumn(textBlock1, 1); Grid.SetRow(player1TextBox, 0); Grid.SetColumn(player1TextBox, 1); Grid.SetRow(textBlock2, 0); Grid.SetColumn(textBlock2, 1); Grid.SetRow(player2TextBox, 0); Grid.SetColumn(player2TextBox, 1); Grid.SetRow(textBlock3, 0); Grid.SetColumn(textBlock3, 1);
6.
In the Windows Phone 7 emulator, change from portrait orientation to landscape left orientation, and notice the flow of the controls is exactly the same as in portrait orientation. In the Windows Phone 7 emulator toolbar, click the Rotate Right button.
3.
In the Windows Phone 7 emulator, change from landscape left orientation to portrait orientation, and notice the flow the controls is now exactly the same as when you started the application. In the Windows Phone 7 emulator toolbar, click the Rotate Left button.
4.
Close the Windows Phone 7 emulator. In the Windows Phone 7 emulator toolbar, click the Close button.
2.
3.
In the AdvancedGamePlay.xaml.vb or AdvancedGamePlay.xaml.cs file, locate the TODO comment, Construct the code to navigate back to the MainPage using the NavigationService, in the textBlock3_ManipulationStarted event handler. Use the Task List window to locate the comment. In the Task List window, double-click the row with the text, TODO: Construct the code to navigate back to the MainPage using the NavigationService.
Notice how the AdvancedGamePlay.xaml.vb or AdvancedGamePlay.xaml.cs file is opened, and the cursor placed at the TODO comment. 4. In the textBlock3_ManipulationStarted event handler method, append code to navigate back to the MainPage page. Use the shared/static NavigationService.Navigate, passing in a new relative URI. In the textBlock3_ManipulationStarted method, append the following code.
5.
10
In the AdvancedGamePlay.xaml window, locate the Grid control named buttonFrameGrid. Examine the buttonFrameGrid control, which in addition to the Button controls in the MainPage.xaml page for selecting a square, also holds a number of Polyline controls.
<Polyline Stroke="White" StrokeThickness="5" Points="10,10,290,290" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineDiagonalRightAndDown" Visibility="Collapsed" /> <Polyline Stroke="White" StrokeThickness="5" Points="10,290,290,10" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineDiagonalRightAndUp" Visibility="Collapsed" /> <Polyline Stroke="White" StrokeThickness="5" Points="50,10,50,290" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineVerticalLeftColumn" Visibility="Collapsed" /> <Polyline Stroke="White" StrokeThickness="5" Points="150,10,150,290" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineVerticalCenterColumn" Visibility="Collapsed" /> <Polyline Stroke="White" StrokeThickness="5" Points="250,10,250,290" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineVerticalRightColumn" Visibility="Collapsed" /> <Polyline Stroke="White" StrokeThickness="5" Points="10,50,290,50" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineHorizontalTopRow" Visibility="Collapsed" /> <Polyline Stroke="White" StrokeThickness="5" Points="10,150,290,150" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineHorizontalCenterRow" Visibility="Collapsed" /> <Polyline Stroke="White" StrokeThickness="5" Points="10,250,290,250" Grid.ColumnSpan="3" Grid.RowSpan="3" Name="lineHorizontalBottomRow" Visibility="Collapsed" />
The Polyline control is generally used for drawing a series of connected straight lines. In the context of the Tic Tac Toe game, it is used to draw a straight line covering the three winning squares, when a game is won. 4. Open the AdvancedGamePlay.xaml in Code view. 5. In the Solution Explorer window, in the TicTacToe project, right-click AdvancedGamePlay.xaml and then click View Code.
Locate and examine the PrepareForWinnerLineDraw method. myPolyline is declared as a member variable at the top of the class as a Polyline object, and firstPoint and secondPoint are declared as member variables at the top of the class as Point objects. This method is used to initialize myPolyline with the data from winnerLine, which is also declared as a member variable at the top of the class as a Polyline object.
[Visual Basic] ' This method is used to initialize myPolyline with data from the winnerLine Public Sub PrepareForWinnerLineDraw() myPolyline = winnerLine ' get the first and last point of the winning path (polyline) firstPoint = winnerLine.Points.First() lastPoint = winnerLine.Points.Last() myPolyline.Points.Clear() ' add the first point of the winning path to the new polyline myPolyline.Points.Add(firstPoint) myPolyline.Visibility = System.Windows.Visibility.Visible
11
' create a point that is the end of the line as it is constructed/drawn myEndPoint = myPolyline.Points.First() End Sub
[Visual C#] // This method is used to initialize myPolyline with data from the winnerLine public void PrepareForWinnerLineDraw() { myPolyline = winnerLine; // get the first and last point of the winning path (polyline) firstPoint = winnerLine.Points.First(); lastPoint = winnerLine.Points.Last(); myPolyline.Points.Clear(); // add the first point of the winning path to the new polyline myPolyline.Points.Add(firstPoint); myPolyline.Visibility = System.Windows.Visibility.Visible; // create a point that is the end of the line as it is constructed/drawn myEndPoint = myPolyline.Points.First();
6.
Locate and examine the DrawWinnerLine method, which is used to draw the actual winner line.
[Visual Basic] Public Sub DrawWinnerLine() ' construct the next point to add to the polyline Dim myNextPoint As New Point() myNextPoint.X = myEndPoint.X + ((lastPoint.X - firstPoint.X) / CType(numberOfLineSegments, Double)) myNextPoint.Y = myEndPoint.Y + ((lastPoint.Y - firstPoint.Y) / CType(numberOfLineSegments, Double)) ' add the point of the winning path to the new polyline myPolyline.Points.Add(myNextPoint) lineSegmentCounter += 1 If lineSegmentCounter>= numberOfLineSegments Then gameStatus = "game complete, announcing game result" Else myEndPoint = myNextPoint End If End Sub
[Visual C#] public void DrawWinnerLine() { // construct the next point to add to the polyline Point myNextPoint = new Point(); myNextPoint.X = myEndPoint.X + ((lastPoint.X - firstPoint.X) / (double) numberOfLineSegments); myNextPoint.Y = myEndPoint.Y + ((lastPoint.Y - firstPoint.Y) / (double) numberOfLineSegments); // add the point of the winning path to the new polyline
12
myPolyline.Points.Add(myNextPoint); lineSegmentCounter++; if (lineSegmentCounter>= numberOfLineSegments) { gameStatus = "game complete, announcing game result"; } else { myEndPoint = myNextPoint; }
13
In the App class, after the located comment, add code to create an auto-implemented property named, FirstPlayerTextString, of type, String/string. In the App class, after the located comment, add the following code.
[Visual Basic] Public Property FirstPlayerTextString As String
3.
In the AdvancedGamePlay.xaml.vb or AdvancedGamePlay.xaml.cs file, locate the TODO comment, Construct code to share data between pages, in the textBlock3_ManipulationStarted event handler. Use the Task List window to locate the comment. In the Task List window, double-click the row with the text, TODO: Construct code to share data between pages.
4.
In the textBlock3_ManipulationStarted event handler method, after the located comment, assign the value of the Text property of the player1TextBox to the FirstPlayerTextString property in the App class. In the textBlock3_ManipulationStarted method, after the located comment, add the following code.
[Visual Basic] CType(Application.Current, App).FirstPlayerTextString = player1TextBox.Text.ToString
5.
In the MainPage.xaml.vb or MainPage.xaml.cs file, locate the TODO comment, Construct code in MainPage to share data between pages, in the textBlock3_ManipulationStarted event handler. Use the Task List window to locate the comment. In the Task List window, double-click the row with the text, TODO: Construct code in MainPage to share data between pages.
14
6.
In the textBlock3_ManipulationStarted event handler method, after the located comment, assign the value of the Text property of the player1TextBox to the FirstPlayerTextString property in the App class. In the textBlock3_ManipulationStarted method, after the located comment, add the following code.
[Visual Basic] CType(Application.Current, App).FirstPlayerTextString = player1TextBox.Text.ToString
7.
In the MainPage.xaml.vb or MainPage.xaml.cs file, locate the TODO comment, In MainPage assign the value of the shared data (FirstPlayerTextString) to nameString below, in the OnNavigatedTo event handler. Use the Task List window to locate the comment. In the Task List window, double-click the row with the text, TODO: In MainPage assign the value of the shared data (FirstPlayerTextString) to nameString below.
8.
In the OnNavigatedTo event handler method, after the located comment, assign the value of the FirstPlayerTextString property in the App class to the Text property of the player1TextBox. In the OnNavigatedTo method, after the located comment, modify the line of code that assigns an empty string to the local nameString variable so it resembles the following code.
[Visual Basic] Dim nameString As String = CType(Application.Current, App).FirstPlayerTextString
9.
In the AdvancedGamePlay.xaml.vb or AdvancedGamePlay.xaml.cs file, locate the TODO comment, assign the value of the shared data (FirstPlayerTextString) to nameString below, in the OnNavigatedTo event handler. Use the Task List window to locate the comment. In the Task List window, double-click the row with the text, TODO: assign the value of the shared data (FirstPlayerTextString) to nameString below.
10. In the OnNavigatedTo event handler method, after the located comment, assign the value of the FirstPlayerTextString property in the App class to the Text property of the player1TextBox. In the OnNavigatedTo method, after the located comment, modify the line of code that assigns an empty string to the local nameString variable so it resembles the following code.
[Visual Basic] Dim nameString As String = CType(Application.Current, App).FirstPlayerTextString
15
11. In the AdvancedGamePlay.xaml.vb or AdvancedGamePlay.xaml.cs file, locate the TODO comment, Create a DispatcherTimer to monitor the game status, in the constructor. Use the Task List window to locate the comment. In the Task List window, double-click the row with the text, TODO: Create a DispatcherTimer to monitor the game status.
12. In the constructor, after the located comment, create the variable property of monitorGameStatusDispatcherTimer as a new instance of DispatcherTimer. Set the Interval property of property of monitorGameStatusDispatcherTimer to 34 milliseconds using a new TimeSpan object. Wire up the existing MonitorGameStatus method to the Tick event of monitorGameStatusDispatcherTimer. Start monitorGameStatusDispatcherTimer. In the constructor, after the located comment, add the following code.
[Visual Basic] monitorGameStatusDispatcherTimer = New DispatcherTimer() monitorGameStatusDispatcherTimer.Interval = New TimeSpan(0, 0, 0, 0, 34) ' 34 Milliseconds AddHandler monitorGameStatusDispatcherTimer.Tick, New EventHandler(AddressOf MonitorGameStatus) monitorGameStatusDispatcherTimer.Start()
[Visual C#] monitorGameStatusDispatcherTimer = new DispatcherTimer(); monitorGameStatusDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 34); // 34 Milliseconds monitorGameStatusDispatcherTimer.Tick += new EventHandler(MonitorGameStatus); monitorGameStatusDispatcherTimer.Start();
The Windows Phone emulator opens. It may take a while before your application is deployed, loaded, and displayed. 2. 3. 4. 5. Click the Player 1 text box. In the Player 1 text box, type Student by using the SIP keyboard. Click the application background to close the SIP keyboard. Switch to the advanced level. 6. 7. Click Go To Advanced Level.
Notice how the Student name has been copied across from the MainPage and is now stored in the Player 1 text box. In the game pad, click any of the squares, separated by a dashed line, to start the game. Notice how the application automatically selects a box. This time, the application selection is more advanced; it is actually trying to win. You, as Player 1, use the X as the symbol for a selected box, whereas the application or computer as Player 2 uses the O symbol.
16
8.
Keep playing till you win, draw, or lose. Notice that if you draw, the message box pops up with the winning message. If you win or lose, you will see a message box stating whether the X's or O's win. In addition, notice the straight line being drawn across the three winning squares. Close the message box. Notice the new message box, prompting you to play another game.
9.
12. Close the Windows Phone 7 emulator. In the Windows Phone 7 emulator toolbar, click the Close button
13. Close all applications. You have now completed this lab.