DevX HomePage

Leveraging Silverlight 2 with SQL Server and XML

With Silverlight 2 now having a go-live license, you can begin to take advantage of all of its managed code features. In this application and walkthrough we show you how to utilize both a database and XML for your Silverlight 2 application.

As businessmen who also happen to be developers, we're constantly on the lookout for technology to help us market and sell our "DrinkMate" product. A DrinkMate, in case you were wondering, is a little plastic clip that slips onto the side of your plate at a party which lets you hold both your plate and your glass in the same hand (Figure 1). Unless you see it or have used one, it is difficult to illustrate what a DrinkMate is, much less for someone to visualize how one might customize it for use in their own event, such as the name of the bride and groom imprinted on a DrinkMate for a wedding.

Figure 1. A Sample DrinkMate (the black widget holding the glass and plate together).

With Microsoft's Silverlight technology, we immediately saw the potential to create a rich interface that would help potential customers customize a DrinkMate and easily preview it before purchase. So in the fall of 2007, one of us (Cal) took the first step to implementing Silverlight on our DrinkMate.com web site. This first use of Silverlight was to allow site visitors the ability to offer proofing for standard imprints onto the DrinkMate itself. It was a useful first effort but it immediately left users—and us—wanting more. (You can see the Silverlight 1.0 project at www.drinkmate.com/standardimprints/.)

Shortly before the Silverlight Standard Imprint project, Cal had created a related feature to offer live proofing of wedding imprints from the DrinkMate website using XBAP (XAML Browser Application Project) technology. A working version of this XBAP is available at www.drinkmate.com/weddingproofs/ (see Figure 2). This project was also the subject of an MSDN Webcast last summer.

Figure 2. Drink Mate Wedding Proofs (original XBAP application).

This XBAP had its shortcomings—namely users were limited to the Windows platform and were required to download and install Version 3.0 or 3.5 of the .NET Framework for the XBAP to work. With the release of Silverlight 2, we saw the ability to migrate the XBAP to a cross-platform run-time environment and eliminate the need for users to have the .NET Framework. We were also very interested to find out how much easier (or more difficult) it would be to use Silverlight 2 than Silverlight 1.0 and likewise to make the same comparison with the full version of WPF which was used when creating the original XBAP application.

With support for managed code in Silverlight 2, we also wanted to add the functionality of allowing users to save and recall their designs for display later. This meant tying Silverlight to a SQL Server database and storing user provided data—which is one of the things for which Silverlight 2 was designed.

Almost all of our DrinkMates are sold with an imprint of some sort. Those which are used at weddings typically include the names of the bride and groom along with the date of the wedding, as is illustrated in Figure 2. The purpose of this Silverlight 2 application is to allow a prospective couple to conveniently preview various combinations of designs with different product and imprint colors, fonts and layout options, save their selections and recall them later on. In this walkthrough, we'll show you how we went about creating this application in Silverlight 2.

Getting Started with Silverlight 2—the Steps
To build this new application in Silverlight 2, we did the following:

To see the actual application in action, you can go to www.drinkmate.com/devx1.

The Normal Beta Software Disclaimer
This project was initially built using Silverlight 2 Beta 1. As with any beta code, later releases often contain breaking changes to bedevil applications written against earlier beta versions. Pursuant to Murphy's Law, Silverlight 2 Beta 2 was released just days before the scheduled publication of this article, so we revised the application (and this article) to make the application fully Beta 2 compliant.

On the bright side, later releases also tend to fix known issues such as an anomaly relating to popup windows using textboxes for user entry. Be sure to check back here for any project updates any time Silverlight 2 gets further updated.

Another issue whenever working with beta software is that not everything works exactly as desired. For example, see the following lists of known Beta 1 issues in articles by Michael Sync, Shawn Wildemuth and Delay. Silverlight Beta 2 known issues can be found in the Silverlight forums, in the comments section of Scott Guthrie's Silverlight Beta 2 post, in the comments sections of these Tim Anderson blog posts Upgrading to Silverlight 2, Silverlight, VS Setup Hassles and in this Tim Heuer blog post.

That said, we found working with Silverlight 2 to be very rewarding. There is no doubt that this is extremely promising technology. And the advantage of delving into Silverlight right now is that it's a technology which Microsoft seems very serious about promoting. The next release is expected to include over a hundred new controls. Market awareness of Silverlight will explode during the 2008 Beijing Summer Olympics when NBC is set to host thousands of hours of interactive videos on their web site using Silverlight. If you care about making yourself a more marketable programmer, learning Silverlight earlier than your peers should be a great strategy.

Layout & UI Design
We began the layout design by choosing to emulate the Standard Imprints design rather than the earlier XBAP design. Notwithstanding the enormously improved XAML designer in Visual Studio 2008, we found Expression Blend (March Preview) indispensable for working with layout. The principal difficulty with using VS 2008 for layout at this time is that its XAML designer is read only and you cannot drag and drop controls from the toolbox to the design surface or reposition the controls once you put them there. While that shortcoming should be rectified in the not-too-distant future when Service Pack 1 is released, it was simply easier to use Blend for this aspect of the project. But when it comes to writing or editing the XAML itself, VS 2008's IntelliSense is a huge advantage, which hopefully will be included in some future release of Blend.

When we built the Standard Imprints project, the set of controls available in Silverlight 1.0 didn't include Borders so we were forced to use Lines to outline the Image controls. This time with Silverlight 2, we were able to use Borders which turned out to be considerably easier than using Lines.

Silverlight 2 Border Example

