silverlight自定義控件之多媒體視頻播放器


  Silverlight本身提供了多媒體播放控件,但並沒有封裝好,可以直接使用的控件。在網上搜索了一些,都不是很適用,有些過於復雜要引用一大堆dll,感覺很臃腫,有些樣式風格不適合。silverlight只提供了MediaElement,並不像以前html那樣現成的直接使用那么方便,所以就自己封裝一下,做一個滿足基本功能的簡單播放器。通過本篇隨筆認識一下Blend強大的修改控件樣式魔力,和實現一個簡單的播放器。

功能點:

1、播放、暫停及顯示當前播放狀態

2、實時顯示已播放時間

3、播放進度條,並能拖動播放位置

4、全屏按鈕及雙擊播放畫面入或退出全屏

5、調整音量

6、播放列表

  播放器的基本功能點就是需求,將需求分解,羅列出實現難點和功能要點,評估工作量及風險。

一、認識MediaElement控件

使用到的重要屬性:

public MediaElementState CurrentState { get; }
  MediaElement 的當前狀態。狀態可以為下列值之一(如在 MediaElementState 枚舉中所定義):BufferingClosedOpeningPausedPlayingStopped
默認值為 Closed

public bool AutoPlay { get; set; }
  如果自動播放,則為 true;否則為 false。默認值為 true。如果設置 Source 屬性前將此屬性設置為 true,則設置Source屬性時自動播放視頻。

public Uri Source { get; set; }
  獲取或設置 MediaElement 上的媒體來源。即指定一個視頻的統一資源標識符 (URI) 字符串。

public double Volume { get; set; }
  獲取或設置媒體的音量大小。

使用到的重要事件(非運行代碼):

重要事件
//當媒體流已被驗證和打開且已讀取文件頭時發生。在該自定義控件中主要通過該事件獲取視頻的總時長。
        public event RoutedEventHandler MediaOpened
  
        void mediaElement_MediaOpened(object sender, RoutedEventArgs e)
        {
            this.playTools.TotaPlayTime = (int)this.mediaElement.NaturalDuration.TimeSpan.TotalSeconds;
        }

        //當 MediaElement 不再播放音頻或視頻時發生。在該自定義控件中主要通過該事件設置MediaElement為Stop,並判斷是否循環播放而進行繼續循環播放。
        public event RoutedEventHandler MediaEnded
  
        void mediaElement_MediaEnded(object sender, RoutedEventArgs e)
        {
            this.mediaElement.Stop();
            if (this.IsReplay)
            {
                this.mediaElement.Play();
            }
        }
    

        //當 CurrentState 屬性的值更改時發生。在該自定義控件中主要通過該事件顯示當前視頻播放狀態信息。
        public event RoutedEventHandler CurrentStateChanged
  
      if (this.mediaElement.CurrentState == MediaElementState.Buffering)
        {
            this.playTools.CurrentMessage = this.mediaElement.CurrentState + " " + Math.Round(this.mediaElement.BufferingProgress * 100, 0).ToString() + "%";
        }

        //在存在與媒體 Source 關聯的錯誤時發生。MediaFailed 事件可在下列條件下發生:1、未找到文件。2、無效的(無法識別的或不支持的)媒體格式。3、播放期間未知的媒體錯誤。
      //在該自定義控件中主要通過該事件顯示錯誤信息。
        public event EventHandler<ExceptionRoutedEventArgs> MediaFailed
  
        void mediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)
        {
            this.playTools.CurrentMessage = e.ErrorException.Message;
        }

        //該事件是播放時發生,用於獲取當前已播放時間
        CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
              int currentTime = (int)this.mediaElement.Position.TotalSeconds;
              this.playTools.CurrentPlayTime = currentTime;
        }

 其他支持的媒體格式、協議和日志字段,請查看幫助文檔,更詳細更清楚。

