GuOrg / Gu.Wpf.Media

Wrapper for System.Windows.Controls.MediaElement.
MIT License
33 stars 11 forks source link

Gu.Wpf.Media

License NuGet Build status Build Status

Wrapper for System.Windows.Controls.MediaElement.

Contents

1. MediaElementWrapper

1.1 Properties

The wrapper wraps the properties of System.Windows.Controls.MediaElement and adds a couple of new properties. Mapped properties are dependency properties that are updated when needed.

1.1.1 State (MediaState)

The current MediaState of the player.

1.1.2. Position (Timespan?)

The current position in the media, null if no media is loaded. Twoway bindable and updates every 0.1 s when playing.

1.1.3. Length (Timespan?)

The length of the current media, nullif no media is loaded.

1.1.4. CanPauseMedia (bool?)

Mapped to System.Windows.Controls.MediaElement.CanPause, nullif no media is loaded.

1.1.5. NaturalVideoHeight (int?)

Mapped to System.Windows.Controls.MediaElement.NaturalVideoHeight, nullif no media is loaded.

1.1.6. NaturalVideoWidth (int?)

Mapped to System.Windows.Controls.MediaElement.NaturalVideoWidth, nullif no media is loaded.

1.1.7. HasAudio (bool?)

Mapped to System.Windows.Controls.MediaElement.HasAudio, nullif no media is loaded.

1.1.8. HasVideo (bool?)

Mapped to System.Windows.Controls.MediaElement.HasVideo, nullif no media is loaded.

1.1.8. HasMedia (bool)

Returns true if media is loaded.

1.1.9. SpeedRatio (double)

Mapped to System.Windows.Controls.MediaElement.SpeedRatio.

1.1.10. IsBuffering (bool)

Mapped to System.Windows.Controls.MediaElement.IsBuffering.

1.1.11. DownloadProgress (double)

Mapped to System.Windows.Controls.MediaElement.DownloadProgress. Updated every 1 s when buffering.

1.1.12. BufferingProgress (double)

Mapped to System.Windows.Controls.MediaElement.BufferingProgress. Updated every 1 s when buffering.

1.1.13. VolumeIncrement (double)

How much volume is changed when MediaCommands.IncreaseVolume & MediaCommands.DecreaseVolume are invoked. Default 0.05;

1.1.14. VideoFormats

A list of video file formats for convenience. .dat; .wmv; .3g2; .3gp; .3gp2; .3gpp; .amv; .asf; .avi; .bin; .cue; .divx; .dv; .flv; .gxf; .iso; .m1v; .m2v; .m2t; .m2ts; .m4v; .mkv; .mov; .mp2; .mp2v; .mp4; .mp4v; .mpa; .mpe; .mpeg; .mpeg1; .mpeg2; .mpeg4; .mpg; .mpv2; .mts; .nsv; .nuv; .ogg; .ogm; .ogv; .ogx; .ps; .rec; .rm; .rmvb; .tod; .ts; .tts; .vob; .vro; .webm

Usage:

OpenFileDialog openFileDialog = new OpenFileDialog
{
    Filter = $"Media files|{this.MediaElement.VideoFormats}|All files (*.*)|*.*"
};

if (openFileDialog.ShowDialog() == true)
{
    this.MediaElement.Source = new Uri(openFileDialog.FileName);
}

1.1.15. AudioFormats

A list of audio file formats for convenience. .mp3; .wma; .aac; .adt; .adts; .m4a; .wav; .aif; .aifc; .aiff; *.cda

Usage:

OpenFileDialog openFileDialog = new OpenFileDialog
{
    Filter = $"Media files|{this.MediaElement.AudioFormats}|All files (*.*)|*.*"
};

if (openFileDialog.ShowDialog() == true)
{
    this.MediaElement.Source = new Uri(openFileDialog.FileName);
}

1.1.16. Source (Uri)

Mapped to System.Windows.Controls.MediaElement.Source. When source changes play is invoked to trigger load. Then pause is invoked in the MediaOpened event. This results in the video paused at the first frame as initial state after setting Source Subscribe to MediaOpened if you want to start playing on load.

1.1.17. Volume (double)

Mapped to System.Windows.Controls.MediaElement.Volume.

1.1.18. Balance (double)

Mapped to System.Windows.Controls.MediaElement.Balance.

