我正在编写一个带有嵌入式 MediaElement 控件的基本 WPF 应用程序来播放音频文件。通过我的应用程序,我希望用户能够加载 MP3 文件来收听,并在需要时加载新文件。该应用程序将上次收听的文件存储在根文件夹中的 DAT 文件中,以便有一些基本内存来加载您上次收听的文件。
当我的应用程序从保存的 DAT 文件初始化音频文件时,它就可以工作。媒体元素加载正确的轨道,视觉控件显示正确的文件名和轨道中的位置。
当我使用我编码到应用程序中的“打开新轨道”功能时,底层对象会使用新的轨道名称和轨道位置进行更新,但媒体元素或标签都不会被触发以读取新属性。
本质上,当代码初始化默认音轨时,UI 元素上的绑定会触发,但当我从“打开新轨道”功能调用相同的代码时,它们不会触发。
这是我的 UI 控制代码与绑定
<Label Grid.Column="0" Content="{Binding Path=FullFilename}"></Label>
<MediaElement Grid.Column="1" Source="{Binding Path=FullFilename}" Name="mediaPlayer" Width="450"
Height="250" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill"></MediaElement>
<Label Grid.Column="2" Content="{Binding Path=Position}"></Label>
这是我的主窗口代码隐藏中的 On_Loaded 方法,该方法在 UI 控件加载时调用
private void LibraryViewControl_Loaded(object sender, RoutedEventArgs e)
{
var viewModel = new LibraryViewModel(message, confirm);
viewModel.LoadDefault(); // If I move this line below the next one, it does not work
LibraryViewControl.DataContext = viewModel.LoadedItem;
这里是 LoadedItem 类,我想在其上创建 UI 控件绑定的属性
public class LoadedItem : INotifyPropertyChanged
{
private string? _fullFileName;
private TimeSpan _position;
public LoadedItem(string fullFileName, long millisecondsSinceStart = 0)
{
_fullFileName = fullFileName;
Position = TimeSpan.FromMilliseconds(millisecondsSinceStart);
}
public string? FullFilename
{
get => _fullFileName;
set
{
if (_fullFileName == value) return;
_fullFileName = value;
RaisePropertyChanged("FullFilename");
}
}
public TimeSpan Position
{
get => _position;
set
{
if (_position == value) return;
_position = value;
RaisePropertyChanged("Position");
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
如果我将 viewModel.LoadDefault 移动到初始化 UI 控件的 DataContext 的行之后,它将不起作用。
因此,为了将 Position 和 FullFilename 属性的更新传播到绑定到这些属性的 UI 控件,我编写 LoadedItem 类的方式缺少一些内容。
我错过了一些基本的东西吗?
我将向您展示一个或多或少可行的实现示例:
public class LoadedItem : BaseInpc
{
private Uri? _fullUri;
private TimeSpan _position;
public LoadedItem()
{
LoadFile = new RelayCommand<string>(LoadFileExecute);
GotoTime = new RelayCommand<long>(ms => GotoTimeAction?.Invoke(ms));
}
public LoadedItem(string fullFileName)
: this()
{
_fullUri = new Uri(fullFileName, UriKind.Absolute);
}
public Uri? FileUri
{
get => _fullUri;
private set => Set(ref _fullUri, value);
}
public TimeSpan Position
{
get => _position;
set => Set(ref _position, value);
}
public RelayCommand LoadFile { get; }
private void LoadFileExecute(string fileName) => FileUri = new Uri(fileName, UriKind.Absolute);
public RelayCommand GotoTime { get; }
public Action<long>? GotoTimeAction;
}
public class MediaWindowHelper
{
public static readonly RoutedUICommand OpenBrowser = new RoutedUICommand("Getting file name from Windows Browser", nameof(OpenBrowser), typeof(MediaWindowHelper));
public static readonly RoutedUICommand Play = new RoutedUICommand("Play MediaElement ", nameof(Play), typeof(MediaWindowHelper));
static MediaWindowHelper()
{
CommandManager.RegisterClassCommandBinding(
typeof(MediaWindow),
new CommandBinding(OpenBrowser, (sender, e) =>
{
TextBox textBox = (TextBox)e.Source;
OpenFileDialog dialog = new OpenFileDialog();
if (dialog.ShowDialog() == true)
{
textBox.Text = dialog.FileName;
}
}));
CommandManager.RegisterClassCommandBinding(
typeof(MediaWindow),
new CommandBinding(Play, (sender, e) =>
{
MediaElement media = (MediaElement)e.Source;
media.Play();
}));
}
public long Mileseconds { get; set; }
}
<Window x:Class="Core2024.SO.NZJames.question78884480.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:local="clr-namespace:Core2024.SO.NZJames.question78884480"
mc:Ignorable="d"
Title="MediaWindow" Height="450" Width="800"
DataContext="{DynamicResource vm}">
<Window.Resources>
<local:LoadedItem x:Key="vm"/>
<local:MediaWindowHelper x:Key="helper"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="textBox"/>
<Button Grid.Column="1" Content="..." Margin="5" Padding="5"
CommandTarget="{Binding ElementName=textBox, Mode=OneWay}"
Command="{x:Static local:MediaWindowHelper.OpenBrowser}"
ToolTip="{Binding Command.Text, RelativeSource={RelativeSource Self}}"/>
<Button Grid.Column="2" Content="Open File" Margin="5" Padding="15 5"
CommandParameter="{Binding Text, ElementName=textBox}"
Command="{Binding LoadFile, Mode=OneWay}"/>
<TextBox Grid.Column="3" MinWidth="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Mileseconds, Source={StaticResource helper}}"/>
<Button Grid.Column="4" Content="Go to" Margin="5" Padding="15 5"
CommandParameter="{Binding Mileseconds, Source={StaticResource helper}}"
Command="{Binding GotoTime, Mode=OneWay}"/>
</Grid>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Source, ElementName=mediaPlayer}"/>
<MediaElement x:Name="mediaPlayer" Grid.Row="1" Source="{Binding FileUri}"
Width="450" Height="250"
LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill"
Loaded="OnMediaElementLoaded"/>
<UniformGrid Grid.Row="2" Rows="1">
<TextBlock Text="{Binding Position}"/>
<Button Content="Play" Margin="5" Padding="5"
CommandTarget="{Binding ElementName=mediaPlayer, Mode=OneWay}"
Command="{x:Static local:MediaWindowHelper.Play}"
ToolTip="{Binding Command.Text, RelativeSource={RelativeSource Self}}"/>
</UniformGrid>
</Grid>
</Grid>
</Window>
public partial class MediaWindow : Window
{
public MediaWindow()
{
InitializeComponent();
LoadedItem loadedItem = (LoadedItem)mediaPlayer.DataContext;
loadedItem.GotoTimeAction = ms => mediaPlayer.Position = TimeSpan.FromMilliseconds(ms);
}
private readonly DispatcherTimer timer = new DispatcherTimer();
private void OnMediaElementLoaded(object sender, RoutedEventArgs e)
{
timer.Interval = TimeSpan.FromMilliseconds(10);
timer.Tick += OnMediaElementTick;
timer.Start();
}
private void OnMediaElementTick(object? sender, EventArgs e)
{
LoadedItem loadedItem = (LoadedItem)mediaPlayer.DataContext;
loadedItem.Position = mediaPlayer.Position;
}
}