<Border Grid.Row="2" Grid. Column="2" Width="160" BorderBrush="Black" BorderThickness
="1,1,2,1" >
< Grid><!-- Black -->
<  Image x:Name="BackThumbViewer2" Width="160" Stretch="Uniform"
MouseLeftButtonDown="BackThumbViewer2_MouseLeftButtonDown"
MouseEnter="ProductColorViewer_MouseEnter"
MouseLeave="ProductColorViewer_MouseLeave"
Source="BackgroundImages/BlackLandscape640.jpg" />
<  TextBlock x:Name="txbBlack" Text="Black" Margin="0,0,2,0"
FontSize="14" FontFamily="Comic Sans MS" VerticalAlignment="Bottom"
Height="20" Width="54" HorizontalAlignment="Right" />
</ Grid>
</ Border>

One of the first problems encountered was that at this point the set of controls available in Silverlight 2 does not include a combo box. So Joe built one for us. Here are the steps required to build your own control:

  1. Review some relevant tutorials such as:

  2. Determine the visual appearance which you want your control to have. A typical combobox, for example, consists of a textbox, a toggle button and a listbox. Joe initially tried using a Popup window but concluded that at this stage Popups are just too buggy, so he chose a listbox instead. The choice of a toggle button (versus a regular button) was to provide a different appearance not just while the IsPressed property is true but also for as long as the list box is displayed. It also made it possible to display or hide the list box by responding to the Checked and Unchecked events of the toggle button.

  3. Find the Styles (which also contain the Control Templates) for the controls you wish to integrate into your user control in the Silverlight documentation. To customize these styles, copy the XAML you found in the documentation to the appropriate resource section in your project (making sure to add an x:Key), make the desired changes and then assign the styles via the "Style" property. The majority of changes you likely will wish to make will be contained in the ControlTemplate section of each style.

    The following code sample illustrates how the Microsoft ToggleButton Control Template was modified to conform to the needs to integrate it into the custom combobox used in this project. For example, this customized control template includes an arrow (a vector graphic) and different color states for this arrow (eg. black for normal state, red for mouse over and green for pressed). These characteristics emulate the appearance of the button portion of Microsoft DatePicker control.

While the Control Templates for most of the Silverlight 2 controls are available in the help files or in the Microsoft public source code, unfortunately, the Control Template for the TextBox is not. For this reason, Joe decided to use the WatermarkedTextBox instead.

While the WatermarkedTextBox had the advantage of allowing the "Pick a Font" text to be set as the watermark, it also suffered from an anomaly relating to applying a Style. Silverlight 2 only allows you to set the Style property for a control once. Since Microsoft sets the Style property of the WatermarkedTextBox in its constructor, this makes it impossible to apply a custom style by separately setting the Style property. Fortunately, however it is possible to achieve the desired visual effect by using a custom ControlTemplate.

Silverlight 2 Customized ToggleButton Control Template

