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


    上一篇,我们了解了如何在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:MediaSliderlocal: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(0000, 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(thisnew 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(thisnew 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(thisnew 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版酷我音乐盒。

    本篇源代码下载

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

    32679955(六群)
    23413513(五群)
    32679922(四群)
    100844510(三群)
    37891947(二群)
    22308706(一群)
  • 相关阅读:
    Devpexpress 打印预览问题
    常用DOS命令
    C# datetimePicker控件格式设置
    DevExpress中GridControl的属性设置
    C++ 学习1
    layui 时间控件选择一闪就消失,打不开问题解决办法
    echarts报错,Uncaught Error: series.type should be specified?
    vue项目中,在mian.js文件中引入scss文件后,报错
    记一次在vue中使用scss报错
    使用hexo搭建个人博客时引入图片失败
  • 原文地址:https://www.cnblogs.com/jv9/p/1786508.html
Copyright © 2020-2023  润新知