如果曲目已经结束,如何从头开始启动 MediaPlayer?

问题描述 投票:0回答:1

我有一个应用程序,可以让用户加载音轨,根据需要启动和停止它,当您关闭应用程序时,它会记住您离开的位置,因此当您打开应用程序时,曲目会自动加载到您所在的位置离开了。

当您到达曲目末尾时,我使用 Media Ended 事件让视图模型知道曲目不再播放。

但是,当用户到达曲目末尾并结束时,我希望他们能够按下开始按钮并将其重置为开头并再次播放。

目前,我的代码只是尝试从当前位置(即结尾)播放曲目,因此它会立即停止。

我尝试在您按下播放时进行检查,看看当前位置是否等于曲目自然持续时间,如果是,则将位置设置为零,但问题是当我调试时,自然持续时间设置为自动,这意味着 MediaOpened 事件尚未触发。但我不确定我的代码如何做到这一点。

XAML 文件

<Window x:Class="Davids_Library.View.MediaWindow"
        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:viewModel="clr-namespace:Davids_Library.ViewModel"
        mc:Ignorable="d"
        Title="David's Music Listener" Height="450" Width="800"
        DataContext="{DynamicResource Vm}">
    <Window.Resources>
        <viewModel:LoadedItemViewModel x:Key="Vm" />
    </Window.Resources>
    <Window.InputBindings>
        <KeyBinding Key="Right" Command="{Binding QuickForward}"></KeyBinding>
        <KeyBinding Key="Left" Command="{Binding QuickBack}"></KeyBinding>
        <KeyBinding Key="Right" Modifiers="Control" Command="{Binding LongForward}"></KeyBinding>
        <KeyBinding Key="Left" Modifiers="Control" Command="{Binding LongBack}"></KeyBinding>
        <KeyBinding Key="i" Modifiers="Control" Command="{Binding GetInfo}"></KeyBinding>
        <KeyBinding Key="o" Modifiers="Control" Command="{Binding LoadFile}"></KeyBinding>
        <KeyBinding Key="Space" Command="{Binding StartStop}"></KeyBinding>
    </Window.InputBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Button Content="Open File" Margin="5" Padding="15 5" 
                CommandParameter="{Binding Text, ElementName=textBox}" 
                Command="{Binding LoadFile, Mode=OneWay}"
                ToolTip="Open File"></Button>
        </Grid>
        <Grid Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Text="{Binding Source, ElementName=MediaPlayer}"></TextBlock>
            <MediaElement x:Name="MediaPlayer" Grid.Row="1" Source="{Binding FileUri}"
            Width="450" Height="250" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill"
            Loaded="OnMediaElementLoaded" MediaEnded="OnMediaEnded" ></MediaElement>
            <UniformGrid Grid.Row="2" Rows="1">
                <TextBlock Text="{Binding Position}"></TextBlock>
                <Button Content="Play" Margin="5" Padding="5" 
                    Command="{Binding StartStop}"
                    ToolTip="Start / Stop"></Button>
            </UniformGrid>
        </Grid>
        </Grid>
</Window>

和代码隐藏

using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Threading;
using Davids_Library.Services;
using Davids_Library.ViewModel;
using Microsoft.Win32;

namespace Davids_Library.View
{
    public partial class MediaWindow
    {
        private readonly DispatcherTimer? _positionUpdateTimer = new();
        private readonly MemoryStoreService _memoryStoreService = new();

        private readonly Func<string, string, bool> _message;
        
        public MediaWindow()
        {
            InitializeComponent();

            _message = (Func<string, string, bool>)((msg, capt) =>
                MessageBox.Show(msg, capt, MessageBoxButton.OK) == MessageBoxResult.OK);

            if (_positionUpdateTimer != null)
            {
                _positionUpdateTimer.Interval = TimeSpan.FromMilliseconds(10);
                _positionUpdateTimer.Tick += OnMediaElementTick;
            }

            Closing += Cleanup;
        }