<!-- ToggleButton Control Template -->
<ControlTemplate TargetType="ToggleButton">
<Grid x:Name="RootElement">
<Grid.Resources>
<!-- Visual constants used by the template -->
<Color x:Key="BaseColor">#FF003255</Color>
<Color x:Key="LinearBevelLightStartColor">#FCFFFFFF
</Color> <Color x:Key="LinearBevelLightEndColor">#F4FFFFFF</Color>
<Color x:Key="LinearBevelDarkStartColor">#E0FFFFFF</Color>
<Color x:Key="LinearBevelDarkEndColor">#B2FFFFFF</Color>
<Color x:Key="MouseOverLinearBevelDarkEndColor">
#7FFFFFFF</Color>
<Color x:Key="HoverLinearBevelLightStartColor">
#FCFFFFFF</Color>
<Color x:Key
="HoverLinearBevelLightEndColor">
#EAFFFFFF</Color>
<Color x:Key="HoverLinearBevelDarkStartColor">#D8FFFFFF</Color>
<Color x:Key="HoverLinearBevelDarkEndColor">#4CFFFFFF</Color>
<Color x:Key="CheckedLinearBevelLightStartColor">#FCFFFFFF</Color>
<Color x:Key="CheckedLinearBevelLightEndColor">#EAFFFFFF</Color>
<Color x:Key="CheckedLinearBevelDarkStartColor">#D8FFFFFF</Color>
<Color x:Key="CheckedLinearBevelDarkEndColor">#4CFFFFFF</Color>
<Color x:Key="CurvedBevelFillStartColor">#B3FFFFFF</Color>
<Color x:Key="CurvedBevelFillEndColor">#3CFFFFFF</Color>
<Color x:Key="HoverPressedCurvedBevelFillStartColor">#6FFFFFFF</Color>
<Color x:Key="HoverPressedCurvedBevelFillEndColor">#39FFFFFF</Color>
<Color x:Key="ArrowColor">#FF000000</Color>
<Color x:Key="ArrowActiveColor">#FF00B300</Color>
<Color x:Key="ArrowHoverColor">#FFFF0000</Color>
<SolidColorBrush x:Key="BorderBrush" Color="#FF000000" />
<SolidColorBrush x:Key="AccentBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="DisabledBrush" Color="#A5FFFFFF" />
<LinearGradientBrush x:Key="FocusedStrokeBrush"
StartPoint="0.5,0"Endpoint="0.5,1">
<GradientStop Color="#B2FFFFFF" Offset="0" />
<GradientStop Color="#51FFFFFF" Offset="1" />
<GradientStop Color="#66FFFFFF" Offset="0.325" />
<GradientStop Color="#1EFFFFFF" Offset="0.325" />
</LinearGradientBrush>
</Grid.Resources>
<!-- Visual states of the togglebutton template -->
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CheckedMouseStates">
<vsm:VisualState x:Name="MouseLeave">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetName="ArrowBrush"
Storyboard.TargetProperty="Color" To="{StaticResource
ArrowColor}" />
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="MouseEnter">
<Storyboard>
<ColorAnimation Duration="0:0:0.1"
Storyboard.TargetName="ArrowBrush"
Storyboard.TargetProperty="Color" To="{StaticResource
ArrowHoverColor}" />
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition To="MouseOver" Duration="0:0:0.2" />
<vsm:VisualTransition To="Pressed" Duration="0:0:0.1" />
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Normal" />
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="LinearBevelDarkEnd"
Storyboard.TargetProperty="Color" To="{StaticResource
MouseOverLinearBevelDarkEndColor}"
Duration="0" />
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="LinearBevelLightEnd"
Storyboard.TargetProperty="Offset" To=".2" Duration="0" />
<ColorAnimation Storyboard.TargetName="LinearBevelLightStart"
Storyboard.TargetProperty="Color" To="{
StaticResource HoverLinearBevelLightEndColor}"
Duration="0" /><ColorAnimation Storyboard.TargetName="LinearBevelLightEnd"
Storyboard.TargetProperty="Color" To="{
StaticResource
HoverLinearBevelLightEndColor}"
Duration="0" />
<ColorAnimation Storyboard.TargetName="LinearBevelDarkStart"
Storyboard.TargetProperty="Color" To="{
StaticResource HoverLinearBevelDarkStartColor}"Duration="0" />
<ColorAnimation Storyboard.TargetName="LinearBevelDarkEnd"
Storyboard.TargetProperty="Color"
To="{
StaticResource HoverLinearBevelDarkEndColor}" Duration="0" />
<DoubleAnimation Storyboard.TargetName="DownStroke"
Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Disabled">
<Storyboard>
<Storyboard.TargetName="DisabledVisual"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0" />
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="CheckStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition Duration="0:0:0.1" />
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="DownStroke"
Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualElement"
Storyboard.TargetProperty="Opacity" To="0" Duration="0" />
<ColorAnimation Storyboard.TargetName="ArrowBrush"
Storyboard.TargetProperty="Color" To="{
StaticResource ArrowActiveColor}" Duration="0"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Unchecked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="LinearBevelLightEnd"
Storyboard.TargetProperty ="Offset" To="0.35" Duration="0" />
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Indeterminate">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="LinearBevelLightEnd"
Storyboard.TargetProperty ="Offset" To="0.2" 
Duration="0" />
<DoubleAnimation Storyboard.TargetName
="IndeterminateNormal" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="FocusStates">
<vsm:VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName
="FocusVisualElement"
Storyboard.TargetProperty="Visibility"Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Unfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName
="FocusVisualElement"
Storyboard.TargetProperty="Visibility"Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value><Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Rectangle RadiusX="1"
RadiusY="1" Margin="1,2,1.5,2" StrokeThickness="1">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.7,0" Endpoint="0.7,1">
<GradientStop x:Name="LinearBevelLightStart" Color="{
StaticResource LinearBevelLightStartColor}" Offset="0" />
<GradientStop x:Name="LinearBevelLightEnd" Color="{
StaticResource LinearBevelLightEndColor}" Offset="0.35" />
<GradientStop x:Name="LinearBevelDarkStart" Color="{
StaticResource LinearBevelDarkStartColor}" Offset="0.35" />
<GradientStop x:Name="LinearBevelDarkEnd"
Color="{
StaticResource LinearBevelDarkEndColor}" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Grid x:Name="CurvedBevelScale" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="7*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Path x:Name="CurvedBevel" Stretch="Fill" Margin="1.5,1,1.5,2"
Data="F1 M0,0.02 L0,0.15
C0.15,0.22 0.3,0.25 0.5,0.25 0.7,0.25 0.85,
0.22 1,0.15 L1,0.02 1,0 0.02,0 0,0.02 z">
<Path.Fill>
<LinearGradientBrush StartPoint="0.5,0" Endpoint="0.5,1">
<GradientStop x:Name="CurvedBevelFillStart" Color="{
StaticResource CurvedBevelFillStartColor}" Offset="0" />
<GradientStop x:Name="CurvedBevelFillEnd" Color="{
StaticResource CurvedBevelFillEndColor}" Offset="1" />
</LinearGradientBrush>
</Path.Fill>
</Path>
</Grid>
<Grid x:Name
="FocusVisualElement" Visibility="Collapsed">
</Grid>
<Grid x:Name="CheckedNormal" Opacity="0">
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2.5,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2.25,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,1.75,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="1" Margin="1" />
<Rectangle RadiusX="1"
RadiusY="1" StrokeThickness="1" Margin="1" >
<Rectangle.Stroke>
<LinearGradientBrush Endpoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#A5FFFFFF" Offset="0" />
<GradientStop Color="#FFFFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
<Grid x:Name="CheckedPressed" Opacity="0">
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2.5,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2.25,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,1.75,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="1" Margin="1" />
<Rectangle RadiusX="1"
RadiusY="1" StrokeThickness="1" Margin="1">
<Rectangle.Stroke>
<LinearGradientBrush Endpoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#A5FFFFFF" Offset="0" />
<GradientStop Color="#FFFFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
<Grid x:Name="IndeterminateNormal" Opacity="0">
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2.5,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2.25,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,1.75,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="1" Margin="1" />
<Rectangle RadiusX="1"
RadiusY="1" StrokeThickness="1" Margin="1">
<Rectangle.Stroke>
<LinearGradientBrush Endpoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#A5FFFFFF" Offset="0" />
<GradientStop Color="#FFFFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
<Grid x:Name="IndeterminatePressed" Opacity="0">
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1" StrokeThickness="1" Opacity="0.05" Margin="1.5,2.5,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="0.05" Margin="1.5,2.25,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="0.05" Margin="1.5,2,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="0.05" Margin="1.5,1.75,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="1"Margin="1" />
<Rectangle RadiusX="1"
RadiusY="1"StrokeThickness="1"Margin="1">
<Rectangle.Stroke>
<LinearGradientBrush Endpoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#A5FFFFFF" Offset="0" />
<GradientStop Color="#FFFFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
<Grid x:Name="DownStroke" Opacity="0">
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="0.05" Margin="1.5,2.5,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="0.05" Margin="1.5,2.25,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="0.05" Margin="1.5,2,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="0.05" Margin="1.5,1.75,1.5,1.5" />
<Rectangle Stroke="{TemplateBinding Background}" RadiusX="1"
RadiusY="1"StrokeThickness="1"Opacity="1" Margin="1" />
<Rectangle RadiusX="1"
RadiusY="1" StrokeThickness="1" Margin="1">
<Rectangle.Stroke>
<LinearGradientBrush Endpoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#A5FFFFFF" Offset="0" />
<GradientStop Color="#FFFFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
<!-- ToggleButton content -->
<Grid>
<Path Margin="0,0,0,0" HorizontalAlignment="Center"
VerticalAlignment="Center" Data="M 0,0 L 3.5,4 L 7,0 Z">
<Path.Fill>
<SolidColorBrush x:Name="ArrowBrush" Color="{
StaticResource ArrowColor}"/>
</Path.Fill>
</Path>
</Grid>
</Grid>
</ControlTemplate> 