1.1.19. IsMuted (bool)

Mapped to System.Windows.Controls.MediaElement.IsMuted.

1.1.20. ScrubbingEnabled (bool)

Mapped to System.Windows.Controls.MediaElement.ScrubbingEnabled.

1.1.21. Stretch (Stretch)

Mapped to System.Windows.Controls.MediaElement.Stretch.

1.1.22. StretchDirection (StretchDirection)

Mapped to System.Windows.Controls.MediaElement.StretchDirection.

1.1.22. LoadedBehavior (MediaState)

Set initial state after media is loaded. This is very unlike MediaElement.LoadedBehavior where setting it to paused will throw if touching play.

1.2. Events

1.2.1. MediaFailed

Mapped to System.Windows.Controls.MediaElement.MediaFailed.

1.2.2. MediaOpened

Mapped to System.Windows.Controls.MediaElement.MediaOpened.

1.2.3. BufferingStarted

Mapped to System.Windows.Controls.MediaElement.BufferingStarted.

1.2.4. BufferingEnded

Mapped to System.Windows.Controls.MediaElement.BufferingEnded.

1.2.5. ScriptCommand

Mapped to System.Windows.Controls.MediaElement.ScriptCommand.

1.2.6. MediaEnded

Mapped to System.Windows.Controls.MediaElement.MediaEnded.

1.3. CommandBindings

Command bindings for:

1.3.1 IncreaseVolume & DecreaseVolume

If parameter is null the value of VolumeIncrement is used.

1.3.2 Skip, SkipForward & SkipBack