        private void OnMediaElementLoaded(object sender, RoutedEventArgs e)
        {
            var loadedItem = (LoadedItemViewModel)MediaPlayer.DataContext;
            loadedItem.GotoTimeAction = ms => MediaPlayer.Position = TimeSpan.FromMilliseconds(ms);

            loadedItem.LoadFileExecuted = OpenTrack;

            loadedItem.GetInfoAction = (isPlaying, uri, position) =>
            {
                var message = string.Format("Track {0} is {1}currently playing, and is at position {2}",
                    Path.GetFileName(uri?.AbsolutePath),
                    isPlaying ? "" : "not ",
                    position.ToReadableString());
                _message.Invoke(message, "");
            };

            loadedItem.StartStopRequested = () => 
            {
                if (loadedItem.IsPlaying)
                {
                    MediaPlayer.Pause();
                    _positionUpdateTimer?.Stop();
                    _memoryStoreService.SaveState(loadedItem);
                    loadedItem.IsPlaying = false;
                }
                else
                {
                    MediaPlayer.Position = loadedItem.Position;
                    MediaPlayer.Play();
                    _positionUpdateTimer?.Start();
                    loadedItem.IsPlaying = true;
                }
            };

            var item = _memoryStoreService.LoadDefault();
            if (item == null)
            {
                _message("No track has been loaded yet.", "");
                return;
            }

            _message("Track " + Path.GetFileName(item.Source?.AbsolutePath) + " has been loaded, and is at position " + item.Position.ToReadableString(), "");
            
            loadedItem.FileUri = item.Source;
            loadedItem.Position = item.Position;
        }

        public void OpenTrack()
        {
            var loadedItem = (LoadedItemViewModel)MediaPlayer.DataContext;
            if (loadedItem.IsPlaying)
                loadedItem.StartStop.Execute(null);

            var dialog = new OpenFileDialog();
            var success = dialog.ShowDialog() ?? false;

            if (!success || string.IsNullOrEmpty(dialog.FileName))
            {
                _message("No valid audio file was specified, unable to load", "");
                return;
            }
            
            var filename = dialog.FileName;
            loadedItem.FileUri = new Uri(filename, UriKind.Absolute);
            loadedItem.Position = TimeSpan.Zero;

            _memoryStoreService.SaveState(loadedItem);

            _message("Track " + filename + " has been loaded, and is at the start of the track", "");
        }


        private void Cleanup(object? sender, CancelEventArgs e)
        {
            _positionUpdateTimer?.Stop();

            var loadedItem = (LoadedItemViewModel)MediaPlayer.DataContext;
            _memoryStoreService.SaveState(loadedItem);
        }

        private void OnMediaElementTick(object? sender, EventArgs e)
        {
            var loadedItem = (LoadedItemViewModel)MediaPlayer.DataContext;
            loadedItem.Position = MediaPlayer.Position;
        }

        private void OnMediaEnded(object sender, RoutedEventArgs e)
        {
            var loadedItem = (LoadedItemViewModel)MediaPlayer.DataContext;
            loadedItem.IsPlaying = false;
            _message("You have reached the end of the track.", "");
        }
    }
}

当我将 StartStopRequested 事件分配给内联方法时,我需要在 OnMediaElementLoaded 方法中添加此代码。这两行就是发生的地方

MediaPlayer.Position=loadedItem.Position; MediaPlayer.Play();

我这里需要的是某种方法来检查曲目是否已经结束,如果是,则将其重置到开头。

我有什么想法可以做到这一点吗?

c# wpf
1个回答
0
投票

我在这里需要的是某种方法来检查轨道是否已经在 结束,如果是,则将其重置为开头。

目前,我的代码只是尝试播放当前的曲目 位置,就是结束,所以就立刻停止了。

当您的代码尝试播放曲目时,您将启动一个可访问已完成事件的计时器。假设在这种情况下已完成火灾,经过的时间应该告诉您是否已达到/接近结束......以便您可以重播;或者视情况忽略它。

© www.soinside.com 2019 - 2024. All rights reserved.