Control Data Binding vs. Event Handling
Although the XBAP project relied almost entirely on Data Binding to link changes in the text property of the user entry TextBoxes to the text property of the floating (imprint) TextBlocks, after studying this area, we concluded that at this early beta stage, data binding to control properties in Silverlight 2 was not yet practical for our purposes. (The current Silverlight version of Data Binding has neither an ElementName nor a Path property.) Instead, we used a series of event handlers which set the text property of each TextBlock equal to the text property of its corresponding TextBox.

Editor's Note: For a walkthrough of databinding as it stands today in Silverlight, check out this tutorial on silverlight.net.

Setting the Text Properties of a TextBlock

private void txtFirstName_TextChanged
(object sender, TextChangedEventArgs e)
{//When the user types into the First Name textbox,
add the same text in the First Name textblock
txbFirstName.Text = txtFirstName.Text;
PositionTextBlockInCanvas(txbFirstName);
//Adjust the location if it would fall
//outside the Imprint Area
If (txbFirstName.Text.Length == 0)
{//Also, unhide the And Character whenever the
//First Name textblock contains one or more letters
txbAndCharacter.Visibility = Visibility.Collapsed;
}
else
{
txbAndCharacter.Visibility = Visibility.Visible;
}
}

In this code sample, the Visibility property of the AndCharacter TextBlock is linked to the length of the Text property of the FirstName TextBlock. When there is no content in the FirstName TextBlock, the AndCharacter TextBlock is not visible but as soon as the user begins typing in the FirstName TextBox, the AndCharacter TextBlock is automatically set to Visible.

Changing the imprint color (Foreground property of the floating TextBlocks) is accomplished via event handlers. The MouseEnter event handler creates a SolidColorBrush which matches the color of the color rectangle currently beneath the cursor and then calls the ApplyNewImprintColorMethod.

Silverlight 2 Changing the Foreground Property of Floating TextBlocks

<!-- Colored rectangles representing Drink Mate standard imprint colors -->
<Rectangle x:Name="ImprintColorRectangle1" Height="30" Width="30" Fill="#000000" Stroke="#FFFFFF" StrokeThickness="0" MouseEnter="handleMouseEnterImprintColors" MouseLeftButtonDown="handleImprintColorClick" MouseLeave="handleMouseLeaveImprintColors" /><! --Black --> <Rectanglex:Name="ImprintColorRectangle2" Height="30" Width="30" Fill="#000099" Stroke="#FFFFFF" StrokeThickness="0" MouseEnter="handleMouseEnterImprintColors" MouseLeave ="handleMouseLeaveImprintColors" MouseLeftButtonDown ="handleImprintColorClick" /><! --Reflex Blue --> <Rectangle x:Name="ImprintColorRectangle3" Height="30" Width="30" Fill="#0000CC" Stroke="#FFFFFF" StrokeThickness="0" MouseEnter="handleMouseEnterImprintColors" MouseLeave="handleMouseLeaveImprintColors" MouseLeftButtonDown="handleImprintColorClick" /><! -- Medium Blue --> <Rectangle x:Name="ImprintColorRectangle4" Height="30" Width="30" Fill="#3232FF" Stroke="#FFFFFF" StrokeThickness="0" MouseEnter="handleMouseEnterImprintColors" MouseLeave="handleMouseLeaveImprintColors" MouseLeftButtonDown="handleImprintColorClick" /><! -- Process Blue --> private void handleMouseEnterImprintColors(object sender, MouseEventArgs e) { Rectangle rect = (Rectangle)sender; SolidColorBrush newImprintColor = (SolidColorBrush)rect.Fill; ApplyNewImprintColor(newImprintColor); bdrTooltips.Visibility = Visibility.Visible; txbTooltips.Text = "Click to change the imprint color."; } private void ApplyNewImprintColor(SolidColorBrush newImprintColor) { try { txbFirstName.Foreground = newImprintColor; txbAndCharacter.Foreground = newImprintColor; txbSecondName.Foreground = newImprintColor; txbEventDate.Foreground = newImprintColor; } catch (System.Exception ex) { Console.WriteLine(ex.Message); } }

After the MouseLeave event occurs, the ApplyNewImprintColorMethod is called again to restore the imprint color to its currently selected value.

Drag and Drop
Different fonts have different metrics and when a user switches from font to font, the layout of the imprint information is not always optimal. To compensate for this we added the capability for a user to simply drag each floating TextBlock to a new location of his choice. Adding this drag and drop functionality under Silverlight 1.0 was a snap because all that was needed to do was to copy some code which we got from a Jesse Liberty video tutorial. Upgrading to Silverlight 2 was no more difficult because all we had to do this time was to copy and paste similar code from the Silverlight help files.

