`
lilisalo
  • 浏览: 1110197 次
文章分类
社区版块
存档分类
最新评论

Silverlight实例教程 - Out of Browser音乐播放器

 
阅读更多

上一篇,我们了解了如何在Silverlight的Out of Browser模式下进行Debug调试,另外学习Silverlight OOB应用的一个新特性Notifications窗口。本篇,我们将结合以往的Out of Browser特性,创建一款新的Out of Browser实例, 音乐播放器。 该实例目的比较简单,实现音乐播放,实现音乐文件列表读取,实现音乐文件信息读取,另外音乐播放自动跳转等功能。

在实例开始前,我们仍旧需要了解一些基础知识。Silverlight对音频的支持是使用MediaElement类,该类使用方法非常简单,该类的详细解释,请看MSDN

1<MediaElement
2x:Name="media"
3Source="xbox.wmv"
4CurrentStateChanged="media_state_changed"
5Width="300"Height="300"/>

在了解了音频播放类的简单使用后,让我们先看看项目完成后的效果图,

从上面效果图中可以看出整个实例项目UI分5个部分,

1. 音频控制部分,这部分是实例主要功能;

2. 音频文件信息部分,这部分是获取显示当前和下一首音乐文件信息;

3. 唱片图片信息,其实这部分也是属于音频文件信息,不过这里单独列出来,使用独立的类进行处理;

4. 音频文件列表,该列表是载入My Music目录中的音乐文件,并支持用户选择播放功能;

5. UI控制,该部分可以使播放器进入最小化状态。例如:

下面我们开始分别解释以上几个部分的实例设计方法。

我们仍旧使用SilverlightOOBDemo项目,不过为了使代码更清晰易读,这次不再使用OutofBrowserMainPage作为OOB应用主界面,我们重新创建一个新的OOB应用界面OutofBrowserMusicPlayer。

为了修改启动页面为OutofBrowserMusicPlayer,为此,我们需要修改App.xaml中的启动页面代码:

1privatevoidApplication_Startup(objectsender,StartupEventArgse)
2{
3if(!Application.Current.IsRunningOutOfBrowser)
4{
5this.RootVisual=newMainPage();
6}
7else
8{
9//this.RootVisual=newOutofBrowserMainPage();
10this.RootVisual=newOutofBrowserMusicPlayer();
11}
12
13}

根据实例需求,我们最主要的功能就是播放音乐,所以,我们第一步首先实现Out of Browser应用音频控制。

1. 创建自定义音频控制控件;

对于音频控制,这里我们使用了自定义控件控制音乐的播放。AudioControl.xaml控件,

这里我仅贴上部分代码,大家可以在文章最后下载完整源代码。

1<Gridx:Name="LayoutRoot">
2<Grid.ColumnDefinitions>
3<ColumnDefinitionWidth="Auto"/>
4<ColumnDefinitionWidth="*"/>
5<ColumnDefinitionWidth="25"/>
6<ColumnDefinitionWidth="Auto"/>
7<ColumnDefinitionWidth="Auto"/>
8</Grid.ColumnDefinitions>
9<GridGrid.Column="0"Margin="0,0,0,0"HorizontalAlignment="Left"VerticalAlignment="Center"x:Name="gridCol1">
10<ToggleButtonCursor="Hand"Margin="0,0,0,0"x:Name="btnPlay"RenderTransformOrigin="0.5,0.5"Template="{StaticResourceplayControlTemplate}">
11<ToggleButton.RenderTransform>
12<TransformGroup>
13<ScaleTransformScaleX="1"ScaleY="1"/>
14<SkewTransform/>
15<RotateTransform/>
16<TranslateTransform/>
17</TransformGroup>
18</ToggleButton.RenderTransform>
19</ToggleButton>
20</Grid>
21<GridGrid.Column="1"Margin="0,0,0,0"HorizontalAlignment="Stretch"x:Name="gridCol2"VerticalAlignment="Center">
22<Grid.ColumnDefinitions>
23<ColumnDefinitionWidth="*"/>
24<ColumnDefinitionWidth="40"/>
25<ColumnDefinitionWidth="10"/>
26<ColumnDefinitionWidth="40"/>
27</Grid.ColumnDefinitions>
28<TextBlockx:Name="tbCurrentTime"Margin="0,1.5,0,0"Height="12"FontFamily="Verdana"FontSize="10"Text="00:00"TextWrapping="Wrap"Foreground="#FFFFFFFF"FontStyle="Normal"HorizontalAlignment="Right"TextAlignment="Right"Grid.Column="1"/>
29<TextBlockMargin="0,1.5,0,0"Height="12"FontFamily="Verdana"FontSize="10"Text="/"TextWrapping="Wrap"Foreground="#FFFFFFFF"FontStyle="Normal"HorizontalAlignment="Center"TextAlignment="Right"Grid.Column="2"/>
30<TextBlockx:Name="tbTotalTime"Margin="0,1.5,0,0"Height="12"FontFamily="Verdana"FontSize="10"Text="00:00"TextWrapping="Wrap"Foreground="#FFFFFFFF"FontStyle="Normal"HorizontalAlignment="Left"TextAlignment="Right"Grid.Column="3"/>
31<local:MediaSliderMargin="0,1.5,0,0"HorizontalAlignment="Stretch"Maximum="100"x:Name="sliderTimeline"Style="{StaticResourceprogressSliderStyle}"Grid.Column="0"Value="0"Visibility="Visible"/>
32</Grid>
33<GridGrid.Column="2"Margin="4,0,4,0"HorizontalAlignment="Stretch"x:Name="gridCol3"VerticalAlignment="Center">
34<local:SpinnerMargin="0,0,0,0"x:Name="spinner"Width="17"Height="17"HorizontalAlignment="Center"VerticalAlignment="Center"/>
35</Grid>
36<GridGrid.Column="3"Margin="0,10.30,0,10.30"HorizontalAlignment="Stretch"x:Name="gridCol4"Width="70"VerticalAlignment="Stretch"d:LayoutOverrides="Height">
37<GridMargin="0,0,0,0"HorizontalAlignment="Right"VerticalAlignment="Center"Width="70">
38<Grid.ColumnDefinitions>
39<ColumnDefinitionWidth="Auto"/>
40<ColumnDefinitionWidth="*"/>
41</Grid.ColumnDefinitions>
42<ToggleButtonHorizontalAlignment="Left"IsChecked="True"Margin="0,0,0,0"x:Name="btnSpeaker"Template="{StaticResourcespeakerControlTemplate}"/>
43<SliderGrid.Column="1"HorizontalAlignment="Stretch"Margin="3,0,0,0"VerticalAlignment="Center"Maximum="1"x:Name="sliderVolume"Style="{StaticResourcevolumeSliderStyle}"Background="#FF777777"/>
44</Grid>
45</Grid>
46<GridGrid.Column="4"Margin="0,10.3120002746582,4,10.3120002746582"HorizontalAlignment="Right"x:Name="gridCol5"VerticalAlignment="Stretch"d:LayoutOverrides="Height">
47<ToggleButtonCursor="Hand"HorizontalAlignment="Left"Margin="0,0,0,0"x:Name="btnFullScreen"Template="{StaticResourcefullScreenControlTemplate}"/>
48</Grid>
49</Grid>

从以上代码可以看到,在AudioControl中有两个自定义控件local:MediaSliderlocal:Spinner

MediaSlider:

其功能是控制音乐播放进度,支持拖拽前进或者后退音乐播放进度。其代码如下:

1publicclassMediaSlider:Slider
2{
3publicThumbhorizontalThumb;
4privateFrameworkElementhorizontalLeftTrack;
5privateFrameworkElementhorizontalRightTrack;
6privatedoubleoldValue=0,newValue=0,prevNewValue=0;
7publiceventRoutedPropertyChangedEventHandler<double>MyValueChanged;
8publiceventRoutedPropertyChangedEventHandler<double>MyValueChangedInDrag;
9privateDispatcherTimerdragtimer=newDispatcherTimer();
10privatedoubledragTimeElapsed=0;
11privateconstshortDragWaitThreshold=200,DragWaitInterval=100;
12publicRectangleprogressRect=null;
13privatebooldragSeekJustFired=false;
14
15publicMediaSlider()
16{
17
18this.ValueChanged+=newRoutedPropertyChangedEventHandler<double>(CustomSlider_ValueChanged);
19dragtimer.Interval=newTimeSpan(0,0,0,0,DragWaitInterval);
20dragtimer.Tick+=newEventHandler(dragtimer_Tick);
21}
22
23voiddragtimer_Tick(objectsender,EventArgse)
24{
25dragTimeElapsed+=DragWaitInterval;
26
27if(dragTimeElapsed>=DragWaitThreshold)
28{
29RoutedPropertyChangedEventHandler<double>handler=MyValueChangedInDrag;
30
31if((handler!=null)&&(newValue!=prevNewValue))
32{
33handler(this,newRoutedPropertyChangedEventArgs<double>(oldValue,newValue));
34dragSeekJustFired=true;
35prevNewValue=newValue;
36}
37
38dragTimeElapsed=0;
39}
40}
41
42voidCustomSlider_ValueChanged(objectsender,RoutedPropertyChangedEventArgs<double>e)
43{
44oldValue=e.OldValue;
45newValue=e.NewValue;
46
47if(horizontalThumb.IsDragging)
48{
49dragTimeElapsed=0;
50dragtimer.Stop();
51dragtimer.Start();
52dragSeekJustFired=false;
53}
54}
55
56publicoverridevoidOnApplyTemplate()
57{
58base.OnApplyTemplate();
59
60horizontalThumb=GetTemplateChild("HorizontalThumb")asThumb;
61horizontalLeftTrack=GetTemplateChild("LeftTrack")asFrameworkElement;
62horizontalRightTrack=GetTemplateChild("RightTrack")asFrameworkElement;
63progressRect=GetTemplateChild("Progress")asRectangle;
64
65if(horizontalLeftTrack!=null)horizontalLeftTrack.MouseLeftButtonDown+=newMouseButtonEventHandler(OnMoveThumbToMouse);
66
67if(horizontalRightTrack!=null)horizontalRightTrack.MouseLeftButtonDown+=newMouseButtonEventHandler(OnMoveThumbToMouse);
68
69horizontalThumb.DragCompleted+=newDragCompletedEventHandler(DragCompleted);
70
71progressRect.Width=this.Width;
72}
73
74publicStoryboardProgressStoryboard{get{return(GetTemplateChild("ProgressStoryboard")asStoryboard);}}
75
76publicRectangleProgressBar{get{return(GetTemplateChild("Progress")asRectangle);}}
77
78protectedoverrideSizeArrangeOverride(SizefinalSize)
79{
80Sizes=base.ArrangeOverride(finalSize);
81
82if(double.IsNaN(horizontalThumb.Width)&&(horizontalThumb.ActualWidth!=0))
83{
84horizontalThumb.Width=horizontalThumb.ActualWidth;
85}
86
87if(double.IsNaN(horizontalThumb.Height)&&(horizontalThumb.ActualHeight!=0))
88{
89horizontalThumb.Height=horizontalThumb.ActualHeight;
90}
91
92if(double.IsNaN(horizontalThumb.Width))horizontalThumb.Width=horizontalThumb.Height;
93if(double.IsNaN(horizontalThumb.Height))horizontalThumb.Height=horizontalThumb.Width;
94
95return(s);
96}
97
98privatevoidOnMoveThumbToMouse(objectsender,MouseButtonEventArgse)
99{
100e.Handled=true;
101Pointp=e.GetPosition(this);
102
103if(this.Orientation==Orientation.Horizontal)
104{
105Value=(p.X-(horizontalThumb.ActualWidth/2))/(ActualWidth-horizontalThumb.ActualWidth)*Maximum;
106}
107
108RoutedPropertyChangedEventHandler<double>handler=MyValueChanged;
109
110if(handler!=null)
111{
112handler(this,newRoutedPropertyChangedEventArgs<double>(oldValue,Value));
113}
114}
115
116privatevoidDragCompleted(objectsender,DragCompletedEventArgse)
117{
118dragtimer.Stop();
119dragTimeElapsed=0;
120
121RoutedPropertyChangedEventHandler<double>handler=MyValueChanged;
122
123if((handler!=null)&&(!dragSeekJustFired))
124{
125handler(this,newRoutedPropertyChangedEventArgs<double>(oldValue,this.Value));
126}
127}
128}

而Spinner控件,是一个载入标识,当音频载入时,会显示该控件。该控件为Path绘制的控件,这里不再贴出代码描述。

2. 获取音频文件信息部分

该部分我们同样也创建一个自定义控件来实现,TrackInfo.xaml,主要是负责在客户端显示音频文件的信息,而Silverlight没有相关API可以实现读取音频文件的标签信息,这里,我们需要引入一个微软开源类库TagLib。该类库的主要功能就是读取和修改音乐文件的标签信息。

其调用方法非常简单:

1//获取标签
2tags=TagLib.File.Create(MediaFile.ID);
3//设置标签属性
4MediaFile.Artist=tags.Tag.FirstPerformer;
5MediaFile.Title=tags.Tag.Title;
6MediaFile.Album=tags.Tag.Album;
7MediaFile.Genre=tags.Tag.FirstGenre;

当音乐标签信息获取成功后,即可将信息绑定到TrackInfo.DataContext。

3. 唱片图片信息

对于唱片的图片信息,这里需要读取Image从本地目录,当没有唱片图片时,则显示默认Music.png图片。这里需要注意的是,读取本地文件,需要OOB应用权限信任。

1publicImageSourceAlbumArtStream
2{
3get
4{
5BitmapImageimage;
6
7if(string.IsNullOrEmpty(AlbumArtPath))
8{
9if(null==_default)
10{
11_default=newBitmapImage(newUri("../Images/Music.png",UriKind.Relative));
12}
13
14image=_default;
15}
16else
17{
18FileStreamstream=File.Open(AlbumArtPath,FileMode.Open,FileAccess.Read);
19
20image=newBitmapImage();
21image.SetSource(stream);
22stream.Close();
23}
24
25returnimage;
26}
27}

4. 获取音频文件列表

从演示图片可以看出,我们的音频文件列表,是用了一个绑定了音乐播放文件信息的Datagrid。

其代码非常简单,创建两列,分别绑定歌手和歌曲名:

1<data:DataGridx:Name="playList"
2Grid.Row="1"
3Grid.Column="1"
4Grid.RowSpan="3"
5VerticalAlignment="Top"
6Margin="4"
7Height="296"
8Style="{StaticResourceDataGridStyle}"
9AutoGenerateColumns="False"
10CanUserResizeColumns="True"
11CanUserSortColumns="False"
12SelectionChanged="playList_SelectionChanged">
13<data:DataGrid.Columns>
14<data:DataGridTextColumnHeader="歌手"
15Binding="{BindingArtist}"
16FontSize="12"/>
17<data:DataGridTextColumnHeader="歌名"
18Binding="{BindingTitle}"
19FontSize="12"
20Width="*"/>
21</data:DataGrid.Columns>
22</data:DataGrid>

而后台,在读取了My Music目录后,将数据集绑定到datagrid.ItemsSource就可以正常实现歌曲列表了。

1publicstaticList<MediaFile>GetMediaFiles()
2{
3List<MediaFile>files=null;;
4MediaFilemf;
5stringpath=Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
6IEnumerable<string>list=Directory.EnumerateFiles(path,"*.mp3",SearchOption.AllDirectories);
7TagLib.Filetags;
8files=GetCachedList(list);
9if(null==files||files.Count==0)
10{
11files=newList<MediaFile>();
12foreach(stringfileinlist)
13{
14mf=newMediaFile();
15mf.ID=file;
16mf.AlbumArtPath=GetAlbumArtPath(file);
17files.Add(mf);
18}
19
20for(intidx=0;idx<files.Count;idx++)
21{
22mf=files[idx];
23tags=TagLib.File.Create(mf.ID);
24mf.Artist=tags.Tag.FirstPerformer;
25mf.Title=tags.Tag.Title;
26mf.Album=tags.Tag.Album;
27mf.Genre=tags.Tag.FirstGenre;
28}
29SaveCachedList(files);
30}
31
32returnfiles;
33}

在绑定成功后,同时,我们支持用户选择指定音乐播放,使用Datagrid的SelectionChanged事件即可。

1privatevoidplayList_SelectionChanged(objectsender,SelectionChangedEventArgse)
2{
3DataGriddg=(senderasDataGrid);
4
5if(dg.SelectedIndex!=_nowPlaying)
6{
7if(dg.SelectedIndex!=0)
8{
9me.AutoPlay=true;
10}
11OpenAndPlay(dg.SelectedIndex);
12}
13
14}

5. UI控制

对于UI的控制,这里我们只是简单的实现了隐藏和显示音乐信息框的功能,其代码实现:

1privatevoidMinimize_Click(objectsender,MouseButtonEventArgse)
2{
3Windowmain=Application.Current.MainWindow;
4
5if(!_min)
6{
7main.Height=40;
8rot.Angle=0;
9}
10else
11{
12main.Height=340;
13rot.Angle=180;
14}
15
16_min=!_min;
17}

上面是OOB音乐播放器5个部分的核心功能代码,这里,我想同时将上一篇讲到的Notifications窗口应用到实例中,我们可以仍旧使用NotificationControl文件,在其中对播放音乐Title进行绑定,即当音乐播放完毕后,即弹出消息提示播放下一首“XXX”音乐。效果如下图:

根据上一篇介绍Notifications窗口的代码,我们简单进行修改,即可实现本篇实例需求:

1NotificationWindownotifyWindow=null;
2privatevoidShowToast()
3{
4notifyWindow=newNotificationWindow();
5
6if(notifyWindow.Visibility==Visibility.Visible)
7notifyWindow.Close();
8
9NotificationControlmyNotify=newNotificationControl();
10myNotify.DataContext=_playList[_nowPlaying];
11notifyWindow.Width=300;
12notifyWindow.Height=100;
13notifyWindow.Content=myNotify;
14notifyWindow.Show(10000);
15}

至此,一款基于Silverlight的Out of Browser模式的音乐播放器基本完成了。大家可以根据该实例添加更多自定义功能,例如添加互联网音乐播放功能,音乐搜索功能等,创建属于自己的Silverlight版酷我音乐盒。

本篇源代码下载

欢迎大家加入"专注Silverlight" 技术讨论群:

32679955(六群)
23413513(五群)
32679922(四群)
100844510(三群)
37891947(二群)
22308706(一群)
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics