你的分享就是我们的动力 ---﹥

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

时间:2011-10-08 09:55来源:www.chengxuyuans.com 点击:

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

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

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

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

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

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

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

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

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

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

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

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

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

1 private void Application_Startup(object sender, StartupEventArgs e)
2 {
3             if (!Application.Current.IsRunningOutOfBrowser)
4             {
5                 this.RootVisual = new MainPage();
6             }
7             else
8             {
9                 //this.RootVisual = new OutofBrowserMainPage ();
10                  this.RootVisual = new OutofBrowserMusicPlayer ();
11             }
12
13 }

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

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

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

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

1 <Grid x:Name="LayoutRoot">
2         <Grid.ColumnDefinitions>
3             <ColumnDefinition Width="Auto" />
4             <ColumnDefinition Width="*" />
5             <ColumnDefinition Width="25" />
6             <ColumnDefinition Width="Auto" />
7             <ColumnDefinition Width="Auto" />
8         </Grid.ColumnDefinitions>
9         <Grid Grid.Column="0" Margin="0,0,0,0"  HorizontalAlignment="Left" VerticalAlignment="Center" x:Name="gridCol1">
10             <ToggleButton Cursor="Hand" Margin="0,0,0,0"  x:Name="btnPlay" RenderTransformOrigin="0.5,0.5" Template="{StaticResource  playControlTemplate}">
11                 <ToggleButton.RenderTransform>
12                     <TransformGroup>
13                         <ScaleTransform ScaleX="1"  ScaleY="1"/>
14                         <SkewTransform/>
15                         <RotateTransform/>
16                         <TranslateTransform/>
17                     </TransformGroup>
18                 </ToggleButton.RenderTransform>
19             </ToggleButton>
20         </Grid>
21         <Grid Grid.Column="1" Margin="0,0,0,0"  HorizontalAlignment="Stretch" x:Name="gridCol2" VerticalAlignment="Center">
22             <Grid.ColumnDefinitions>
23                 <ColumnDefinition Width="*" />
24                 <ColumnDefinition Width="40" />
25                 <ColumnDefinition Width="10" />
26                 <ColumnDefinition Width="40" />
27             </Grid.ColumnDefinitions>
28             <TextBlock x: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             <TextBlock Margin="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             <TextBlock x: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:MediaSlider Margin="0,1.5,0,0"  HorizontalAlignment="Stretch" Maximum="100" x:Name="sliderTimeline"  Style="{StaticResource progressSliderStyle}" Grid.Column="0" Value="0"  Visibility="Visible"/>
32         </Grid>
33         <Grid Grid.Column="2" Margin="4,0,4,0"  HorizontalAlignment="Stretch" x:Name="gridCol3" VerticalAlignment="Center">
34             <local:Spinner Margin="0,0,0,0" x:Name="spinner"  Width="17" Height="17" HorizontalAlignment="Center" VerticalAlignment="Center"/>
35         </Grid>
36         <Grid Grid.Column="3" Margin="0,10.30,0,10.30"  HorizontalAlignment="Stretch" x:Name="gridCol4" Width="70" VerticalAlignment="Stretch"  d:LayoutOverrides="Height">
37             <Grid Margin="0,0,0,0" HorizontalAlignment="Right"  VerticalAlignment="Center" Width="70">
38                 <Grid.ColumnDefinitions>
39                     <ColumnDefinition Width="Auto" />
40                     <ColumnDefinition Width="*" />
41                 </Grid.ColumnDefinitions>
42                 <ToggleButton HorizontalAlignment="Left"  IsChecked="True" Margin="0,0,0,0" x:Name="btnSpeaker" Template="{StaticResource  speakerControlTemplate}"/>
43                 <Slider Grid.Column="1"  HorizontalAlignment="Stretch" Margin="3,0,0,0" VerticalAlignment="Center" Maximum="1"  x:Name="sliderVolume" Style="{StaticResource volumeSliderStyle}"  Background="#FF777777"/>
44             </Grid>
45         </Grid>
46         <Grid Grid.Column="4"  Margin="0,10.3120002746582,4,10.3120002746582" HorizontalAlignment="Right"  x:Name="gridCol5" VerticalAlignment="Stretch" d:LayoutOverrides="Height">
47             <ToggleButton Cursor="Hand" HorizontalAlignment="Left"  Margin="0,0,0,0" x:Name="btnFullScreen" Template="{StaticResource  fullScreenControlTemplate}"/>
48         </Grid>
49     </Grid>

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

MediaSlider:

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

