Привязка слайдера приводит к значительному снижению производительности - PullRequest
0 голосов
/ 27 апреля 2018

Как видно из названия, у меня есть пользовательский слайдер, объявленный так:

<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}}"

Удаление также удаляет все лаги. На моем ПК он работает нормально, но при тестировании на старой машине без графического процессора, используя только процессор как таковой, он не работает хорошо. Этот ползунок используется для навигации по 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 не имеет DependencyProperty, отвечающего за Position, и нет INotifyPropertyChanged событие, которое запускается.

Обработчик событий System.Timers.Timer:

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

Что может быть причиной проблемы и как я могу ее исправить?

Ответы [ 3 ]

0 голосов
/ 04 мая 2018

Разве установка значения не приводит к тому, что привязка запускает и обновляет позицию медиапроигрывателя, но, поскольку прошло несколько миллисекунд, пока все это не произойдет, медиапроигрыватель немного отскочит назад, вызывая заикание? Я бы предпринял шаги, чтобы не обновлять положение медиаплеера во время синхронизации.

0 голосов
/ 05 мая 2018

Вы пытались установить задержку для привязки?

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

Возможно, вам придется немного поиграть с разными значениями.

0 голосов
/ 03 мая 2018

Я не вижу необходимости в создании собственного слайдера. Я считаю, что это может быть достигнуто с помощью обычного слайдера.

Вы заявили следующее:

Каждые 250 мсек происходит событие таймера для синхронизации значения ползунка со значением MediaElement, привязка не может этого сделать, потому что MediaElement не имеет DependencyProperty, отвечающего за Position, и нет * INotifyPropertyChanged событие, которое запускается.

Тогда нет смысла использовать привязку, вы можете просто обработать MouseLeftButtonUpEvent вашего ползунка и установить Position вашего MediaElement таким образом.
Возможно, таймер, который обновляется каждые 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);

Вот пример обработки события для обновления Position вашего MediaElement:

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

Комментарий : Вероятно, я испортил некоторые настройки MVVM, но это должно по крайней мере подтолкнуть вас в правильном направлении. При необходимости вы всегда можете работать со свойством DataContext вашего представления.

...