Contents
After some time of listening to my husband saying to me, "write an article darling", I finally decided to give it a go. I started looking into Silverlight (read a book or two, many articles, and watched loads of videos), and got very excited about the technology, especially its capabilities to develop line of business applications. So, I decided to learn Silverlight by building a real project and thought others could benefit if I shared my experience. As it turned out, the project evolved into quite a cool application showcasing some of the new Silverlight 4 features such as RIA services, Right-click event handling, as well as embedding and playing YouTube videos within a Silverlight application. This is my article writing debut, so please be gentle.
Silverlight is an exciting new technology that is progressively evolving and just getting better and better. This demo is built in Silverlight 4 Beta, which is the latest available developer release. The present official release of Silverlight is version 3, released in July 2009. Silverlight 3 came with many new features and some major improvements such as out of browser support or enhanced data support like element to element binding. Silverlight 4 Beta offers further improvements and many exciting new features such as microphone support, right click handling, and printing. A good place to start learning about Silverlight is the silvelright.net website where you can find tutorials, videos, and more.
The demo application is a Silverlight YouTube Jukebox which allows users to select artists, view their albums, select an album, and view the album's tracks. Users can right click on any track name and select to view the track details. By selecting a track (left click), users initiate a search for a YouTube video. Up to four possible matches are presented to the user as thumb images; the video title appears as the user mouses over the image. The user can then click on any of the images to view the YouTube video.
The demo application was developed using Visual Studio Beta 2 and Silverlight 4 Beta. You will need the following:
Please note that Visual Studio 2010 Beta 2 can misbehave with Silverlight 4. I have experienced many crashes over the course of developing this application. Visual Studio 2010 and .NET 4 release candidates are now available; however, at the time of writing, Silverlight 4 projects were not supported, therefore I haven't upgraded to the release candidate.
- RIA Services and Visual Studio 2010 support
- Themes and Styles
- Right-click event handling
- Hosting HTML content in Silverlight in-browser applications
- Embedding the YouTube video search control
The demo is based on the Silverlight Business Application template which is a new template that we get when we install .NET RIA Services, and comes with styled views, page navigation, as well as localization, authentication, and registration support. As Brad Abrams explains here, the template solution setup follows the "RIA Application" pattern in which the two tiers (projects), the Silverlight client, and the server tiers are connected in a way that any changes in one are reflected in the other. For the purpose of the demo, I modified some parts of this template, such as styles, and I also unhooked all unnecessary views. The demo uses the Chinook database which represents a digital media store, and is a nice alternative to the Northwind database. The Chinook database can be downloaded here. The demo uses the Entity Framework for data access. Our data model depicted in Figure 1 is generated from the Chinook database, and resides on the server project.
Figure 1: Data Model
With .NET RIA Services is installed, we get a new service called DomainService. We use this DomainService to expose data from our server project to the Silverlight client project. We can create the DomainService easily though a wizard shown in Figure 2:
Figure 2: Domain Service Wizard
Upon completion of the wizard, Visual Studio will add all the necessary references and configurations to our project and generate our domain service class and a metadata class. The domain service class contains auto generated CRUD LINQ queries for all our selected entities. We can modify or add our own LINQ queries in the domain service class. The metadata can also be modified to suit our entities. We can add validation rules or UI requirements. For the demo, I modified both our metadata and domain service classes, which will be shown in the next section. The Model, Domain Service, and Metadata reside in our server project.
The three list boxes are populated by RIA Services. Since the demo was developed in Visual Studio 2010, I thought to try the new support that Visual Studio provides for RIA Services, and so the data binding is implemented using Visual Studio 2010 supporting tools for RIA Services. Tim Heuer did a very good video tutorial on this. This method offers a fast way to bind our data whereby we are able to simply drag our data objects from the DataSource onto the Silverlight designer without writing much code. This perhaps is not the best approach for all applications, as we are tightly coupling the data source with our UI and seems almost magical, but was sufficient for the demo and offered a fast way to bind our data. The implementation of the three ListBoxe
s was inspired by the ContosoSales demo presented by Scott Hanselman at PDC 09. Jeff Handley wrote step by step instructions on how the ContosoSales demo was built, which can be found here.
For our demo, we only need one LINQ query, so we condense our Domain Service class to this one query as shown in the following excerpt:
namespace MediaStore.Web.Services
{
using System.Linq;
using System.Web.DomainServices.Providers;
using System.Web.Ria;
using MediaStore.Web.Models;
[EnableClientAccess()]
public class MediaStoreDomainService :
LinqToEntitiesDomainService<ChinookEntities>
{
public IQueryable<Artist> GetArtists()
{
return from artist in this.ObjectContext.Artists
.Include("Albums").Include("Albums.Tracks")
.Include("Albums.Tracks.Genre")
where artist.Albums.Count > 0
orderby artist.Name
select artist;
}
}
}
In our metadata class, I marked Track and Album Entity Collections as include fields, and left the auto generated properties untouched.
Having our data objects prepared, we can move onto our client project to bind the data. In our view, we use a simple grid where we position the three list boxes side by side, as you can see in Figure 3. In Figure 3, we can also see the Data Sources window with our MediaStoreDomainContext. The data object's default option is grid, which we can change to ListBox by selecting Customize from the drop down list and selecting ListBox. We can then drag the data objects one after another (Artist, Albums, and Tracks) onto our designer, leaving Visual Studio doing all the work.
Figure 3: Data Sources Window
Because our ListBox
es have a set width, we want to show the item's text in a tooltip. The following excerpt shows how this is done:
<ListBox ItemsSource="{Binding ElementName=artistDomainDataSource, Path=Data}"
Name="artistListBox" Margin="10,0,10,0" Grid.Row="1" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ToolTipService.ToolTip>
<ToolTip HorizontalOffset="0" VerticalOffset="0"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</ToolTip>
</ToolTipService.ToolTip>
<TextBlock Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What is left to do is add the new BusyIndicator
control from the Silverlight 4 Toolkit. This progress indicator can be customized, but for the demo, we chose to leave the default settings.
<controlsToolkit:BusyIndicator Grid.Row="1" Width="154" Height="55"
Name="busyIndicator1" Margin="98,161,128,284" Foreground="Black"
IsBusy="{Binding ElementName=artistDomainDataSource, Path=DomainContext.IsLoading}"
Grid.Column="1" Grid.ColumnSpan="2" />
The Silverlight Business Application template comes with predefined layout styles in a file called Styles.xaml, which is located in the Silverlight project under Assets. This file is well documented and can be easily customized. There are additional application layout styles available for download here. The demo application is styled using a combination of the styles defined in the Styles.xaml file and the Expression Dark Toolkit theme. Applying the Expression Dark Toolkit theme is easy. We can simply drag the theme from the toolbox onto our design, and Visual Studio will automatically add all the necessary references. We can then position our ExpressionDarkTheme opening and closing elements around our content. I modified the Styles.xaml file to suite the Expression Dark Theme, mainly changing colours, and I also added my own style declarations.
Although I haven't utilized this in the demo, I feel it is worth mentioning that in Silverlight 4, we can now use implicit styles. This means that a style can be declared for a particular target element such as a button or a textbox, which will implicitly apply to all buttons and textboxes unless we explicitly override their style. For explicit style, we would typically specify our style declaration, as the following excerpt shows, with a unique name for the Key
attribute and a TargetType
:
<!---->
<Style x:Key="TrackDetailsTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground"
Value="{StaticResource BodyTextColorBrush}"/>
<Setter Property="TextAlignment" Value="Left"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
In our view, we would then use the style like this:
<TextBlock Grid.Row="2" Grid.Column="0" Text="Album:"
Style="{StaticResource TrackDetailsTextStyle}"/>
In the above style declaration example, if we were to remove the Key
attribute, we would implicitly set the style to all TextBlock
elements within our application, and would not need to explicitly set the style attribute in our view.
With Silverlight 4, we are able to handle right click events and display a custom context menu by attaching the MouseRightButtonUp
and MouseRightButtonDown
events to any UIElement
and wiring up our handlers. In the demo, we use this functionality on the Track listbox to give user the option to view details about a particular track. See Figure 4.
Figure 4: Right Click Menu
The right click functionality in the demo is implemented in the way described in this video tutorial by Tim Heuer, using Dave Relyea's Dialog
class for the context menu. First, we attach MouseRightButtonUp
and MouseRightButtonDown
to the tracklistBox
control and wire up our handlers like this:
trackListBox.MouseRightButtonDown += new MouseButtonEventHandler(
TrackListBoxRightMouseButtonDownHandler);
trackListBox.MouseRightButtonUp += new MouseButtonEventHandler(
TrackListBoxRightMouseButtonUpHandler);
In TrackListBoxRightButtonDownHandler, we set the MouseButtonEventArgs.Handled
property to true
to eliminate the default Silverlight options menu popping up.
static void TrackListBoxRightMouseButtonDownHandler(
object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
In our TrackListBoxRightButtonUpHandler
, we want to determine the track on which the right mouse button event occurred, and we want to display our context menu, passing it information about the track. The following excerpt shows how this is done:
void TrackListBoxRightMouseButtonUpHandler(object sender, MouseButtonEventArgs e)
{
var element = e.OriginalSource as FrameworkElement;
if (element == null)
{
return;
}
var track = element.DataContext as Track;
if (track == null)
{
return;
}
trackListBox.SelectedItem = track;
TrackDetails trackDetails = new TrackDetails();
trackDetails.Album = track.Album;
trackDetails.Artist = track.Album.Artist;
trackDetails.Track = track;
trackDetails.Genre = track.Genre;
TrackContextMenu contextMenu = new TrackContextMenu(trackDetails);
contextMenu.TrackDetails = trackDetailsView;
contextMenu.Show(e.GetPosition(null));
}
TrackContextMenu
is a simple control based on the Dialog
class.
One of the new features introduced in Silverlight 4 Beta is the ability to host HTML content in a Silverlight application. It can be achieved either by using the WebBrowser
control or HtmlBrush
. Unfortunately, at this point in time, this great feature is supported only for Out of Browser applications. I wanted my demo to be an in-browser application, so I searched for other solutions and found the Divelements HtmlHost
control. The control is free, and can be downloaded as part of Silverlight Tools 1.0.1 from the Divelements website. Using the HtmlHost
control is simple. The following excerpt shows you how we can embed HTML content within a border:
<Border Style="{StaticResource HtmlHostBorderStyle}" Grid.Column="3" Grid.Row="1">
-->
<divtools:HtmlHost x:Name="htmlHost" />
</Border>
The HTML content can be defined in the code-behind like this:
htmlHost.SourceHtml = "<p>Select a Track to search for a YouTube video.</p>"
+ "<div id='videosearch'></div>";
The important thing to remember when using the HtmlHost
control is that we need to set our Silverlight plug-in to be in windowless mode. We do this on our default page, like this:
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object id="xaml" data="data:application/x-silverlight-2,"
type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/MediaStore.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<%
string currentCulture =
System.Threading.Thread.CurrentThread.CurrentCulture.ToString();
Response.Cookies.Add(new HttpCookie("MediaStore-culture", currentCulture));
%>
<param name="uiculture"
value="<%= System.Threading.Thread.CurrentThread.CurrentCulture %>" />
<param name="windowless" value="true" /> -->
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0"
style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181"
alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>
<iframe id="_sl_historyFrame"
style="visibility:hidden;height:0px;width:0px;border:0px">
</iframe>
</div>
</form>
The demo uses the Google AJAX Search API to search for YouTube videos. You can find more information about it here. Adding the search control requires a few simple steps. The first includes obtaining a Google AJAX Search API key. For the purpose of this demo, I have used a key with a value of 'internal-solution', but if you were to use this search control on a website, make sure you sign up for your own key here. Detailed instructions on how to add the Google search control to your website can be found here. In order to use the search control, we need to include links to the AJAX Search API, the video search script, and style sheets within the head of our default page, like this:
<head id="Head1" runat="server">
<!---->
<script src="http://www.google.com/uds/api?file=uds.js&v=1.0&key=internal-solution"
type="text/javascript"></script>
<script src="http://www.google.com/uds/solutions/videosearch/gsvideosearch.js"
type="text/javascript"></script>
-->
<link href="http://www.google.com/uds/css/gsearch.css" rel="stylesheet"
type="text/css"/>
<link href="http://www.google.com/uds/solutions/videosearch/gsvideosearch.css"
rel="stylesheet" type="text/css"/>
.
.
.
</head>
The AJAX search control is placed within a <div>
element which we position on our Silverlight home page in the code-behind through the HtmlHost
control, like this:
htmlHost.SourceHtml = "<p>Select a Track to search for a YouTube " +
"video.</p><div id='videosearch'></div>";
Next, we need to include a JavaScript function to create and configure the video search control. We include this function in our default page, as the following excerpt shows:
<script type="text/javascript">
var options = { string_allDone: "Close Video" };
var videoSearchControl;
function CreateVideoSearchControl() {
var defaultTags = [
{ query: "" },
{ query: "" },
{ query: "" },
{ query: "" }
];
videoSearchControl = new GSvideoSearchControl(
document.getElementById("videosearch"), defaultTags, null,
null,
options );
}
</script>
Note the array defaultTags
in the above code. This array represents an initial set of search tags, and is required for the GSvideoSearchControl
. For the purpose of this demo, we leave the search expressions blank.
Now, we need to be able to supply our own search string and initiate the search. The following excerpt shows how we do this.
<script type="text/javascript">
function SearchForClip(searchString) {
CreateVideoSearchControl();
videoSearchControl.execute(searchString);
}
</script>
Finally, we set some styling attributes to the videosearch div element to restrict the width and set other attributes to suite our design. We can also customize the look of the search control by overriding the video search control styles. The styling attributes are defined within the style element on our default page.
The search is initiated on the left mouse click on a track. The following shows how we can call the JavaScript function, passing it a search string from our Silverlight application.
HtmlPage.Window.Invoke("SearchForClip", searchString);
Although I would like to think that my first Silverlight 4 application is perfect, it does have some small imperfections, some of which are related to Silverlight 4.0 beta issues. The following lists some of the known imperfections:
- Mouse wheel scrolling on list boxes doesn't seem to work in Firefox.
- When searching for a YouTube video, if the search returns no results, the user is not notified.
- User dialog box is intentionally placed across the three list boxes because the YouTube HTML content would otherwise leap over.
- Clicking on the YouTube video takes the user away from the application and redirects to the YouTube website.
- Tooltips on
ListBoxItem
s get misplaced in IE.
- The Expression Dark Theme
ListboxItem
selected item's style makes text nearly illegible.
- Sometimes when I build the project, I get this error: 'The type 'MediaStore.Web.User' already contains a definition for 'DisplayName'. It seems to be a bug with the Business Application Template. To resolve it, simply Clean the solution.
The intention of this project was to learn some of the new Silverlight 4 features by building a real application. We learned about right click handling, Visual Studio 2010 support for RIA Services, and implicit styles. We also learned how to embed YouTube videos in our Silverlight application. I hope you found my efforts useful. If you enjoyed my article, please rate it and maybe share your thoughts below.
- February 2010: Initial release.
- April 2010: Support for Silverlight 4 RTW , and WCF RIA Services RC 2