如何为音乐播放器WPF制作滑块控件和进度条

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

我怎样才能制作滑块来控制歌曲应该播放的持续时间并根据歌曲的持续时间移动我有这个用于滑块和进度条的xaml代码:

            <ProgressBar x:Name="progressBar" Margin="5,5,5,59"/>
            <Slider x:Name="slider" Minimum="0" Maximum="100" ValueChanged="Slider_ValueChanged" Margin="5"/>
            <MediaElement x:Name="mediaElement" />

我有单独的播放、暂停、恢复、停止按钮,当我在文件对话框中选择这首歌曲后播放歌曲时,它应该开始播放歌曲,进度条和滑块应该开始随着歌曲移动,我也可以更改滑块,它应该从那里播放歌曲,进度条也应该从滑块所在的地方开始移动。

我有这个 C# 代码,但它从滑块所在的位置播放歌曲,但问题是当我更改滑块时,进度条开始比歌曲和滑块运行得快得多,并且当我什至不触摸滑块时,进度条会向前移动很多它的速度比歌曲和滑块还快:

        public MainWindow()
        {
            InitializeComponent();
            mediaElement.LoadedBehavior = MediaState.Manual;

            mediaElement.MediaOpened += MediaElement_MediaOpened;
            CompositionTarget.Rendering += CompositionTarget_Rendering;
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(100);
            timer.Tick += Timer_Tick;
        }

        private void PlayButton(object sender, RoutedEventArgs e)
        {
            var dialog = new Microsoft.Win32.OpenFileDialog
            {
                FileName = "Music", // Default file name
                DefaultExt = ".mp3", // Default file extension
                Filter = "Audio Files (.mp3)|*.mp3"
            };

            bool? result = dialog.ShowDialog();

            if (result == true)
            {
                // Open document
                string filename = dialog.FileName;
                mediaElement.Source = new Uri(filename, UriKind.RelativeOrAbsolute);
                mediaElement.Play();
            }
        }

        private void MediaElement_MediaOpened(object sender, RoutedEventArgs e)
        {
            // Check if the media has a valid duration
            if (mediaElement.NaturalDuration.HasTimeSpan)
            {
                // Set the maximum value of the slider to the total duration of the media
                slider.Maximum = mediaElement.NaturalDuration.TimeSpan.TotalSeconds;

                // Update the progress bar and slider based on the current position of the media
                progressBar.Value = 0;
                slider.Value = 0;

                // Start the timer after media is opened
                timer.Start();
            }
        }

        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (!isDraggingSlider)
            {
                double currentPosition = mediaElement.Position.TotalSeconds;

                progressBar.Value = currentPosition;
                slider.Value = currentPosition;
            }

            // Check if the media has finished playing
            if (mediaElement.Position >= mediaElement.NaturalDuration)
            {
                // Stop the rendering event when the media completes
                CompositionTarget.Rendering -= CompositionTarget_Rendering;
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            // Update the progress bar and slider based on the current position of the media
            if (!isDraggingSlider)
            {
                double currentPosition = mediaElement.Position.TotalSeconds;

                progressBar.Value = currentPosition;
                slider.Value = currentPosition;
            }

            // Check if the media has finished playing
            if (mediaElement.Position >= mediaElement.NaturalDuration)
            {
                // Stop the timer when the media completes
                timer.Stop();
            }
        }

        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            // Update the media position when the slider value changes
            if (!isDraggingSlider)
            {
                mediaElement.Position = TimeSpan.FromSeconds(slider.Value);
            }
        }

        private void Slider_DragStarted(object sender, RoutedEventArgs e)
        {
            // Pause the rendering event when the user starts dragging the slider
            isDraggingSlider = true;
        }

        private void Slider_DragCompleted(object sender, RoutedEventArgs e)
        {
            // Resume the rendering event when the user finishes dragging the slider
            isDraggingSlider = false;
        }
    }