Silverlight 2 Text Drag and Drop

public void TextBlock_MouseDown(object sender, MouseEventArgs args)
{
TextBlock item = sender as TextBlock;
mouseVerticalPosition = args.GetPosition(null).Y;
mouseHorizontalPosition = args.GetPosition(null).X;
isMouseCaptured = true;
item.CaptureMouse();
}
public void TextBlock_MouseMove(object
sender, MouseEventArgs args)
{
TextBlock item = sender as TextBlock;
if (isMouseCaptured)
{
// Calculate the current position of the object. 
double deltaV = args.GetPosition(null).Y
- mouseVerticalPosition;
double deltaH = args.GetPosition(null).X
- mouseHorizontalPosition;
double newTop = deltaV + (double)item.GetValue
(Canvas.TopProperty);
double MaxTop = cnvImprintArea.ActualHeight - item.ActualHeight;
double MaxLeft = cnvImprintArea.ActualWidth - item.ActualWidth;
//Put in some limitations to prevent a user from
//dragging a TextBlock outside the imprint area
if (newTop > MaxTop)
{
newTop = MaxTop;
}
if (newTop < 0)
{
newTop = 0;
}
double newLeft = deltaH +
(double)item.GetValue(Canvas.LeftProperty);
if (newLeft > MaxLeft)
{
newLeft = MaxLeft;
}
if (newLeft < 0)
{
newLeft = 0;
}
// Set new position of object. 
item.SetValue(Canvas.TopProperty, newTop);
item.SetValue(Canvas.LeftProperty, newLeft);
// Update position global variables. 
mouseVerticalPosition = args.GetPosition(null).Y;
mouseHorizontalPosition = args.GetPosition(null).X;
}
}
public void
TextBlock_MouseUp(object sender, MouseEventArgs args)
{
TextBlock item = sender as TextBlock;
isMouseCaptured = false;
item.ReleaseMouseCapture();
mouseVerticalPosition = -1;
mouseHorizontalPosition = -1;
}

Working with Custom Fonts
Wedding planners generally tend to use script-type fonts in their printing. While Silverlight 2 includes a number of attractive fonts, none of them is particularly suited to use with wedding designs. Fortunately, it is possible to include custom fonts with your Silverlight app by following these simple steps:

Silverlight 2 Working with Custom Fonts

//Using a custom font in Silverlight requires a combination of the filename
//along with the technical font name
string m_strAlexiCopperplate = @"alcoppln.ttf#Alexei Copperplate";
string m_strBrushScript = @"brushscn.ttf#BrushScript BT";
string m_strFlemishScript = @"flemscrn.ttf#FlemishScript BT";
string m_strFreestyle = @"frestysn.ttf#FreestyleScrD";
string m_strLinus = @"linusn.ttf#Linus";
string m_strParkAvenue = @"parkaven.ttf#ParkAvenue BT";
string m_strShelleyAllegro = @"shlyalln.ttf#ShelleyAllegro BT";
string m_strWeddingText = @"wedtxtn.ttf#WeddingText BT";
public void setFontFamilyProperty(string strFontFamily)
{
try
{
string strSelectedFontFamily;
switch (strFontFamily)
{//When a given case contains two entries,
the upper entry come from a selection in the combo box
//Lower entries come from saved designs
(some are identical, in which case only a
single entry is required)
case "Alexei Copperplate":
strSelectedFontFamily = m_strAlexiCopperplate;
break;
case "Brush Script":
case "BrushScript BT":
strSelectedFontFamily = m_strBrushScript;
break;
case "Flemish Script":
case "FlemishScript BT":
strSelectedFontFamily = m_strFlemishScript;
break;
case "Freestyle":
case "FreestyleScrD":
strSelectedFontFamily = m_strFreestyle;
break;
case "Linus":
strSelectedFontFamily = m_strLinus;
break;
case "Park Avenue":
case "ParkAvenue BT":
strSelectedFontFamily = m_strParkAvenue;
break;
case "Shelley Allegro":
case "ShelleyAllegro BT":
strSelectedFontFamily = m_strShelleyAllegro;
break;
case "Wedding Text":
case "WeddingText BT":
strSelectedFontFamily = m_strWeddingText;
break;
default:
strSelectedFontFamily = m_strAlexiCopperplate;
break;
}
txbFirstName.FontFamily = new FontFamily(strSelectedFontFamily);
txbAndCharacter.FontFamily = new FontFamily(strSelectedFontFamily);
txbSecondName.FontFamily = new FontFamily(strSelectedFontFamily);
txbEventDate.FontFamily = new FontFamily(strSelectedFontFamily);
//Display the message about Drag and Drop capability
bdrTooltips.Visibility = Visibility.Visible;
txbTooltips.Text = "Imprints can be repositioned by dragging.";
//Set a timer to hide the message about Drag and Drop
m_dtDragAndDropMessageTimer.Interval =
new
TimeSpan(0, 0, 0, 10, 0);
// 10 Seconds
m_dtDragAndDropMessageTimer.Tick += new
EventHandler(dtDragAndDropMessageTimer_Tick);
m_dtDragAndDropMessageTimer.Start();
}
Catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
}

Using this same technique permits the drop down list of our custom combobox to preview the appearance of each custom font.

For more information on using Custom Fonts you can review this piece written by Tim Heuer. For a discussion of alternative delivery options, see this article by Rob Houweling.





Design Parameter Storage and Retrieval
Naturally you want every project to improve on any previous version so in this case we decided to add a feature which allows a user to save the parameters for a given design and then later to retrieve that design. Of course, the repository for these design parameters is a SQL Server database.

