MediaElement 未从绑定属性更新

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

我正在编写一个带有嵌入式 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 类的方式缺少一些内容。

我错过了一些基本的东西吗?

c# wpf binding dependency-properties
1个回答
0
投票

我将向您展示一个或多或少可行的实现示例:

    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;
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.