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

八、源码下载

 如需源代码,请猛点击下载

 

准备放假咯,明年再见!!

posted @ 2012-12-28 15:14 小李飛菜刀 阅读(...) 评论(...) 编辑 收藏