我有一个应用程序,可以让用户加载音轨,根据需要启动和停止它,当您关闭应用程序时,它会记住您离开的位置,因此当您打开应用程序时,曲目会自动加载到您所在的位置离开了。
当您到达曲目末尾时,我使用 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();
我这里需要的是某种方法来检查曲目是否已经结束,如果是,则将其重置到开头。
我有什么想法可以做到这一点吗?
我在这里需要的是某种方法来检查轨道是否已经在 结束,如果是,则将其重置为开头。
目前,我的代码只是尝试播放当前的曲目 位置,就是结束,所以就立刻停止了。
当您的代码尝试播放曲目时,您将启动一个可访问已完成事件的计时器。假设在这种情况下已完成火灾,经过的时间应该告诉您是否已达到/接近结束......以便您可以重播;或者视情况忽略它。