二、分解播放器元素

  

  可以將播放器分為視頻顯示區和控制條兩部分。

  視頻顯示區比較簡單,就只有一個MediaElement控件就可以了。

  控制條部分較為復雜,我們再進一步分解,並分析各個按鈕的功能特性,選擇合適的控件修改其樣式。

  1、實時播放進度條。獲取視頻總時長和當前播放時長,可以計算出進度百分比,在silverlight控件中,ProgressBar可以通過設置Value屬性顯示進度,所以ProgressBar 控件很適合作為播放進度的顯示,只需要修改一下樣式就可以了,不需要編寫其他代碼來實現;

  2、播放、暫停按鈕。這是一個互斥的操作,同一時間只有一種狀態,在silverlight控件中CheckBox和ToggleButton都有這樣的特性,每點擊一次都切換一種狀態,所以選擇其中一個控件都可以。本人更傾向於ToggleButton,可能是因為比CheckBox的名字更容易於理解,不過都無所謂。

  3、停止按鈕。只有一種狀態,所以用Button控件就可以了,不作多解釋。

  4、當前時間、總時長、播放狀態、視頻名稱 。使用TextBlock就可以了,不作多解釋。

  5、播放列表按鈕。點擊顯示視頻列表,再點擊隱藏,有兩個狀態進行切換,所以選擇ToggleButton控件。

  6、全屏按鈕。點擊全屏,再點擊退出全屏,兩個狀態進行切換,所以選擇ToggleButton控件。

  7、音量調整按鈕。是最復雜的一個按鈕,點擊小喇叭圖標,彈出調整閥值條,點擊播放器的其他地方,彈出消失。在silverlight中,點擊應用的其他地方,會觸發事件的控件,可以想一下有哪些,其中ComboBox就是在下拉下來的時候,點擊界面其他地方,會自動收起。可以上下來回拖動調整音量大小,silverlight中Silder控件具有上下拖動的特性,使用它只需要調整樣式就可以了。所以使用ComboBox和Silder組合成音量調整按鈕。

三、設計控件樣式

   修改控件原本的樣式,改變成符合實際需求的樣式,是silverlight的強大優勢之一。

  下面看看ProcessBar怎么樣從傳統的樣子改變成實時播放進度條

  

  從工具箱將ProcessBar拖入工作區,創建一個新的樣式,選擇Eidt a Copy ,輸入progressBarStyle鍵名;

  1、修改ProgressBarRootGradient紅色框選中屬性值,如下圖

  

  效果如下:

  

  2、編輯ProgressBarIndicator,將其類型改為Border類型,接着添加Grid,再添加Rectangle和Ellipse元素。將Grid分成兩列,第一列自適應,第二列固定寬度為10px。Rectangle放在第一列,作用高亮顯示已播放進度,Ellipse放在第二列,作用是顯示為當前播放的進度點。如下圖

  

  設置DeterminateRoot屬性:

  

  設置ProgressBarIndicator屬性:

  

  設置Grid屬性:

  

  設置Rectangle屬性:

  

  設置Ellipse屬性:

  

  效果圖:

  

  3、修改ProgressBarTrack屬性,如下圖所示:

  

  效果圖:

  

  整個播放實時進度條的樣式已經完成,其中最關鍵的就是第2步驟,巧妙地運用ProcessBar控件通過改變ProgressBarIndicator的Width屬性,顯示進度變化,所以利用這個原理添加Grid、Rectangle、Ellipse元素,喬裝成符合自己實際需要的控件。

  

  接下來設計播放、暫停按鈕的樣式,看看ToggleButton按鈕如何轉變的。

  

  從工具箱將ToggleButton拖入工作區,創建一個新的樣式,選擇Create Empty ,輸入PlayPuseTemplate鍵名;

  我們創建的是一個空的模板,所以里面什么元素都沒有,那么添加我們所需要的元素,如下圖:

  

  設置第一個Rectangle屬性:

  

  設置borderPlay屬性(<Border x:Name="borderPlay" CornerRadius="3">),使用鋼筆工具繪制IconPlay形狀

  

  設置borderPause屬性,和borderPlay大同小異,不多作說明了。唯一不同的是默認是隱藏的。

  設置第二個Rectangle屬性:

  

  效果圖:

  

  接下來還需要添加鼠標進入時的過度效果,修改在MouseOver狀態的樣式(高亮按鈕顏色)和Checked選中狀態時的樣式(暫停按鈕顯示,播放按鈕隱藏)。

                                     

  整個播放、暫停按鈕的樣式就完成了。播放列表按鈕、全屏按鈕、停止按鈕都可以依葫蘆畫瓢,就不一步一步寫出來了。

  

  接下來設計最復雜的音量調整按鈕的樣式。看看怎樣從傳統的ComboBox+Silder 轉變成符合實際需要的樣式。

  

  從工具箱將Slider拖入工作區,調整高度固定為80px,設置Orientation="Vertical".如圖所示:

  

  創建一個新的樣式,選擇Edit a Copy ,輸入SliderStyle鍵名。確定后,如下圖所示:

  有HorizontalTemplate和VerticalTemplate兩種類型,我們這里只需修改VerticalTemplate。將Rectangle寬度設為4;右鍵-》Edit Template -》Edit a Copy;確定后,將除Background元素外,其他元素刪除,如下圖所示:

  

  整個調整聲音的滑塊樣式就完成了,接着設計音量按鈕的樣式。

  從工具箱將ComboBox拖入工作區,調整寬高固定為22px,創建一個新的樣式,選擇Edit a Copy ,輸入CmbVolmeStyle鍵名。確定后,如下圖所示:

  

  注意框框里的元素,接下來我們要對DropDownToggle進行修改,右鍵-》Edit Template -》Edit Current,將Grid包含的元素contentPresenter外,其他全部刪除。結果如下圖所示:

  

  接着將BtnArrow刪除,添加Canvas 命名為 IconVolume ,在其里面再添加Canvas,用於繪制小喇叭的樣式,使用鋼筆工具繪制(畫圖需要耐性和反復修改);如圖所示:

  

  接着將ContentPresenter元素刪除,因為我們不需要設置選中的內容。跟着添加兩個Rectangle,目的是作美化用。如圖所示:

  

  緊接着刪除DisabledVisualElement、FocusVisualElement、ScrollViewer。清空PopupBorder的邊框色和背景色。如下圖所示:

  

  從工具箱將Slider拖入到PopupBorder,調整高度固定為80px,設置Orientation="Vertical",Value="5", Margin="0,0,0,-6",並應用SliderStyle樣式。如圖所示:

  

  整個音量調整按鈕的完成了,其他按鈕的樣式依葫蘆畫瓢就可以了。是否有點像周星馳電影007里面說的,它表面上是一個吹風機,其實它是一個刮胡刀。

