65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight YouTube Jukebox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (69投票s)

2010年2月21日

CPOL

12分钟阅读

viewsIcon

234472

downloadIcon

3203

在 Silverlight 4.0 应用程序中搜索和播放 YouTube 视频!了解 RIA Services、右键单击事件处理、主题和样式。

MediaStore

目录

引言

在听我的丈夫说了我“亲爱的,写篇文章吧”一段时间后,我终于决定尝试一下。我开始研究 Silverlight(读了几本书、许多文章,观看了大量视频),并对这项技术感到非常兴奋,尤其是其开发业务应用程序的能力。因此,我决定通过构建一个实际项目来学习 Silverlight,并认为如果我分享我的经验,其他人可能会受益。结果,该项目演变成了一个很酷的应用程序,展示了一些新的 Silverlight 4 功能,例如 RIA services、右键单击事件处理,以及在 Silverlight 应用程序中嵌入和播放 YouTube 视频。这是我的首次文章写作尝试,请大家温柔对待。

背景

Silverlight 是一项令人兴奋的新技术,它正在不断发展和变得越来越好。此演示是用 Silverlight 4 Beta 构建的,这是最新可用的开发人员版本。Silverlight 当前的正式版本是 3,于 2009 年 7 月发布。Silverlight 3 带来了许多新功能和一些重大改进,例如脱机运行支持或增强的数据支持,如元素到元素的绑定。Silverlight 4 Beta 提供了进一步的改进和许多令人兴奋的新功能,例如麦克风支持、右键单击处理和打印。学习 Silverlight 的一个好地方是 silvelright.net 网站,您可以在那里找到教程、视频等。

演示应用程序

演示应用程序是一个 Silverlight YouTube Jukebox,允许用户选择艺术家、查看他们的专辑、选择专辑并查看专辑的曲目。用户可以右键单击任何曲目名称并选择查看曲目详细信息。通过选择一个曲目(左键单击),用户会启动一个 YouTube 视频搜索。最多会向用户显示四个可能的匹配项作为缩略图;当用户将鼠标悬停在图像上时,会出现视频标题。然后,用户可以单击任何图像来观看 YouTube 视频。

您将需要

演示应用程序是使用 Visual Studio Beta 2 和 Silverlight 4 Beta 开发的。您将需要以下内容

请注意,Visual Studio 2010 Beta 2 可能无法正常使用 Silverlight 4。在开发此应用程序的过程中,我遇到了许多崩溃。Visual Studio 2010 和 .NET 4 发布候选版本现已可用;但是,在撰写本文时,Silverlight 4 项目不受支持,因此我没有升级到发布候选版本。

将涵盖的内容

  • RIA Services 和 Visual Studio 2010 支持
  • 主题和样式
  • 右键单击事件处理
  • 在 Silverlight 浏览器内应用程序中托管 HTML 内容
  • 嵌入 YouTube 视频搜索控件

入门

该演示基于 Silverlight Business Application 模板,这是一个在我们安装 .NET RIA Services 时获得的新模板,它带有样式视图、页面导航以及本地化、身份验证和注册支持。正如 Brad Abrams 在此处所解释的,模板解决方案设置遵循“RIA Application”模式,其中两个层(项目),Silverlight 客户端和服务器层以一种方式连接,即一个层的任何更改都会反映在另一个层中。对于演示目的,我修改了此模板的一些部分,例如样式,并且我还解除了所有不必要的视图的连接。该演示使用了 Chinook 数据库,它代表了一个数字媒体商店,是 Northwind 数据库的一个不错的替代方案。Chinook 数据库可以在此处下载。该演示使用 Entity Framework 进行数据访问。我们的数据模型如图 1 所示,它来自 Chinook 数据库,并驻留在服务器项目中。

图 1:数据模型

安装 .NET RIA Services 后,我们会获得一个名为 DomainService 的新服务。我们使用此 DomainService 将服务器项目中的数据公开给 Silverlight 客户端项目。我们可以通过图 2 所示的向导轻松创建 DomainService

图 2:Domain Service 向导

完成向导后,Visual Studio 会将所有必要的引用和配置添加到我们的项目中,并生成我们的域服务类和元数据类。域服务类包含我们所有选定实体自动生成的 CRUD LINQ 查询。我们可以在域服务类中修改或添加我们自己的 LINQ 查询。元数据也可以修改以适应我们的实体。我们可以添加验证规则或 UI 要求。对于演示,我修改了我们的元数据和域服务类,这将在下一节中显示。模型、域服务和元数据驻留在我们的服务器项目中。

RIA Services 和 Visual Studio 2010 支持

三个列表框由 RIA Services 填充。由于该演示是在 Visual Studio 2010 中开发的,我认为尝试 Visual Studio 对 RIA Services 的新支持,因此数据绑定是使用 Visual Studio 2010 对 RIA Services 的支持工具实现的。Tim Heuer 制作了一个非常好的视频教程。这种方法提供了一种快速绑定数据的方式,我们可以直接将数据对象从 DataSource 拖到 Silverlight 设计器上,而无需编写太多代码。这可能不是所有应用程序的最佳方法,因为我们将数据源与我们的 UI 紧密耦合,并且看起来几乎是神奇的,但对于演示来说已足够,并且提供了一种快速绑定数据的方法。三个ListBox的实现灵感来自 Scott Hanselman 在 PDC 09 上演示的 ContosoSales 演示。Jeff Handley 撰写了关于 ContosoSales 演示如何构建的分步说明,可以在此处找到。

对于我们的演示,我们只需要一个 LINQ 查询,因此我们将 Domain Service 类精简为这一个查询,如下面的摘录所示

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

在我们的元数据类中,我将 Track 和 Album Entity Collections 标记为包含字段,并保留了自动生成的属性不变。

准备好我们的数据对象后,我们就可以转到客户端项目来绑定数据了。在我们的视图中,我们使用一个简单的网格,将三个列表框并排放置,如图 3 所示。在图 3 中,我们还可以看到带有 MediaStoreDomainContext 的 Data Sources 窗口。数据对象的默认选项是网格,我们可以通过从下拉列表中选择 Customize 并选择 ListBox 来将其更改为 ListBox。然后,我们可以将数据对象一个接一个地(Artist、Albums 和 Tracks)拖到我们的设计器上,让 Visual Studio 完成所有工作。

图 3:Data Sources 窗口

因为我们的ListBox有固定的宽度,所以我们希望在工具提示中显示项目的文本。下面的摘录显示了如何做到这一点

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

剩下要做的就是添加 Silverlight 4 Toolkit 中的新BusyIndicator控件。此进度指示器可以自定义,但对于演示,我们选择保留默认设置。

<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" />

主题和样式

Silverlight Business Application 模板在名为 _Styles.xaml_ 的文件中包含预定义的布局样式,该文件位于 Silverlight 项目的 Assets 下。此文件记录良好,可以轻松自定义。可以在此处下载其他应用程序布局样式。演示应用程序使用 _Styles.xaml_ 文件中定义的样式和 Expression Dark Toolkit 主题的组合进行样式设置。应用 Expression Dark Toolkit 主题很容易。我们可以简单地将主题从工具箱拖到我们的设计器上,Visual Studio 将自动添加所有必要的引用。然后,我们可以将 ExpressionDarkTheme 的开始和结束元素放置在我们的内容周围。我修改了 _Styles.xaml_ 文件以适应 Expression Dark Theme,主要是更改了颜色,并且还添加了我自己的样式声明。

虽然我还没有在演示中使用这个功能,但我认为提及 Silverlight 4 中现在可以使用隐式样式是值得的。这意味着可以为特定目标元素(如按钮或文本框)声明样式,该样式将隐式应用于所有按钮和文本框,除非我们明确覆盖它们的样式。对于显式样式,我们通常会指定我们的样式声明,如下面的摘录所示,具有唯一的 Key 属性值和 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>

在我们的视图中,我们将像这样使用样式

<TextBlock Grid.Row="2" Grid.Column="0" Text="Album:" 
    Style="{StaticResource TrackDetailsTextStyle}"/>

在上面的样式声明示例中,如果我们删除 Key 属性,我们将隐式地将样式设置到我们应用程序中的所有 TextBlock 元素,而无需在我们的视图中显式设置样式属性。

右键单击事件处理

使用 Silverlight 4,我们可以通过将 MouseRightButtonUpMouseRightButtonDown 事件附加到任何 UIElement 并连接我们的处理程序来处理右键单击事件并显示自定义上下文菜单。在演示中,我们在 Track 列表框上使用此功能,为用户提供查看特定曲目详细信息的选项。请参阅图 4。

图 4:右键单击菜单

演示中的右键单击功能是通过 Tim Heuer 在视频教程中所述的方式实现的,并使用 Dave Relyea 的 Dialog 类作为上下文菜单。首先,我们将 MouseRightButtonUpMouseRightButtonDown 附加到 tracklistBox 控件并像这样连接我们的处理程序

trackListBox.MouseRightButtonDown += new MouseButtonEventHandler(
    TrackListBoxRightMouseButtonDownHandler);
trackListBox.MouseRightButtonUp += new MouseButtonEventHandler(
    TrackListBoxRightMouseButtonUpHandler);

在 TrackListBoxRightButtonDownHandler 中,我们将 MouseButtonEventArgs.Handled 属性设置为 true,以消除默认的 Silverlight 选项菜单弹出。

static void  TrackListBoxRightMouseButtonDownHandler(
             object sender, MouseButtonEventArgs e)
{
    //Stops the default Silverlight options menu  popping up.
    e.Handled = true;
}

在我们的 TrackListBoxRightButtonUpHandler 中,我们想确定发生右键鼠标按钮事件的曲目,并且我们想显示我们的上下文菜单,并将曲目信息传递给它。下面的摘录显示了如何做到这一点

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 是一个基于 Dialog 类的简单控件。

