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

(混合智能客户端)RSS 媒体播放器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.92/5 (8投票s)

2009年3月9日

CPOL

4分钟阅读

viewsIcon

69886

downloadIcon

1069

一个使用 MVVM 模式构建的 WPF 应用程序,用于查看 RSS 视频源。

MediaPlayerImage.gif

MediaPlayerImage_MediaBrowser.gif

引言

我长期以来一直在 CodeProject 上阅读文章,我想是时候尝试自己写一篇了。我写文章的能力几乎为零,所以希望第一篇至少还能勉强读得下去。

我想要一个替代 Internet Explorer 的工具来阅读/播放 RSS 视频源,因此我决定编写一个应用程序,让我可以轻松地浏览和观看我最喜欢的源中的视频。在编写完 RSS 部分后,我决定也添加支持从文件系统打开媒体。

Using the Code

要使用此应用程序,您需要安装 .NET 3.5。该应用程序使用 WPF 的 MediaElement 进行视频和音频播放。MediaElement 依赖于 Windows Media Player 10+ 才能运行。

当您启动应用程序时,媒体浏览器将展开,其中包含库视图和 RSS 视图。您可以关闭任一视图,并从菜单栏的“窗口”下重新打开它们。

要隐藏媒体浏览器,请点击屏幕最底部写着“Media”的地方。应用程序的其余部分相当直观;只需选择您想查看的媒体,然后您就可以像大多数其他媒体应用程序一样控制播放。

关注点

媒体元素

应用程序的整洁性

MediaElement 与 MVVM 模式并非完全兼容,老实说,这给编写此类应用程序带来了复杂性。为什么?因为它缺少依赖属性。我不知道原因,我只相信 WPF 神明自有安排。

不管原因是什么,我必须找到一种方法来解决这个问题。由于我无法在 XAML 中绑定它的属性,例如 PositionSourceVolume,所以我需要一个代码隐藏文件,但同时,我不想弄乱 UI 和主窗口的代码隐藏文件。对我来说,解决方案是使用一个包含媒体元素和所有操作它的控件的用户控件。

依赖的强大功能

这解决了保持整洁的问题。现在,我该如何更新要播放的媒体?

在 MVVM 应用程序中,ViewModel 根本不知道其关联视图中有哪些控件。我决定在用户控件上创建一个依赖属性,这样在主窗口中,我就可以在 XAML 中将控件绑定到 ViewModel 的一个属性。

这有点令人困惑,也许用代码解释会更好。

MyMediaControl 的代码隐藏中,我这样声明一个依赖属性。重要的是要注意,您应该**仅**在包装属性中包含 getset。像这里所示。这样做会导致属性无法正常工作。

public static readonly DependencyProperty MediaSourceProperty =
          DependencyProperty.Register("MyMediaSource", typeof(string), 
            typeof(MyMediaControl), new PropertyMetadata(LastNameChangedCallback));

public string MyMediaSource
{
    get
    {
        return (string)GetValue(MediaSourceProperty);
    }
    set
    {
        SetValue(MediaSourceProperty, value);
    }
}

剩下要做的就是捕获属性更改事件。像这样

private static void MediaSourceChangedCallBack(
               DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != null)
        if (e.NewValue.ToString().Length > 0)
            _MediaURI = new Uri((string)e.NewValue);
}

现在有了功能齐全的依赖属性,剩下要做的就是在 XAML 中绑定该属性。

MainWindow.xaml

<UC:MyMediaControl MyMediaSource="{Binding ElementName=mainwnd,Path=DataContext.Source}"/>

该属性需要显式绑定到主窗口的数据上下文。出于某种原因,它默认情况下不会查看主窗口的数据上下文,而是会查看用户控件中的属性。

通过这种设置,我能够更改 MainWindowViewModel 上的 Source 属性,并同时更新用户控件,同时保持所有元素之间的松散耦合。

无外观控件

WPF 控件被设计成无外观的。这意味着您可以在不影响业务逻辑的情况下更改任何视觉效果。要使用现有控件实现此目的,您可以使用控件模板。

这是此应用程序中使用的滑块控件的模板

<ControlTemplate x:Key="HorizontalSlider" TargetType="{x:Type Slider}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto" 
               MinHeight="{TemplateBinding Slider.MinHeight}"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TickBar 
  Name="TopTick"
  SnapsToDevicePixels="True" 
  Placement="Top"
  Fill="{StaticResource GlyphBrush}"
  Height="15"
  Visibility="Collapsed" />
        <Border 
  Name="TrackBackground"
  Margin="0"
  CornerRadius="4" 
  Height="15"
  Grid.Row="1"
  Background="{StaticResource LightBrush}" 
  BorderBrush="{StaticResource NormalBorderBrush}"
  BorderThickness="1" />
    <Track Grid.Row="1" Name="PART_Track">
        <Track.DecreaseRepeatButton>
            <RepeatButton 
  Style="{StaticResource SliderButtonStyle}"
  Command="Slider.DecreaseLarge" />
        </Track.DecreaseRepeatButton>
        <Track.Thumb>
            <Thumb Style="{StaticResource SliderThumbStyle}" />
        </Track.Thumb>
        <Track.IncreaseRepeatButton>
            <RepeatButton 
  Style="{StaticResource SliderButtonStyle}"
  Command="Slider.IncreaseLarge" />
        </Track.IncreaseRepeatButton>
    </Track>
    <TickBar 
  Name="BottomTick"
  SnapsToDevicePixels="True" 
  Grid.Row="2"
  Fill="{TemplateBinding Foreground}"
  Placement="Bottom"
  Height="4"
  Visibility="Collapsed" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="TickPlacement" Value="TopLeft">
            <Setter TargetName="TopTick" 
               Property="Visibility" Value="Visible"/>
        </Trigger>
        <Trigger Property="TickPlacement" Value="BottomRight">
            <Setter TargetName="BottomTick" 
               Property="Visibility" Value="Visible"/>
        </Trigger>
        <Trigger Property="TickPlacement" Value="Both">
            <Setter TargetName="TopTick" 
               Property="Visibility" Value="Visible"/>
            <Setter TargetName="BottomTick" 
               Property="Visibility" Value="Visible"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

无外观控件可以让您的应用程序拥有非常独特的外观,而无需从头开始创建自定义控件。

您会发现,从控件的默认模板开始是最容易的。有很多方法可以在代码中获取它,但如果您使用的是标准的 WPF 控件,MSDN 提供了所有 WPF 控件的默认模板:http://msdn.microsoft.com/en-us/library/aa970773.aspx

数据转换器

我最后想介绍的一个快速主题是数据转换器。在使用 MVVM 时,您应该很少(如果需要的话)使用它们,但我认为它们是非常强大的工具,值得简要介绍。

数据转换器允许您在 XAML 中绑定各种数据,并使用一个可以操作代码中数据的自定义类来转换它。一个简单的 Converter 类看起来像这样

public class DoubleToPercent : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, 
                          System.Globalization.CultureInfo culture)
    {
        // value is the data from the source object. 
        double thisdbl = (double)value;
        int percent = (int)(thisdbl * 100);
        // Return the value to pass to the target. 
        return percent;
    }
       public object ConvertBack(object value, Type targetType, object parameter, 
                     System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

您需要实现 IvalueConverter,它为您的类提供了两个转换数据的功能。

在 XAML 中,您的绑定将如下所示

<TextBlock Text="{Binding ElementName=mMediaElement, Path=Volume, 
                  Converter={StaticResource DoubleToPercent}}"/>
© . All rights reserved.