``` I hope you understand what i'm trying to do because this is my first question



I want to make the song play according to the slider and the progress bar should follow the slider and when i change the slider the song should play form that duration just like that VLC has that slider
c# wpf slider progress-bar audio-player
1个回答
0
投票

您应该删除

Rendering
事件处理。它是多余的,只会降低性能。它经常被升起非常(每当渲染表面时)。这可能会给渲染引擎带来压力。例如,移动滑块需要再次渲染元素以更新表面 --> CompositionTarget.Rendering 被引发。然后事件处理程序执行并操作表面 --> WPF 渲染引擎再次激活 --> CompositionTarget.Rendering 被引发。您可以看到,这会妨碍 UI 线程和渲染线程的资源。

另外,你还没有设置ProgressBar.Maximum。

一般来说,您应该避免处理滑块 进度条。而是将 Progressbar.Value 绑定到 Slider.Value,将 ProgressBar.Maximum 绑定到 Slider.Maximum。

您还应该从

MediaElement.MediaEnded
事件处理程序停止计时器。

接下来,你处理DispatcherTimer的方式会导致内存泄漏。您必须始终取消订阅计时器事件。有些计时器(例如

System.Threading.PeriodicTimer
System.Threading.Timer
)甚至实现了
IDisposable
。 WPF 计时器在内部使用系统计时器。这是一个不受管理的系统资源,并且一直保持活动状态,直到不再有计时器事件侦听器为止。这是一种特殊情况,即使关闭应用程序也不会释放资源。
取消订阅计时器事件(并在一次性时处置其实例)的一个好位置是
Window.OnClosed
覆盖。

以下修复应该可以解决您的问题:

<!-- Bind the ProgressBar to the Slider for automatic update of value and range -->
<ProgressBar x:Name="progressBar"
             Value="{Binding ElementName=slider, Path=Value}"
             Maximum="{Binding ElementName=slider, Path=Maximum}" />
<Slider x:Name="slider"
        ValueChanged="Slider_ValueChanged" />
<MediaElement x:Name="mediaElement" />

public MainWindow(TestViewModel dataContext, INavigator navigator)
{
  InitializeComponent();

  this.mediaElement.LoadedBehavior = MediaState.Manual;
  this.mediaElement.MediaOpened += MediaElement_MediaOpened;
  this.mediaElement.MediaEnded += MediaElement_MediaEnded;
  //CompositionTarget.Rendering += CompositionTarget_Rendering;
  this.timer = new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Input, Timer_Tick, this.Dispatcher);
}

private void PlayButton(object sender, RoutedEventArgs e)
{
  var dialog = new Microsoft.Win32.OpenFileDialog
  {
    FileName = "Music", // Default file name
    DefaultExt = ".mp3", // Default file extension
    Filter = "Audio Files (.mp3)|*.mp3"
  };

  bool? result = dialog.ShowDialog();

  if (result == true)
  {
    // Open document
    string filename = dialog.FileName;
    this.mediaElement.Source = new Uri(filename, UriKind.RelativeOrAbsolute);
    this.mediaElement.Play();
  }
}

private void MediaElement_MediaOpened(object sender, RoutedEventArgs e)
{
  // Check if the media has a valid duration
  if (this.mediaElement.NaturalDuration.HasTimeSpan)
  {
    // Set the maximum value of the slider to the total duration of the media
    this.slider.Maximum = this.mediaElement.NaturalDuration.TimeSpan.TotalSeconds;

    // Initialize the slider.
    // The ProgressBar is automatically updated 
    // as it is bound to the Slider.
    this.slider.Value = 0;

    // Start the timer after media is opened
    this.timer.Start();
  }
}

private void MediaElement_MediaEnded(object sender, RoutedEventArgs e) 
  => this.timer.Stop();

private void Timer_Tick(object sender, EventArgs e)
{
  // Update the slider based on the current position of the media.
  // The ProgressBar is automatically updated 
  // as it is bound to the Slider.
  if (!this.isDraggingSlider)
  {
    double currentPosition = this.mediaElement.Position.TotalSeconds;

    this.slider.Value = currentPosition;
  }
}

private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
  // Update the media position when the slider value changes
  if (!this.isDraggingSlider)
  {
    this.mediaElement.Position = TimeSpan.FromSeconds(slider. Value);
  }
}

protected override void OnClosed(EventArgs e)
{
  base.OnClosed(e);

  this.timer.Stop();

  // Unsubscribe ALL handlers from timer events
  // to avoid the event handler leak
  this.timer.Tick -= Timer_Tick;
}
© www.soinside.com 2019 - 2024. All rights reserved.