Silverlight實例教程 - Out of Browser音樂播放器

作者: jv9  來源: 博客園  發布時間: 2010-08-15 10:19  閱讀: 1552 次  推薦: 0   原文鏈接   [收藏]  

  Silverlight 實例教程索引

  本篇,我們將結合以往的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版酷我音樂盒。

  本篇源代碼下載

0
0
 
標簽:Silverlight
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()