四、組合成播放器界面

   新建PlayTools用戶控件,將實時播放進度條(ProgressBar)、播放、暫停按鈕(ToggleButton)、停止按鈕(Button)\當前時間、總時長、播放狀態、視頻名稱 (TextBlock)、播放列表按鈕(ToggleButton)、全屏按鈕(ToggleButton)、音量調整按鈕(ComboBox)添加到Grid里,並應用樣式,結果如下圖所示:

  

  新建Player用戶控件,將MediaElement、PlayTools、PlayListBox(沒有詳細說明,有需要請看源代碼) 添加到Grid里,結果如下圖所示:

  

五、關鍵代碼及注意點

   MediaElement控件本身沒提供實時播放進度的事件,所以是通過注冊CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);來實時獲取當前已播放時間

當前播放進度
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);

void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            int currentTime = (int)this.mediaElement.Position.TotalSeconds;
            this.playTools.CurrentPlayTime = currentTime;
        }

 

六、特別處理及說明

   雙擊全屏,由於silverlight4.0版本還沒有鼠標雙擊的事件,所以需要模擬實現雙擊操作,原理是記錄連續點擊的兩次時間差為300毫秒內,則判定為雙擊。

  

雙擊實現
     void mediaElement_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (mouseClickList.Count == 0)
            {
                mouseClickList.Add(DateTime.Now);
            }
            else
            {
                if (mouseClickList.Count == 1)
                {
                    if (DateTime.Now.Subtract(mouseClickList[0]).TotalMilliseconds <= 300)
                    {
                        this.playTools.IsScreenFull = true;
                        playTools_ClickFullScreenButton(true);
                    }
                    mouseClickList.Clear();
                }
            }
        }

 

  全屏效果的實現,這里的實現使用了VideoBrush來渲染,就是所看到的全屏,其實是視頻筆刷,后面才是真正的視頻播放,只是把它投影到全屏控件上了。

 

全屏效果實現
    this.FullPopup = new Popup();

                double width = Application.Current.Host.Content.ActualWidth;
                double height = Application.Current.Host.Content.ActualHeight;
                VideoFullPlayer videoFullPlayer = new VideoFullPlayer();
                videoFullPlayer.FullScreenChange += new Action<bool>(videoFullPlayer_FullScreenChange);
                videoFullPlayer.Width = width;
                videoFullPlayer.Height = height;
                videoFullPlayer.SetVideoBrush(this.mediaElement);

                PlayTools playTools = this.playTools;
                this.LayoutRoot.Children.Remove(this.playTools);
                videoFullPlayer.CurrentPlayTools = playTools;
                FullPopup.Child = videoFullPlayer;
                FullPopup.IsOpen = true;    

七、演示示例

Get Microsoft Silverlight

八、源碼下載

 如需源代碼,請猛點擊下載

 

准備放假咯,明年再見!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM