Search code examples
c#xamluwpslider

UWP slider After sliding


I am using a slider to control the position of a playing MediaElement. I have implemented the ValueChanged event to synchronize the position and the value of slider. So when I am dragging on the Thumb, the position of MediaElement also changes. However, this creates bad user experience. The ideal case should be like, after user finishes sliding, MediaElement jumps to that final position. So I am looking for events/handlers like AfterSliding.

I have seen solutions like using OnThumbDragCompleted. However, those questions are about WPF and I cannot find such in UWP. Does anyone have a workaround?

Current:

    private void ProgressBar_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
    {
        MediaPlayer.Position = TimeSpan.FromSeconds(e.NewValue);
        LeftTimeTextBlock.Text = MusicDurationConverter.ToTime((int)e.NewValue);
    }

Expected:

    private void ProgressBar_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
    {
        if (!isDragging || slidingFinished) MediaPlayer.Position = TimeSpan.FromSeconds(e.NewValue);
        LeftTimeTextBlock.Text = MusicDurationConverter.ToTime((int)e.NewValue);
    }

---Update---

I have a timer that ticks every second, but when I tap on the slider (not the thumb) area, the tapped event is not always fired so the thumb will go back and forth between the clicked place and its new position. How can I resolve it so that when user is still holding the thumb, it stays put at the clicked position? I guess the tapped event maybe is not what I should use.

    public void Tick()
    {
        if (ShouldUpdate && !SliderClicked)
        {
            MediaSlider.Value = MediaControl.Player.PlaybackSession.Position.TotalSeconds;
        }
        SliderClicked = false;
    }

    private void MediaSlider_Tapped(object sender, TappedRoutedEventArgs e)
    {
        Debug.WriteLine("Tapped");
        MediaControl.SetPosition(MediaSlider.Value);
        SliderClicked = true;
    }

    private void MediaSlider_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
    {
        MediaControl.SetPosition(MediaSlider.Value);
        ShouldUpdate = true;
        Debug.WriteLine("Completed");
    }

    private void MediaSlider_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
    {
        ShouldUpdate = false;
        Debug.WriteLine("Started");
    }

Solution

  • Slider has ManipulationCompleted event.Occurs when a manipulation on the UIElement is complete.You can subscribe it to listen if Slider is completed.At the same time,you need to set the ManipulationMode to a value other than System or None if want to handle manipulation events.

    in XAML:

    <Slider Name="timelineSlider" ManipulationCompleted="Slider_ManipulationCompleted" ManipulationMode="All" ValueChanged="SeekToMediaPosition" />
    

    in C#:

    private void Slider_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
            {​
                Slider MySlider = sender as Slider;​
                myMediaElement.Position = TimeSpan.FromSeconds(MySlider.Value);​
                LeftTimeTextBlock.Text = MusicDurationConverter.ToTime((int)e.NewValue);​
            }
    

    Update:

    You can subscribe ValueChanged and ManipulationStarting event.I find when you drag the Thumb,it will first trigger the starting event and then valuechanged.If you only click,it will first trigger the valuechange and then starting event.

    in XAML:

    <Slider Name="timelineSlider" ManipulationStarting="TimelineSlider_ManipulationStarting"  ManipulationCompleted="Slider_ManipulationCompleted" ManipulationMode="All" ValueChanged="SeekToMediaPosition" Width="70"/>
    

    in C#:

    You can declare a oldValue property to save the value of Slider.When trigger the starting event,you can compare oldValue and the current value.If equal,you drag the Thumb.

    private double oldValue;
    
    private void SeekToMediaPosition(object sender, RangeBaseValueChangedEventArgs e)
            {
                if (isTap==true)
                {
                    ...//tap
                }
                else
                {
    
                }
                oldValue = timelineSlider.Value;
            }
    private void Slider_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
            {
    
                Slider MySlider = sender as Slider;
                double s = MySlider.Value;
                isTap = true;
            }
    
            private void TimelineSlider_ManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
            {
                if (oldValue == timelineSlider.Value) {
                    //tuozhuai
                    isTap = false;
                }
                else{
                    isTap = true;
                }
            }