1 public class MediaSlider : Slider
2     {
3         public Thumb              horizontalThumb;
4         private FrameworkElement horizontalLeftTrack;
5         private FrameworkElement horizontalRightTrack;
6         private double oldValue = 0, newValue = 0, prevNewValue =  0;
7         public event RoutedPropertyChangedEventHandler<double>  MyValueChanged;
8         public event RoutedPropertyChangedEventHandler<double>  MyValueChangedInDrag;
9         private DispatcherTimer dragtimer = new DispatcherTimer ();
10         private double dragTimeElapsed = 0;
11         private const short DragWaitThreshold = 200, DragWaitInterval  = 100;
12         public Rectangle progressRect = null;
13         private bool dragSeekJustFired = false;
14
15         public MediaSlider()
16         {
17
18             this.ValueChanged += new  RoutedPropertyChangedEventHandler<double>(CustomSlider_ValueChanged);
19             dragtimer.Interval = new TimeSpan(0, 0, 0, 0,  DragWaitInterval);
20             dragtimer.Tick += new EventHandler(dragtimer_Tick);
21         }
22
23         void dragtimer_Tick(object sender, EventArgs e)
24         {
25             dragTimeElapsed += DragWaitInterval;
26
27             if (dragTimeElapsed >= DragWaitThreshold)
28             {
29                 RoutedPropertyChangedEventHandler<double>  handler = MyValueChangedInDrag;
30
31                 if ((handler != null) && (newValue !=  prevNewValue))
32                 {
33                     handler(this, new  RoutedPropertyChangedEventArgs<double>(oldValue, newValue));
34                     dragSeekJustFired = true;
35                     prevNewValue = newValue;
36                 }
37
38                 dragTimeElapsed = 0;
39             }
40         }
41
42         void CustomSlider_ValueChanged(object sender,  RoutedPropertyChangedEventArgs<double> e)
43         {
44             oldValue = e.OldValue;
45             newValue = e.NewValue;
46
47             if (horizontalThumb.IsDragging)
48             {
49                 dragTimeElapsed = 0;
50                 dragtimer.Stop();
51                 dragtimer.Start();
52                 dragSeekJustFired = false;
53             }
54         }
55
56         public override void OnApplyTemplate()
57         {
58             base.OnApplyTemplate();
59
60             horizontalThumb = GetTemplateChild("HorizontalThumb") as  Thumb;
61             horizontalLeftTrack = GetTemplateChild("LeftTrack") as  FrameworkElement;
62             horizontalRightTrack = GetTemplateChild("RightTrack") as  FrameworkElement;
63             progressRect = GetTemplateChild("Progress") as  Rectangle;
64
65             if (horizontalLeftTrack != null)  horizontalLeftTrack.MouseLeftButtonDown += new MouseButtonEventHandler (OnMoveThumbToMouse);
66
67             if (horizontalRightTrack != null)  horizontalRightTrack.MouseLeftButtonDown += new MouseButtonEventHandler (OnMoveThumbToMouse);
68
69             horizontalThumb.DragCompleted += new  DragCompletedEventHandler(DragCompleted);
70
71             progressRect.Width = this.Width;
72         }
73
74         public Storyboard ProgressStoryboard { get { return  (GetTemplateChild("ProgressStoryboard") as Storyboard); } }
75
76         public Rectangle ProgressBar { get { return  (GetTemplateChild("Progress") as Rectangle); } }
77
78         protected override Size ArrangeOverride(Size finalSize)
79         {
80             Size s = base.ArrangeOverride(finalSize);
81
82             if (double.IsNaN(horizontalThumb.Width) &&  (horizontalThumb.ActualWidth != 0))
83             {
84                 horizontalThumb.Width =  horizontalThumb.ActualWidth;
85             }
86
87             if (double.IsNaN(horizontalThumb.Height) &&  (horizontalThumb.ActualHeight != 0))
88             {
89                 horizontalThumb.Height =  horizontalThumb.ActualHeight;
90             }
91
92             if (double.IsNaN(horizontalThumb.Width))  horizontalThumb.Width = horizontalThumb.Height;
93             if (double.IsNaN(horizontalThumb.Height))  horizontalThumb.Height = horizontalThumb.Width;
94
95             return (s);
96         }
97
98         private void OnMoveThumbToMouse(object sender,  MouseButtonEventArgs e)
99         {
100             e.Handled = true;
101             Point p = e.GetPosition(this);
102
103             if (this.Orientation == Orientation.Horizontal)
104             {
105                 Value = (p.X - (horizontalThumb.ActualWidth /  2)) / (ActualWidth - horizontalThumb.ActualWidth) * Maximum;
106             }
107
108             RoutedPropertyChangedEventHandler<double> handler =  MyValueChanged;
109
110             if (handler != null)
111             {
112                 handler(this, new  RoutedPropertyChangedEventArgs<double>(oldValue, Value));
113             }
114         }
115
116         private void DragCompleted(object sender, DragCompletedEventArgs  e)
117         {
118             dragtimer.Stop();
119             dragTimeElapsed = 0;
120
121             RoutedPropertyChangedEventHandler<double> handler =  MyValueChanged;
122
123             if ((handler != null) && (!dragSeekJustFired))
124             {
125                 handler(this, new  RoutedPropertyChangedEventArgs<double>(oldValue, this.Value));
126             }
127         }
128     }

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

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

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

其调用方法非常简单:

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

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

3. 唱片图片信息

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

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

4. 获取音频文件列表

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

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

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

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

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

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

1 private void playList_SelectionChanged(object sender,  SelectionChangedEventArgs e)
2 {
3             DataGrid dg = (sender as DataGrid);
4
5             if (dg.SelectedIndex != _nowPlaying)
6             {
7                 if (dg.SelectedIndex != 0)
8                 {
9                     me.AutoPlay = true;
10                 }
11                 OpenAndPlay(dg.SelectedIndex);
12             }
13
14 }

5. UI控制

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

1         private void Minimize_Click(object sender,  MouseButtonEventArgs e)
2         {
3             Window main = Application.Current.MainWindow;
4
5             if (!_min)
6             {
7                 main.Height = 40;
8                 rot.Angle = 0;
9             }
10             else
11             {
12                 main.Height = 340;
13                 rot.Angle = 180;
14             }
15
16             _min = !_min;
17         }

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

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

1         NotificationWindow notifyWindow = null;
2         private void ShowToast()
3         {
4             notifyWindow = new NotificationWindow();
5
6             if (notifyWindow.Visibility == Visibility.Visible)
7                 notifyWindow.Close();
8
9             NotificationControl myNotify = new NotificationControl ();
10             myNotify.DataContext = _playList[_nowPlaying];
11             notifyWindow.Width = 300;
12             notifyWindow.Height = 100;
13             notifyWindow.Content = myNotify;
14             notifyWindow.Show(10000);
15         }

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

随文源码

转载注明地址:http://www.chengxuyuans.com/slverlight/21813.html