If parameter is null the value of SkipIncrement is used. If parameter is an int the value of parameter*SkipIncrement is used. If parameter is double `TimeSpan.FromSeconds(parameter) is used. If parameter is TimeSpan it is used. Values can be negative.

2. MouseWheelGesture

For using mousewheel in inputbindings

<MouseBinding Command="media:Commands.SkipBack" CommandTarget="{Binding ElementName=MediaElement}">
    <MouseBinding.Gesture>
        <media:MouseWheelGesture Direction="Down" />
    </MouseBinding.Gesture>
</MouseBinding>

And with modifier

<MouseBinding Command="media:Commands.SkipBack"
                CommandParameter="60"
                CommandTarget="{Binding ElementName=MediaElement}">
    <MouseBinding.Gesture>
        <media:MouseWheelGesture Direction="Down" Modifiers="Control" />
    </MouseBinding.Gesture>
</MouseBinding>

3. Icon

Exposes a Geometry attached property.

<Button media:Icon.Geometry="{StaticResource {x:Static media:Geometries.PlayGeometryKey}}"
        Command="Play"
        CommandTarget="{Binding ElementName=MediaElement}" />

4. Drag

Exposes a PauseWhileDragging attached property. When binidng this to a MediaElementWrapperplayback is paused while dragging.

<Slider x:Name="ProgressSlider"
        Grid.Row="0"
        media:Drag.PauseWhileDragging="{Binding ElementName=MediaElement}"
        Maximum="{Binding ElementName=MediaElement,
                            Path=Length,
                            Converter={x:Static demo:TimeSpanToSecondsConverter.Default}}"
        Minimum="0"
        Style="{StaticResource {x:Static media:Styles.ProgressSliderStyleKey}}"
        Value="{Binding ElementName=MediaElement,
                        Path=Position,
                        Converter={x:Static demo:TimeSpanToSecondsConverter.Default}}" />

5. TimeSpanToStringConverter

Converts Timespans like this:

Time Result
null -:--
00:00:01 0:01
00:00:12 0:12
00:01:23 1:23
00:12:34 12:23
01:23:45 1:23:45

6. Commands

MediaElementWrapper has a command bindings for:

7. Sample

<Window x:Class="Gu.Wpf.Media.Demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:media="https://github.com/JohanLarsson/Gu.Wpf.Media"
        Title="MainWindow"
        MinWidth="300"
        Background="Black"
        SizeToContent="WidthAndHeight"
        mc:Ignorable="d">
    <Window.InputBindings>
        <KeyBinding Key="Space"
                    Command="TogglePlayPause"
                    CommandTarget="{Binding ElementName=MediaElement}" />
        <KeyBinding Key="F11" Command="media:Commands.ToggleFullScreen" />
        <KeyBinding Key="Escape" Command="media:Commands.EndFullScreen" />
        <KeyBinding Key="Left"
                    Command="media:Commands.SkipBack"
                    CommandTarget="{Binding ElementName=MediaElement}" />
        <MouseBinding Command="media:Commands.SkipBack" CommandTarget="{Binding ElementName=MediaElement}">
            <MouseBinding.Gesture>
                <media:MouseWheelGesture Direction="Down" />
            </MouseBinding.Gesture>
        </MouseBinding>

        <KeyBinding Key="Left"
                    Command="media:Commands.SkipBack"
                    CommandParameter="60"
                    CommandTarget="{Binding ElementName=MediaElement}"
                    Modifiers="Control" />
        <MouseBinding Command="media:Commands.SkipBack"
                      CommandParameter="60"
                      CommandTarget="{Binding ElementName=MediaElement}">
            <MouseBinding.Gesture>
                <media:MouseWheelGesture Direction="Down" Modifiers="Control" />
            </MouseBinding.Gesture>
        </MouseBinding>

        <KeyBinding Key="Right"
                    Command="media:Commands.SkipForward"
                    CommandTarget="{Binding ElementName=MediaElement}" />
        <MouseBinding Command="media:Commands.SkipForward" CommandTarget="{Binding ElementName=MediaElement}">
            <MouseBinding.Gesture>
                <media:MouseWheelGesture Direction="Up" />
            </MouseBinding.Gesture>
        </MouseBinding>

        <KeyBinding Key="Right"
                    Command="media:Commands.SkipForward"
                    CommandParameter="60"
                    CommandTarget="{Binding ElementName=MediaElement}"
                    Modifiers="Control" />
        <MouseBinding Command="media:Commands.SkipForward"
                      CommandParameter="60"
                      CommandTarget="{Binding ElementName=MediaElement}">
            <MouseBinding.Gesture>
                <media:MouseWheelGesture Direction="Up" Modifiers="Control" />
            </MouseBinding.Gesture>
        </MouseBinding>
    </Window.InputBindings>
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Open" Executed="OpenExecuted" />
        <CommandBinding Command="media:Commands.ToggleFullScreen" Executed="OnToggleFullScreenExecuted" />
        <CommandBinding CanExecute="OnEndFullScreenCanExecute"
                        Command="media:Commands.EndFullScreen"
                        Executed="OnEndFullScreenExecuted" />

    </Window.CommandBindings>
    <Grid>
        <media:MediaElementWrapper x:Name="MediaElement"
                                   LoadedBehavior="Play"
                                   ScrubbingEnabled="True"
                                   Stretch="None" />

        <Grid VerticalAlignment="Bottom" Background="#19000000">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Slider x:Name="ProgressSlider"
                    Grid.Row="0"
                    media:Drag.PauseWhileDragging="{Binding ElementName=MediaElement}"
                    Maximum="{Binding ElementName=MediaElement,
                                      Path=Length,
                                      Converter={x:Static media:NullableTimeSpanToSecondsConverter.Default}}"
                    Minimum="0"
                    Style="{StaticResource {x:Static media:Styles.ProgressSliderStyleKey}}"
                    Value="{Binding ElementName=MediaElement,
                                    Path=Position,
                                    Converter={x:Static media:NullableTimeSpanToSecondsConverter.Default}}" />

            <Grid Grid.Row="1">
                <Grid.Resources>
                    <Style BasedOn="{StaticResource {x:Static media:Styles.PlayerButtonBaseStyleKey}}" TargetType="{x:Type Button}" />
                </Grid.Resources>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>

                <ToggleButton x:Name="PlayPauseButton"
                              Grid.Column="0"
                              IsChecked="{Binding ElementName=MediaElement,
                                                  Path=IsPlaying}"
                              IsEnabled="{Binding ElementName=MediaElement,
                                                  Path=HasMedia}">
                    <ToggleButton.Style>
                        <Style BasedOn="{StaticResource {x:Static media:Styles.PlayerButtonBaseStyleKey}}" TargetType="{x:Type ToggleButton}">
                            <Setter Property="media:Icon.Geometry" Value="{StaticResource {x:Static media:Geometries.PauseGeometryKey}}" />
                            <Style.Triggers>
                                <Trigger Property="IsChecked" Value="False">
                                    <Setter Property="media:Icon.Geometry" Value="{StaticResource {x:Static media:Geometries.PlayGeometryKey}}" />
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </ToggleButton.Style>
                </ToggleButton>

                <ToggleButton x:Name="ToggleMutedButton"
                              Grid.Column="1"
                              IsChecked="{Binding ElementName=MediaElement,
                                                  Path=IsMuted}"
                              IsEnabled="{Binding ElementName=MediaElement,
                                                  Path=HasMedia}">
                    <ToggleButton.Style>
                        <Style BasedOn="{StaticResource {x:Static media:Styles.PlayerButtonBaseStyleKey}}" TargetType="{x:Type ToggleButton}">
                            <Setter Property="media:Icon.Geometry" Value="{StaticResource {x:Static media:Geometries.UnMuteGeometryKey}}" />
                            <Style.Triggers>
                                <Trigger Property="IsChecked" Value="False">
                                    <Setter Property="media:Icon.Geometry" Value="{StaticResource {x:Static media:Geometries.MuteGeometryKey}}" />
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </ToggleButton.Style>
                </ToggleButton>

                <TextBlock x:Name="ProgressTextBlock"
                           Grid.Column="2"
                           VerticalAlignment="Center"
                           Foreground="{Binding ElementName=ToggleMutedButton,
                                                Path=Foreground}"
                           Opacity="{Binding ElementName=ToggleMutedButton,
                                             Path=Opacity}">
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} / {1}">
                            <Binding Converter="{x:Static media:TimeSpanToStringConverter.Default}"
                                     ElementName="MediaElement"
                                     Path="Position" />
                            <Binding Converter="{x:Static media:TimeSpanToStringConverter.Default}"
                                     ElementName="MediaElement"
                                     Path="Length" />
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>

                <Button Grid.Column="4"
                        media:Icon.Geometry="{StaticResource {x:Static media:Geometries.FolderOpenGeometryKey}}"
                        Command="ApplicationCommands.Open" />

                <Button Grid.Column="5"
                        HorizontalAlignment="Right"
                        media:Icon.Geometry="{StaticResource {x:Static media:Geometries.FullScreenGeometryKey}}"
                        Command="media:Commands.ToggleFullScreen"
                        IsEnabled="{Binding ElementName=MediaElement,
                                            Path=HasMedia}"
                        Padding="12,6,6,6" />
            </Grid>
        </Grid>
    </Grid>
</Window>

With code behind:

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Win32;

public partial class MainWindow : Window
{
    private Stretch stretch;

    public MainWindow()
    {
        this.InitializeComponent();
    }

    private void OpenExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        OpenFileDialog openFileDialog = new OpenFileDialog
        {
            Filter = $"Media files|{this.MediaElement.VideoFormats}|All files (*.*)|*.*"
        };

        if (openFileDialog.ShowDialog() == true)
        {
            this.MediaElement.SetCurrentValue(MediaElementWrapper.SourceProperty, new Uri(openFileDialog.FileName));
        }
    }

    private void OnToggleFullScreenExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        if (this.WindowStyle == WindowStyle.SingleBorderWindow)
        {
            this.stretch = this.MediaElement.Stretch;
            this.MediaElement.Stretch = Stretch.Uniform;
            this.WindowStyle = WindowStyle.None;
            this.SizeToContent = SizeToContent.Manual;
            this.WindowState = WindowState.Maximized;
        }
        else
        {
            this.MediaElement.Stretch = this.stretch;
            this.WindowStyle = WindowStyle.SingleBorderWindow;
            this.SizeToContent = SizeToContent.WidthAndHeight;
            this.WindowState = WindowState.Normal;
        }

        e.Handled = true;
    }

    private void OnEndFullScreenCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = this.WindowState == WindowState.Maximized && this.WindowStyle == WindowStyle.None;
    }

    private void OnEndFullScreenExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        this.MediaElement.Stretch = this.stretch;
        this.WindowStyle = WindowStyle.SingleBorderWindow;
        this.SizeToContent = SizeToContent.WidthAndHeight;
        this.WindowState = WindowState.Normal;
        e.Handled = true;
    }
}

image

Check out the demo project for more samples. Sample video from https://pixabay.com/