The database design to store the information required to permit a user to retrieve a stored imprint design is relatively simple. All that was needed was a single table which contained each of the various design parameters (product color, imprint color, font family, etc.)


Figure 5. SQL Server Table for Storing Design Parameters

Silverlight 2 Code for Popup Control

<!-- Popup to display stored links -->
<Popup x:Name="popDisplayStoredLinks">
<Grid x:Name="grdDisplayStoredLinks" Background="#007799aa">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" BorderThickness="2" Background="WhiteSmoke"
CornerRadius="15" Grid.Column="1"
Grid.Row="1">
<StackPanel
Margin="8" Width="330" >
<StackPanel
Orientation="Vertical">
<TextBlock
Margin="5,5,0,5"
HorizontalAlignment="Center"
FontFamily="Comic Sans MS"
FontSize="22" >
Saved Designs for </TextBlock>
<TextBlock
x:Name="txbDisplayStoredLinksUserID"
Margin="0,0,5,5"
HorizontalAlignment="Center"
FontFamily="Comic Sans MS"
FontSize="22"
Text="UserIDHere" />
</StackPanel>
<ListBox x:Name="lstSavedDesigns" SelectionChanged
="lstSavedDesigns_SelectionChanged"
MouseLeftButtonUp="lstSavedDesigns_MouseLeftButtonUp"
Margin="10" />
<Button
x:Name="btnPopCancelDisplayStoredLinks"
Click="btnPopCancelDisplayStoredLinks_Click"
Margin="5"
Content="Cancel" Width="100"
HorizontalAlignment="Center" VerticalAlignment="Bottom" />
</StackPanel>
</Border>
</Grid>
</Popup> }
//This popup window is triggered
by setting its IsOpen property to true
void webService_GetDesignsByUserIdCompleted(object
sender, CustomerDesignsServiceReference.GetDesigns
ByUserIdCompletedEventArgs e)
{
if (_dbOperationsStatus[e.UserState.ToString()] ==
_dbOperationStatus.Canceling)
{
_dbOperationsStatus[e.UserState.ToString()] =
_dbOperationStatus.Canceled;
}
else
{
try
{
RetrievalCircleAnimation.Stop();
brdWaitAnimationRetrieveStoredLinks.Opacity = 0;
m_dtDatabaseRetrievalTimeout.Stop();
//Check to see how many results have
been returned
//If no results, display an error message
in the existing form
if (e.Result.Length < 1)
{
_dbOperationsStatus[e.UserState.ToString()] =
_dbOperationStatus.Selected;
txbRetrieveStoredLinksResults.Text = "No artwork
found for UserID "
+ txtRetrievalUserID.Text;
btnRetrieveStoredLinksMaskClose.Content = "Close";
return;
}
//If there is only one result, just display the
saved design immediately
else if (e.Result.Length == 1)
{
_dbOperationsStatus[e.UserState.ToString()] =
_dbOperationStatus.Selected;
RetrieveStoredLinksMask.Visibility = Visibility.Collapsed;
popRetrieveStoredLinks.IsOpen = false;
m_cdSavedDesigns = e.Result.ToList();
DisplaySavedDesign(0);
}
else if (e.Result.Length > 1)
{
_dbOperationsStatus[e.UserState.ToString()] =
_dbOperationStatus.Selected;
//If there are multiple results, save each record to a
CustomerDesign object and put a link in the
Display Stored Links popup
RetrieveStoredLinksMask.Visibility = Visibility.Collapsed;
m_cdSavedDesigns = e.Result.ToList();
lstSavedDesigns.ItemsSource = m_cdSavedDesigns;
lstSavedDesigns.DisplayMemberPath = "DesignName";
popRetrieveStoredLinks.IsOpen = false;
popDisplayStoredLinks.VerticalOffset =
(MainImageViewer.ActualHeight / 2)
- (174 / 2) + HeaderImage.ActualHeight - 25;
popDisplayStoredLinks.HorizontalOffset =
(HeaderImage.ActualWidth / 2)
- (350 / 2);
popDisplayStoredLinks.IsOpen = true;
txbDisplayStoredLinksUserID.Text = e.Result[0].UserID.ToString();
}
}
catch (System.Exception ex)
{
_dbOperationsStatus[e.UserState.ToString()] = _dbOperationStatus.Failed;
txbRetrieveStoredLinksResults.Text = "An Error occurred while
retrieving your artwork. Please try again.";
btnRetrieveStoredLinksMaskClose.Content = "Close";
btnRetrieveStoredLinksMaskClose.Focus();
Console.WriteLine(ex.Message);
}
}

Wait Animation
Since the process of communicating with a database located on a remote server is subject to the vagaries of the Internet, we decided to build a wait animation to request a user's patience while data is being saved or retrieved. A popular design these days for wait animations involves a series of circles themselves arranged in a circle. Each participating circle changes either its color or opacity in sequence with its partners, giving the effect of a Ferris wheel spinning around.

Figure 7.

Silverlight 2 Wait Animation

<!-- Wait Animation -->
<Grid
x:Name="RetrieveStoredLinksMask" Visibility="Collapsed" >
<Rectangle
Fill="Black" Opacity=".7" RadiusX="13" RadiusY="13"/>
<StackPanel >
<Border x:Name="brdWaitAnimationRetrieveStoredLinks"
BorderThickness="0"
BorderBrush="#FF000000"
Margin="0,40,0,20"
Height="64"
Width="64" >
<Border.Background>
<LinearGradientBrush
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop
Color="#FF7A7777"/>
<GradientStop
Color="#FF181717" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Grid
ShowGridLines>="False" >
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="Auto" />
<ColumnDefinition
Width="Auto"/>
<ColumnDefinition />
<ColumnDefinition
Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition
Height="Auto" />
<RowDefinition
Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Ellipse x:Name
="waitAnimationCircle1" Grid.Row
="3" Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0,-10,0,0"
Height="16" Width="16" Fill="White" />
<Ellipse x:Name="waitAnimationCircle2"
Grid.Row="1"
Grid.Column="0" Margin="6,2,0,0"
Height="16"
Width="16" Fill="#FFA7A6A6" />
<Ellipse x:Name="waitAnimationCircle3"
Grid.Row="0"
Grid.Column="1" 
Grid.ColumnSpan="2"
Margin="0,0,0,-10" Height="16"
Width="16" Fill="#FFA7A6A6" />
<Ellipse x:Name
="waitAnimationCircle4"
Grid.Row="1"
Grid.Column="3"
Margin="0,0,6,0"
Height="16"
Width="16" Fill="#FFA7A6A6" />
<Ellipse x:Name
="waitAnimationCircle5"
Grid.Row="2"
Grid.Column="3"
Margin="0,0,6,0"
Height="16"
Width="16" Fill="#FFA7A6A6" />
<
Ellipse x:Name
="waitAnimationCircle6" Grid.Row="2"
Grid.Column="0"
Margin="6,2,0,0"
Height="16"
Width="16" Fill="#FFA7A6A6" />
</Grid>
</Border>
<Border
Background="White"
CornerRadius="5"
Margin="20" >
<TextBlock
x:Name="txbRetrieveStoredLinksResults"
HorizontalAlignment="Center"
TextWrapping="Wrap"
Text=""
Foreground="Black"
TextAlignment="Center" />
</Border>
<Button x:Name="btnRetrieveStoredLinksMaskClose" Click
="btnRetrieveStoredLinksMaskClose_Click"
HorizontalAlignment="Center" Margin="20"
Width="100" Content="Cancel"></ Button>
</StackPanel>
</Grid><!
--End of Wait Animation -->
//This wait animation can be started by calling its Begin() method
and later stopped by calling its Stop() method. 
RetrievalCircleAnimation.Begin();
...
RetrievalCircleAnimation.Stop();

Building WPF/Silverlight animations is the forte of Expression Blend. In this case, the animation uses keyframes which change the color of the little circles to create the spinning effect. A useful technique for building an animation which will be incorporated into a much larger project is to create a separate Silverlight project with Blend and then once the animation has been refined, to transfer it into the main project.

Figure 8. Expression Blend Timeline for Wait Animation

Expression Blend Animation to Retrieve Designs Popup

<!-- Wait Animation for Retrieve Designs Popup -->
<Storyboard x:Name
="RetrievalCircleAnimation"
RepeatBehavior="Forever"
SpeedRatio=".25">
<ColorAnimationUsingKeyFrames
Storyboard.TargetName
="waitAnimationCircle6"
Storyboard.TargetProperty
="(Shape.Fill).(SolidColorBrush.Color)"  BeginTime="00:00:00">
<SplineColorKeyFrame KeyTime="00:00:00.1000000" Value="#FFFFFFFF"/>
<SplineColorKeyFrame KeyTime="00:00:00.2000000" Value="#FFA7A6A6"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="waitAnimationCircle1"
Storyboard.TargetProperty
="(Shape.Fill).(SolidColorBrush.Color)"  BeginTime="00:00:00">
<SplineColorKeyFrame KeyTime="00:00:00.1000000" Value="#FFA7A6A6"/>
<SplineColorKeyFrame KeyTime="00:00:00.6000000" Value="#FFFFFFFF"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames
Storyboard.TargetName="waitAnimationCircle2"
Storyboard.TargetProperty
="(Shape.Fill).(SolidColorBrush.Color)" BeginTime="00:00:00">
<SplineColorKeyFrame KeyTime="00:00:00.2000000" Value="#FFFFFFFF"/>
<SplineColorKeyFrame KeyTime="00:00:00.3000000" Value="#FFA7A6A6"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames
Storyboard.TargetName="waitAnimationCircle3"
Storyboard.TargetProperty
="(Shape.Fill).(SolidColorBrush.Color)"  BeginTime="00:00:00">
<SplineColorKeyFrame KeyTime="00:00:00.3000000" Value="#FFFFFFFF"/>
< SplineColorKeyFrame KeyTime="00:00:00.4000000" Value="#FFA7A6A6"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames
Storyboard.TargetName="waitAnimationCircle4"
Storyboard.TargetProperty
="(Shape.Fill).(SolidColorBrush.Color)"  BeginTime="00:00:00">
<SplineColorKeyFrame KeyTime="00:00:00.4000000" Value="#FFFFFFFF"/>
<SplineColorKeyFrame KeyTime="00:00:00.5000000" Value="#FFA7A6A6"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames
Storyboard.TargetName="waitAnimationCircle5"
Storyboard.TargetProperty
="(Shape.Fill).(SolidColorBrush.Color)" BeginTime="00:00:00">
<SplineColorKeyFrame KeyTime="00:00:00.5000000" Value="#FFFFFFFF"/>
<SplineColorKeyFrame KeyTime="00:00:00.6000000" Value="#FFA7A6A6"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>





Building a WCF Web Service
The mechanism we selected for saving customer designs into the SQL Server database was a WCF Web Service hosted on our web server. The model for building this WCF Service was another excellent tutorial by Jesse Liberty.

Following the recognized best practice, our Service Contract is contained in an Interface. The Operation Contracts include a method for saving design parameters, retrieving design information by UserID and by a design ID (AutoID).


[ServiceContract]
public interface ICustomerDesigns
{
[OperationContract]
List<CustomerWeddingImprintDesign> GetDesignsByUserId(string UserId);
[OperationContract]
int InsertNewCustomerWeddingImprintDesign
(CustomerWeddingImprintDesign cstIDesign);
[OperationContract]
List<CustomerWeddingImprintDesign> GetDesignByAutoId(int AutoID);
}

Although the process of creating a WCF Web Service seems very daunting and complicated involving an enormous amount of code, much of it will be automatically generated for you by Visual Studio. One part, however, which you will have to write yourself is the implementation code for your Service Contract.

#region ICustomerDesigns Members
public List<CustomerWeddingImprintDesign> GetDesignsByUserId(string UserId)
{
try
{
DBAccessDataContext dc = new DBAccessDataContext();
var matchingDesigns = from CustomerWeddingImprintDesigns
in dc.CustomerWeddingImprintDesigns
where CustomerWeddingImprint
Designs.UserID.Equals(UserId)
select CustomerWeddingImprintDesigns;
return matchingDesigns.ToList();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
public List<CustomerWeddingImprintDesign>
GetDesignByAutoId(int AutoID)
{
DBAccessDataContext dc = new
DBAccessDataContext();
var matchingDesigns = from CustomerWeddingImprintDesigns
in dc.CustomerWeddingImprintDesigns
where
CustomerWeddingImprintDesigns.AutoID.Equals(AutoID)
select CustomerWeddingImprintDesigns;
return matchingDesigns.ToList();
}
public int InsertNewCustomerWeddingImprintDesign
(CustomerWeddingImprintDesign custIDesign)
{
try
{
DBAccessDataContext dc = new
DBAccessDataContext();
dc.CustomerWeddingImprintDesigns.InsertOnSubmit(custIDesign);
// Insert count of changes to be made to the database
System.Data.Linq.ChangeSet changes = dc.GetChangeSet();
dc.SubmitChanges();
return changes.Inserts.Count;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
return 0;
}
}
#endregion}

In this case the required implementation simply consists of LINQ queries to save and retrieve data to/from the database. Since .NET datatypes and SQL Server datatypes are not identical, it is critical when using LINQ to SQL that the datatypes you do use at each end of the conversation are compatible. Moreover, since Beta 2 now requires when calling the SetValue method that all parameters must be of the exact correct data type, (implicit conversions previously were permitted), care must also be taken to conform to this requirement.

When a Silverlight application is running on the same server as a WCF service, no Cross-Domain policy file is needed. However, if you wish to run or debug a locally running Silverlight application that references a remote WCF service, or if the Silverlight application is hosted on a server that has multiple base addresses, a Cross-Domain policy file is needed. The required format for Cross-Domain policy files changed between Beta 1 and Beta 2, so you will need to exercise special caution to select the correct one if you are copying any sample text from the Internet. For more information on Cross-Domain policy files read the section titled "Configuring a Cross-Domain Policy File (clientaccesspolicy.xml)" at www.wpflearningexperience.com.

Implementation Issues
Joe has put together an excellent blog article describing a number of implementation problems which we encountered while deploying this application at our web host. Since a detailed explanation of these issues is available elsewhere, we'll describe only those which relate to the code contained in this project.

One of the limitations of WCF Web Services hosted on IIS is that each service can have only a single base address. Unfortunately, under some hosting configurations multiple base addresses can exist, thereby confusing WCF. An example of multiple identities would be domain.com and www.domain.com. If, as is often the case, a modification of the hosting configuration is not an option, a relatively simple workaround can trick WCF into only seeing a single base address. This workaround involves creating a custom service factory and overriding the CreateServiceHost() method so that it returns only the first base address reported by IIS. Blog articles by Rob Zelt and Rob Reynolds can provide additional helpful details.

Silverlight 2 Creating a Custom Service Factory

protected override ServiceHost CreateServiceHost(Type serviceType, Uri
[] baseAddresses)
{
// If more than one base address exists then return the second
// address, otherwise return the first address
(protected override ServiceHost CreateServiceHost
(Type serviceType, Uri[] baseAddresses)
{
// If more than one base address exists then return the second
// address, otherwise return the first address
//(You might be wondering why we're picking the second address here
//rather than the first.
//For us, the second address holds the correct address for our deployment domain
//www.silverlightblend.com. You may need to modify this code to return
//a different/custom base address depending on your IIS server configuration. 
if (baseAddresses.Length > 1)
{
return new ServiceHost(serviceType, baseAddresses[1]);
}
else
{
return new ServiceHost(serviceType, baseAddresses[0]);
}
}

The appropriate location for this Custom Host Factory is in the ServiceName.svc.cs file as a sister class to your Service Contract (Interface). Another minor modification required is to add "Factory="YourNamespaceName.CustomHostFactory" to your ServiceName.svc file as shown in the following code:

<% @ ServiceHost
Language="C#" Debug
="true" Service="DMSL2WeddingImprints_Web.CustomerDesigns"
Factory="DMSL2WeddingImprints_Web.CustomHostFactory"
CodeBehind="CustomerDesigns.svc.cs" %>

Finally, don't forget that your web server (not limited to IIS) must have the necessary Silverlight MIME types, particularly one for the .xap files used by Silverlight 2. (xap application/x-silverlight-app) If your web host uses IIS 6, this MIME type must be set administratively. If it uses IIS 7, in some cases this MIME type can be set via a web.config file. For a more detailed explanation of these issues see Joe's blog article.

Recommended Resources
Some resources you'll need as you develop your Silverlight 2 application are:

Silverlight 2 is unquestionably a major advance over Silverlight 1.0. For an experienced C# developer, it is much more comfortable and productive writing code behind in C# instead of JavaScript. And even though not all controls are yet available in Beta 2 (and not all of the ones which are present are completely finished either), the ones that are promised will make developers that much more productive. Silverlight 2 technology allows developers to design and deploy features which previously would have taken teams of people to do. By pulling together these components, Silvelight will make you more productive and (potentially) release your creativity.

* This article was commissioned by and prepared for Microsoft Corporation. This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.