Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Silverlight YouTube Jukebox

0.00/5 (No votes)
27 Apr 2010 4  
Search and play YouTube videos inside a Silverlight 4.0 application! Learn about RIA Services, right-click event handling, themes, and styles.

MediaStore

Contents

Introduction

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.

Background

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

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.

What You Will Need

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.

What Will be Covered

  • 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

Getting Started

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.

RIA Services and Visual Studio 2010 Support

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 ListBoxes 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;

    //Enabling the service to be accessed from clients. 
    [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 ListBoxes 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" />

Themes and Styles

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:

<!-- TrackDetails Text Style -->
<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.

Right-click Event Handling

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)
{
    //Stops the default Silverlight options menu  popping up.
    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)
{
    /*determine the element on which the right mouse button event */
    var element = e.OriginalSource as FrameworkElement;     
        if (element == null)
        {
            return; /* no element selected. */
        }
        /*Set the elements data context to Track*/
        var track = element.DataContext as Track; 
        if (track == null)
        {
            return; /* no track selected. */
        } 
        /*set the track to selected*/
        trackListBox.SelectedItem = track;     
        TrackDetails trackDetails = new TrackDetails(); 
        /*Set TrackDetails properties*/
        trackDetails.Album = track.Album;
        trackDetails.Artist = track.Album.Artist;
        trackDetails.Track = track;
        trackDetails.Genre = track.Genre;

        /*create the context menu control*/
        TrackContextMenu contextMenu = new TrackContextMenu(trackDetails);
        contextMenu.TrackDetails = trackDetailsView;
        contextMenu.Show(e.GetPosition(null));
}

TrackContextMenu is a simple control based on the Dialog class.

HTML Content Hosting

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">
      <!-- Embedding Html Content-->
       <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" /> <!--Set windowless mode-->
      <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>

YouTube Video Search

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">
    <!-- ajax search api and video search control code -->
    <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>

    <!-- ajax search stylesheet, video search stylesheet -->
    <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">
       /**
    * Google AJAX VideoSearch Control 
    */    
    
    //customize "all done" string
    var options = {  string_allDone: "Close Video" };
    var videoSearchControl;

    /**
    * Create a new GSvideoSearchControl.
    */
    function CreateVideoSearchControl() {
        // establish default search tags
            var defaultTags = [
        { query: "" },
        { query: "" },
        { query: "" },
        { query: "" }
      ];

            videoSearchControl = new GSvideoSearchControl(
        document.getElementById("videosearch"),         // container
        defaultTags,                                // default tag array
        null,
        null,
        options         //customizable options tag 
        );
    }
</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">
       /**
    * Initiate a new search by calling the videosearch control.
    */
    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);

Known Imperfections

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 ListBoxItems 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.

Conclusion

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.

History

  • February 2010: Initial release.
  • April 2010: Support for Silverlight 4 RTW , and WCF RIA Services RC 2

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here