65.9K
CodeProject 正在变化。 阅读更多。
Home

带媒体列表的简单 WPF 媒体播放器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (56投票s)

2007 年 9 月 13 日

CPOL

4分钟阅读

viewsIcon

422017

downloadIcon

20132

一个简单的 WPF 媒体播放器,带媒体项列表。

目录

引言

我之前在我最早的 WPF 文章中用过 `MediaElement`,并想是时候重新审视一下它,做一个简单的视频播放器控件。这篇文章其实就是关于这个的。这里没有什么突破性的东西,因为 Josh Smith 是令人惊叹的 WPF 技巧的典范;他太厉害了,相信我,他在 WPF 方面是专家。我只是一个初学者,向 WPF 的初学者展示更简单的课题。所以,我们要做的就是构建一个简单的媒体播放器控件,它将具有以下功能:

  • 播放视频
  • 控制媒体播放(暂停/停止/播放)
  • 控制媒体音量
  • 控制媒体位置
  • 缩放媒体
  • 为每个媒体项评分

这些就是我最初的想法。在文章进展过程中,我将逐一介绍每个项目。

工作原理

好的,首先,我将只提及涉及到的文件;它们如下:

  • Resources/Styles.xaml:所有 WPF `Style`。
  • Resources/Templates.xaml:所有 WPF `Template`。
  • Utility Classes/GridLengthAnimation.cs:GridLength 动画器,由 Christian Graus, Nishant Sivakumar 提供,可在 此处 获取。
  • Utility Classes/MediaItem.cs:一个简单的媒体项,用于绑定 `ItemsControl`(`ListBox`)。
  • Utility Classes/XmlHandler.cs:一个简单的 XML 读写器,用于加载/保存媒体项列表。
  • ucMediaPlayer.xaml:主媒体播放器控件。

就这样。我们继续看看每个部分是如何完成的吗?我不会解释所有内容,因为其中很多是标准的 WPF 内容,如 Styles/Templates/Binding,但有时,也会有一些需要提及的地方。那么,我们继续,好吗?

播放视频

由于 WPF `MediaElement` 依赖于 Windows Media Player,我只允许播放 WMP 支持的文件到附加控件中。这是一件相当简单的事情。我首先允许用户将文件拖放到控件上,然后检查文件的类型。相关代码如下所示:

/// <summary>
/// Handles Drop Event for Media Items.
/// </summary>
private void Media_Drop(object sender, DragEventArgs e)
{
    string[] fileNames = e.Data.GetData(DataFormats.FileDrop, true) 
        as string[];
    //keep a dictionary of added files
    foreach (string f in fileNames)
    {
        if (IsValidMediaItem(f))
            mediaItems.Add(f.Substring(f.LastIndexOf(@"\")+1),
    new MediaItem(@f,0));
    }

    //now add to the list
    foreach (MediaItem mi in mediaItems.Values)
        lstMediaItems.Items.Add(mi);

    // Mark the event as handled,
    // so the control's native Drop handler is not called.
    e.Handled = true;
}

/// <summary>
/// check to see if dragged items are valid
/// </summary>
/// <returns>true if filename is valid</returns>
private bool IsValidMediaItem(string filename)
{
    bool isValid = false;
    string fileExtesion = filename.Substring(filename.LastIndexOf("."));
    foreach (string s in MediaItem.allowableMediaTypes)
    {
        if (s.Equals(fileExtesion, 
    StringComparison.CurrentCultureIgnoreCase))
            isValid = true;
    }
    return isValid;
}

就这样。

控制媒体播放(暂停/停止/播放)

在处理 WPF `MediaElement` 时,您可以使用 Media `StoryBoard` 或代码。要使用代码,您必须确保 `MediaElement.LoadedBehavior="Manual"`,如果有,那么只需在 `MediaElement` 上使用以下方法即可:

  • mediaPlayerMain.Play()
  • mediaPlayerMain.Pause()
  • mediaPlayerMain.Stop()

控制媒体音量

我在这里所做的就是使用一个 `Slider` WPF 控件来更改 `MediaElement` 的 `Volume` 属性。

控制媒体位置

我在这里所做的就是使用一个 `Slider` WPF 控件来更改 `MediaElement` 的 `Position` 属性。

缩放媒体

我希望能够让 `MediaElement` 占据整个可用的屏幕空间。为此,当您单击主 `MediaElement` 时,它将通过隐藏媒体列表来动画化以占据可用的屏幕区域。这使用了 Christian Graus, Nishant Sivakumar 的优秀文章,可在 此处 获取。

为每个媒体项评分

我希望能够为媒体项评分。所以我创建了一个特殊的评分按钮 `Style`,它应用于一些 `ToggleButton`。

评分的代码 `Style` 如下所示,其中星星实际上是使用 `Data` 属性绘制的。

<!-- Rating Cell-->
<Style x:Key="StarToggleButton" TargetType="{x:Type ToggleButton}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type ToggleButton}">
          <Canvas Width="12" Height="12">
            <Path Name="star" Fill="Gray" 
        Data="M 5,0 L 4,4 L 0,4 L 3,7 L 2,11 L 5,9 L 6,9 L 9,11 L 8,
        7 L 11,4 L 7,4 L 6,0"/>
          </Canvas>
          <ControlTemplate.Triggers>
            <Trigger Property="IsChecked" Value="True">
              <Setter TargetName="star" Property="Fill" Value="Gold"/>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
</Style>

结果如下控件:

其他一些东西

一些其他不错的功能是,我使用了一些 3D 按钮(直接取自 Adam Nathan 的 WPF Unleashed 书籍)。由于 3D 按钮是通过使用标准的 WPF 样式实现的,它们也可以显示内容,正如我在这里所做的那样,在 3D 按钮上播放视频。

实现这一点 `Style` 代码如下。但正如我所说,我从 Adam Nathan 的 WPF Unleashed 书籍中“偷”来了这个。

<!-- 3D Media Buttons -->
<Style x:Key="btn3DStyle"  TargetType="{x:Type Button}">
    <Style.Resources>
      <Storyboard x:Key="Spin">
        <DoubleAnimation
          Storyboard.TargetName="CubeRotation"
          Storyboard.TargetProperty="Angle" BeginTime="0:0:0"
          Duration="0:0:1" From="0" To="360" 
          DecelerationRatio="0.5" 
          AccelerationRatio="0.5" />
        <DoubleAnimation
          Storyboard.TargetName="CubeRotation"
          Storyboard.TargetProperty="Angle" BeginTime="0:0:1"
          Duration="0:0:1" From="360" 
          To="0" DecelerationRatio="0.5" 
          AccelerationRatio="0.5" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleX"
          BeginTime="0:0:0"
          Duration="0:0:1" From="0.5" To="0.75" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleX"
          BeginTime="0:0:1"
          Duration="0:0:1" From="0.75" To="1.0" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleY"
          BeginTime="0:0:0"
          Duration="0:0:1" From="0.5" To="0.75" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleY"
          BeginTime="0:0:1"
          Duration="0:0:1" From="0.75" To="1.0" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleZ"
          BeginTime="0:0:0"
          Duration="0:0:1" From="0.5" To="0.75" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleZ"
          BeginTime="0:0:1"
          Duration="0:0:1" From="0.75" To="1.0" />
      </Storyboard>
    </Style.Resources>

    <Setter Property="Width" Value="100"/>
    <Setter Property="Height" Value="100"/>    
    
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate>

          <ControlTemplate.Triggers>
            <Trigger Property="Button.IsMouseOver" Value="true">
              <Trigger.EnterActions>
                <BeginStoryboard Storyboard="{StaticResource Spin}"/>
              </Trigger.EnterActions>
            </Trigger>
          </ControlTemplate.Triggers>

          <Viewport3D>
            <Viewport3D.Camera>
              <PerspectiveCamera Position="4,4,4" LookDirection="-1,-1,-1" />
            </Viewport3D.Camera>
            <Viewport3D.Children>
              <ModelVisual3D>
                <ModelVisual3D.Content>
                  <DirectionalLight Direction="-0.3,-0.4,-0.5" />
                </ModelVisual3D.Content>
              </ModelVisual3D>
              <ModelVisual3D x:Name="Cube">
                <ModelVisual3D.Transform>

                  <Transform3DGroup>
                    <RotateTransform3D>
                      <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D x:Name="CubeRotation" 
                          Axis="1,2,3" Angle="0" />
                      </RotateTransform3D.Rotation>
                    </RotateTransform3D>
                    <ScaleTransform3D x:Name="CubeScale" ScaleX="1" 
                      ScaleY="1" ScaleZ="1" CenterX="0" 
                      CenterY="0" CenterZ="0" />
                  </Transform3DGroup>

                </ModelVisual3D.Transform>
                <ModelVisual3D.Content>
                  <GeometryModel3D x:Name="OB_Cube">
                    <GeometryModel3D.Material>
                      <DiffuseMaterial>
                        <DiffuseMaterial.Brush>
                          <VisualBrush ViewportUnits="Absolute" Transform="1,0,0,-1,0,1">
                            <VisualBrush.Visual>
                              <Border 
                                   Background="{Binding Path=Background, 
                                     RelativeSource='{RelativeSource TemplatedParent}'}">
                                <Label Content="{Binding Path=Content, 
                                     RelativeSource='{RelativeSource TemplatedParent}'}" />
                              </Border>
                            </VisualBrush.Visual>
                          </VisualBrush>
                        </DiffuseMaterial.Brush>
                      </DiffuseMaterial>
                    </GeometryModel3D.Material>
                    <GeometryModel3D.Geometry>
                      <MeshGeometry3D x:Name="ME_Cube"
                        Positions="1,1,-1 1,-1,-1 -1,-1,-1 -1,1,-1 1,1,
                                   1 -1,1,1 -1,-1,1 1,-1,1 1,1,
                                   -1 1,1,1 1,-1,1 1,-1,-1 1,-1,-1 1,-1,1 -1,
                                   -1,1 -1,-1,-1 -1,-1,-1 -1,-1,1 -1,1,1 -1,1,
                                   -1 1,1,1 1,1,-1 -1,1,-1 -1,1,1"
                        TriangleIndices="0 1 2 0 2 3 4 5 6 4 6 7 8 9 10 8 
                                         10 11 12 13 14 12 14 15 16 17 
                                         18 16 18 19 20 21 22 20 22 23"
                        TextureCoordinates="0,1 0,0 1,0 1,1 1,1 -0,1 0,
                                            -0 1,0 1,1 -0,1 0,-0 1,0 1,0 1,
                                            1 -0,1 0,-0 -0,0 1,-0 1,1 0,
                                            1 1,-0 1,1 0,1 -0,0"/>
                    </GeometryModel3D.Geometry>
                  </GeometryModel3D>
                </ModelVisual3D.Content>
              </ModelVisual3D>
            </Viewport3D.Children>
          </Viewport3D>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
</Style>

我还应用了一些动画来旋转和调整这些按钮的大小,很棒吧?哦,媒体项的样式也可以通过使用媒体项区域的最后一个按钮来切换到更传统的按钮。在 3D 按钮和标准按钮的情况下,我还创建了一个更漂亮的工具提示。

如何使用它

实际上(我希望)这很简单;要么将一些媒体项拖放到左侧的列表框区域,要么加载以前保存的 XML 媒体列表。当媒体项加载后,只需单击一个,它就会开始在主窗口中播放,您可以使用提供的控件来控制它。

就是这样

尽管这篇文章的代码量不多,但我在这篇文章的编写过程中非常愉快,并希望它能对某些人有所帮助。

您怎么看?

我想请求一下,如果你喜欢这篇文章,请投它一票,并留下一些评论,这样我就知道这篇文章的水平是否合适,以及它是否包含了人们需要了解的内容。

结论

这里没有什么太多要提及的了,因为我认为文章的其余部分已经涵盖了。

历史

  • v1.0 - 2007/09/13:首次发布。
© . All rights reserved.