HTML 内容托管

Silverlight 4 Beta 引入的新功能之一是在 Silverlight 应用程序中托管 HTML 内容的能力。这可以通过使用 WebBrowser 控件或 HtmlBrush 来实现。不幸的是,目前,这项很棒的功能仅支持脱机应用程序。我希望我的演示是一个浏览器内应用程序,所以我寻找其他解决方案,并找到了 Divelements HtmlHost 控件。该控件是免费的,可以作为 Silverlight Tools 1.0.1 的一部分从 Divelements 网站下载。使用 HtmlHost 控件很简单。下面的摘录显示了我们如何在边框内嵌入 HTML 内容

<Border Style="{StaticResource HtmlHostBorderStyle}"  Grid.Column="3" Grid.Row="1">
      <!-- Embedding Html Content-->
       <divtools:HtmlHost  x:Name="htmlHost" />
</Border>

HTML 内容可以在代码隐藏中定义如下

htmlHost.SourceHtml = "<p>Select a Track to search for a YouTube video.</p>" 
  + "<div id='videosearch'></div>";

使用 HtmlHost 控件时要记住的重要一点是,我们需要将 Silverlight 插件设置为无窗口模式。我们在默认页面上执行此操作,如下所示

<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 视频搜索

该演示使用 Google AJAX Search API 搜索 YouTube 视频。您可以在此处找到有关它的更多信息。添加搜索控件需要几个简单的步骤。第一个步骤是获取 Google AJAX Search API 密钥。出于此演示的目的,我使用了一个值为 'internal-solution' 的密钥,但如果您要在网站上使用此搜索控件,请确保您在此处注册您自己的密钥。有关如何将 Google 搜索控件添加到您网站的详细说明可以在此处找到。为了使用搜索控件,我们需要在我们的默认页面的 head 中包含 AJAX Search API、视频搜索脚本和样式表的链接,如下所示

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

AJAX 搜索控件放置在一个 <div> 元素中,我们通过代码隐藏中的 HtmlHost 控件将其放置在我们的 Silverlight 主页上,如下所示

htmlHost.SourceHtml = "<p>Select a Track to search for a YouTube " + 
                      "video.</p><div id='videosearch'></div>";

接下来,我们需要包含一个 JavaScript 函数来创建和配置视频搜索控件。我们在默认页面中包含此函数,如下面的摘录所示

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

注意上面代码中的 defaultTags 数组。此数组代表初始搜索标签集,并且是 GSvideoSearchControl 所必需的。出于此演示的目的,我们将搜索表达式留空。

现在,我们需要能够提供自己的搜索字符串并启动搜索。下面的摘录显示了我们如何做到这一点。

<script type="text/javascript">
       /**
    * Initiate a new search by calling the videosearch control.
    */
    function SearchForClip(searchString) {
        CreateVideoSearchControl();
        videoSearchControl.execute(searchString);
    }
</script>

最后,我们为 videosearch div 元素设置一些样式属性,以限制宽度并根据我们的设计设置其他属性。我们还可以通过覆盖视频搜索控件样式来定制搜索控件的外观。样式属性在我们的默认页面上的 style 元素中定义。

搜索在单击曲目上的鼠标左键时启动。下面的内容显示了我们如何调用 JavaScript 函数,从 Silverlight 应用程序中向其传递搜索字符串。

HtmlPage.Window.Invoke("SearchForClip", searchString);

已知不足

虽然我想认为我的第一个 Silverlight 4 应用程序是完美的,但它确实存在一些小问题,其中一些与 Silverlight 4.0 Beta 问题有关。以下列表包含一些已知的问题

  • 在 Firefox 中,列表框上的鼠标滚轮滚动似乎不起作用。
  • 搜索 YouTube 视频时,如果搜索没有返回任何结果,则不会通知用户。
  • 用户对话框故意放置在三个列表框的上方,因为否则 YouTube HTML 内容会跳出来。
  • 单击 YouTube 视频会将用户带离应用程序并重定向到 YouTube 网站。
  • IE 中 ListBoxItem 上的工具提示会错位。
  • Expression Dark Theme ListboxItem 选定项的样式使文本几乎无法辨认。
  • 有时当我构建项目时,我会收到此错误:“类型 'MediaStore.Web.User' 已包含 'DisplayName' 的定义。这似乎是 Business Application Template 的一个 bug。要解决它,只需清理解决方案。

结论

此项目的目的是通过构建一个实际应用程序来学习一些新的 Silverlight 4 功能。我们了解了右键单击处理、Visual Studio 2010 对 RIA Services 的支持以及隐式样式。我们还学习了如何在 Silverlight 应用程序中嵌入 YouTube 视频。我希望我的努力对您有所帮助。如果您喜欢我的文章,请给它评分,也许在下面分享您的想法。

历史

  • 2010年2月:初始发布。
  • 2010年4月:支持 Silverlight 4 RTW 和 WCF RIA Services RC 2
© . All rights reserved.