【问题标题】:Slider binding causes significant performance drop滑块绑定导致性能显着下降
【发布时间】:2018-10-08 14:01:57
【问题描述】:

正如标题所说,我有一个这样声明的自定义滑块:

<customControls:ThumbDragSlider IsEnabled="{Binding PlayerSourceState}"
                                Style="{StaticResource {x:Type Slider}}"
                                Value="{Binding CurrentMediaPlayer.MediaElement.Position, Mode=TwoWay, Converter={converters:SecondsToTimeSpanConverter}}"/>

还有更多属性被设置,更多事件和一些命令绑定,但我已经省略了它们,因为我已经将性能问题缩小到这一特定行:

Value="{Binding CurrentMediaPlayer.MediaElement.Position, Mode=TwoWay, Converter={converters:SecondsToTimeSpanConverter}}"

删除它也会消除所有滞后。在我的 PC 上它运行得很好,但是当在没有 GPU 的旧机器上测试时,只使用 CPU,它运行得不好。此滑块用于在 MediaElement 中导航,延迟在实际媒体文件中最为普遍,甚至会关闭。

转换器声明如下:

[ValueConversion(typeof(double), typeof(TimeSpan))]
public class SecondsToTimeSpanConverter : BaseConverter, IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        if (value is TimeSpan ts)
        {
            return ts;
        }
        return TimeSpan.FromSeconds((double)value);
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        return TimeSpan.FromSeconds((double)value);
    }
}

还有这样的自定义滑块:

public class ThumbDragSlider : Slider
{
    public event DragStartedEventHandler DragStarted;
    public event DragCompletedEventHandler DragCompleted;
    public event EventHandler<MouseEventArgs> ThumbMouseEnter;

    public new TimeSpan Value
    {
        get => TimeSpan.FromSeconds(base.Value);
        set => base.Value = value.TotalSeconds;
    }

    public ThumbDragSlider()
    {
        Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, System.Windows.RoutedEventArgs e)
    {
        Loaded -= OnLoaded;
        var track = this.GetElementFromTemplate<Track>("PART_Track");
        track.Thumb.MouseEnter += (o, args) => ThumbMouseEnter?.Invoke(o, args);
    }

    protected override void OnThumbDragStarted(DragStartedEventArgs e)
    {
        base.OnThumbDragStarted(e);
        DragStarted?.Invoke(this, e);
    }

    protected override void OnThumbDragCompleted(DragCompletedEventArgs e)
    {
        base.OnThumbDragCompleted(e);
        DragCompleted?.Invoke(this, e);
    }
}

每 250 毫秒触发一个计时器事件以将滑块值与 MediaElement 值同步,绑定不能这样做,因为 MediaElement 没有负责 PositionDependencyProperty,也没有是 INotifyPropertyChanged 正在触发的事件。

System.Timers.Timer 的事件处理器:

Application.Current.Dispatcher.Invoke(() => sMovieSkipSlider.Value =
    ViewModel.CurrentMediaPlayer.MediaElement.Position);

什么可能导致问题,我该如何解决?

【问题讨论】:

  • @glenebob 删除绑定会导致问题,例如每当拖动滑块的拇指时,媒体元素都不会更新。
  • 对不起,我弄错了。正如我所建议的那样,直接属性更新不会删除两种方式的绑定。
  • 既然滑块值和媒体元素位置都在TimeSpan中,为什么需要转换器?
  • 另外,我建议在拖动完成事件中设置媒体元素位置的值并一起删除绑定。
  • @ShivaniKatukota 查看您的第一条评论this

标签: c# .net wpf xaml controls


【解决方案1】:

您是否尝试过为绑定设置延迟?

Value="{Binding CurrentMediaPlayer.MediaElement.Position, Mode=TwoWay, Converter={converters:SecondsToTimeSpanConverter}, Delay=50}"

也许您必须尝试使用​​不同的值。

【讨论】:

    【解决方案2】:

    设置 Value 不会导致绑定触发并更新媒体播放器的位置,但是因为在这一切发生之前经过了几毫秒,媒体播放器会稍微向后跳,从而导致卡顿?我会采取措施在同步期间不更新媒体播放器的位置。

    【讨论】:

      【解决方案3】:

      我真的不认为需要创建自定义滑块。我相信这可以通过使用常规滑块来实现。

      您陈述了以下内容:

      每 250 毫秒触发一个计时器事件以将滑块值与 MediaElement 值同步,绑定不能这样做,因为 MediaElement 没有负责 PositionDependencyProperty,也没有是 INotifyPropertyChanged 正在触发的事件。

      那么使用绑定就没有意义了,你可以简单地处理你的滑块的MouseLeftButtonUpEvent,然后设置你的MediaElementPosition
      也许每 250 毫秒更新一次的计时器也可能太快,当然这取决于您的要求。就像您打算以高精度使用它一样。但如果你只是想要一个简单的播放器,那么每 1 秒更新一次就足够了。

      这是一个初始化更新滑块的计时器的示例:

      private TimeSpan TotalTime;
      private DispatcherTimer MediaTimer;
      
      private void CurrentMediaPlayer_MediaOpened(object sender, RoutedEventArgs e)
      {
          TotalTime = CurrentMediaPlayer.MediaElement.NaturalDuration.TimeSpan;
          MediaTimer = new DispatcherTimer();
      
          MediaTimer.Interval = TimeSpan.FromSeconds(1);
          //If 1 second is too slow, change this to: TimeSpan.FromMilliseconds(250)
          MediaTimer.Tick += new EventHandler(MediaTimer_Tick);
          MediaTimer.Start();
      }
      
      private void MediaTimer_Tick(object sender, EventArgs e)
      {
          if (CurrentMediaPlayer.MediaElement.NaturalDuration.TimeSpan.TotalSeconds > 0)
          {
              if (TotalTime.TotalSeconds > 0)
              {
                  sMovieSkipSlider.Value = CurrentMediaPlayer.MediaElement.Position.TotalSeconds /
                                         TotalTime.TotalSeconds;
              }
          }
      }
      

      MouseLeftButtonUpEventHandler 添加到您的滑块:

      sMovieSkipSlider.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(sMovieSkipSlider_MouseLeftButtonUp), true);
      

      以下是处理事件以更新您的MediaElementPosition 的示例:

      private void sMovieSkipSlider_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
      {
          if (TotalTime.TotalSeconds > 0)
          {
              CurrentMediaPlayer.MediaElement.Position = TimeSpan.FromSeconds(sMovieSkipSlider.Value * TotalTime.TotalSeconds);
          }
      }
      

      评论:我可能搞砸了您的一些 MVVM 设置,但这至少应该将您推向正确的方向。您可以随时根据需要使用视图的 DataContext 属性。

      【讨论】:

      • 我认为处理 MouseLeftButtonUp 与删除绑定会有同样的问题。虽然拖动媒体不会更新,用户不会知道他在哪里拖动!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多