WPF视频传输控件

7

我在自定义控件方面比较新(从头开始编写控件代码-而不仅仅是样式现有控件)。我正在尝试复制YouTube视频控件,你知道那个...

enter image description here

首先,我想开发“时间轴”(透明灰色条,显示视频的当前位置并允许用户拖动更改位置)。预览面板和其他所有内容稍后再来处理...

目前,控件部分呈现,并且悬停动画和比例非常好...

enter image description here

然而,我在编写允许我拖动“拇指”的正确代码方面遇到了困难。当我尝试处理表示我的拇指的椭圆形上的左键单击时,包含画布的离开事件会触发,这符合WPF文档的规定,所以没有抱怨,只是我不知道如何实现我想要的,并且我已经做的是否正确。

代码:

[ToolboxItem(true)]
[DisplayName("VideoTimeline")]
[Description("Controls which allows the user navigate video media. In addition is can display a " +
    "waveform repesenting the audio channels for the loaded video media.")]
//[TemplatePart(Name = "PART_ThumbCanvas", Type = typeof(Canvas))]
[TemplatePart(Name = "PART_TimelineCanvas", Type = typeof(Canvas))]
[TemplatePart(Name = "PART_WaveformCanvas", Type = typeof(Canvas))]
[TemplatePart(Name = "PART_PreviewCanvas", Type = typeof(Canvas))]
[TemplatePart(Name = "PART_Thumb", Type = typeof(Ellipse))] // Is this the right thing to be doing? 
public class VideoTimeline : Control
{
    private Canvas thumbCanvas;
    private Canvas timelineCanvas;
    private Canvas waveformCanvas;
    private Canvas previewCanvas;

    private Rectangle timelineOuterBox = new Rectangle();
    private Rectangle timelineProgressBox = new Rectangle();
    private Rectangle timelineSelectionBox = new Rectangle();

    private Ellipse timelineThumb = new Ellipse();
    private Path previewWindow = new Path();

    private Point mouseDownPosition;
    private Point currentMousePosition;

    private const int TIMELINE_ANIMATION_DURATION = 400;
    private const string HIGHLIGHT_FILL = "#878787";

    private double __timelineWidth;

    #region Initialization.
    static VideoTimeline()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(VideoTimeline),
            new FrameworkPropertyMetadata(typeof(VideoTimeline)));
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        //thumbCanvas = GetTemplateChild("PART_ThumbCanvas") as Canvas;
        //thumbCanvas.Background = new SolidColorBrush(Colors.Transparent);
        //thumbCanvas.Children.Add(timelineThumb);

        timelineThumb = EnforceInstance<Ellipse>("PART_Thumb");
        timelineThumb.MouseLeftButtonDown -= TimelineThumb_MouseLeftButtonDown;
        timelineThumb.MouseLeftButtonDown += TimelineThumb_MouseLeftButtonDown;

        timelineCanvas = GetTemplateChild("PART_TimelineCanvas") as Canvas;
        timelineCanvas.Background = new SolidColorBrush(Colors.Transparent);
        timelineCanvas.Children.Add(timelineOuterBox);
        timelineCanvas.Children.Add(timelineSelectionBox);
        timelineCanvas.Children.Add(timelineProgressBox);
        timelineCanvas.Children.Add(timelineThumb);

        previewCanvas = GetTemplateChild("PART_PreviewCanvas") as Canvas;
        previewCanvas.Background = new SolidColorBrush(Colors.Transparent);
        previewCanvas.Children.Add(previewWindow);


    }

    private T EnforceInstance<T>(string partName) where T : FrameworkElement, new()
    {
        return GetTemplateChild(partName) as T ?? new T();
    }

    protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
    {
        base.OnTemplateChanged(oldTemplate, newTemplate);

        if (timelineCanvas != null)
            timelineCanvas.Children.Clear();

        SetDefaultMeasurements();
    }
    #endregion // Initialization.

    #region Event Overrides.
    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        base.OnRenderSizeChanged(sizeInfo);
        //UpdateWaveformCacheScaling();
        SetDefaultMeasurements();
        UpdateAllRegions();
    }

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);

        Canvas c = e.OriginalSource as Canvas;
        if (c == null)
            c = Utils.FindParent<Canvas>(e.OriginalSource as FrameworkElement);

        if (c != null)
        {
            CaptureMouse();
            mouseDownPosition = e.GetPosition(c);
            if (c.Name == "PART_TimelineCanvas")
            {
                Trace.WriteLine("OnMouseLeftDown over TimeLine");
            }
        }
    }

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonUp(e);
        ReleaseMouseCapture();

    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        currentMousePosition = e.GetPosition(thumbCanvas);

        if (Mouse.Captured == null)
        {
            Canvas c = e.OriginalSource as Canvas;
            if (c == null)
                c = Utils.FindParent<Canvas>(e.OriginalSource as FrameworkElement);
        }
    }
    #endregion // Event Overrides.

    #region Drawing Methods and Events.
    private void UpdateAllRegions()
    {
        UpdateTimelineCanvas();
    }

    private void UpdateTimelineCanvas()
    {
        if (timelineCanvas == null)
            return;

        SetDefaultMeasurements();

        // Bounding timeline box.
        timelineOuterBox.Fill = new SolidColorBrush(
            (Color)ColorConverter.ConvertFromString("#878787")) { Opacity = 0.25 };
        timelineOuterBox.StrokeThickness = 0.0;
        timelineOuterBox.Width = __timelineWidth;
        timelineOuterBox.Height = TimelineThickness;
        timelineOuterBox.Margin = new Thickness(TimelineExpansionFactor * TimelineThickness,
            (timelineCanvas.RenderSize.Height - TimelineThickness) / 2, 0, 0);
        timelineOuterBox.SnapsToDevicePixels = true;

        // Selection timeline box.
        timelineSelectionBox.Fill = TimelineSelectionBrush;
        timelineSelectionBox.Width = 0.0;
        timelineSelectionBox.Height = TimelineThickness;
        timelineSelectionBox.Margin = new Thickness(TimelineExpansionFactor * TimelineThickness,
            (timelineCanvas.RenderSize.Height - TimelineThickness) / 2, 0, 0);
        timelineSelectionBox.SnapsToDevicePixels = true;

        // Progress timeline box.
        timelineProgressBox.Fill = TimelineProgressBrush;
        timelineProgressBox.StrokeThickness = 0.0;
        timelineProgressBox.Width = 0.0;
        timelineProgressBox.Height = TimelineThickness;
        timelineProgressBox.Margin = new Thickness(TimelineExpansionFactor * TimelineThickness,
            (timelineCanvas.RenderSize.Height - TimelineThickness) / 2, 0, 0);
        timelineProgressBox.SnapsToDevicePixels = true;

        // Animation and selection.
        timelineCanvas.MouseEnter -= TimelineCanvas_MouseEnter;
        timelineCanvas.MouseEnter += TimelineCanvas_MouseEnter;

        timelineCanvas.MouseLeave -= TimelineCanvas_MouseLeave;
        timelineCanvas.MouseLeave += TimelineCanvas_MouseLeave;

        timelineCanvas.MouseMove -= TimelineCanvas_MouseMove;
        timelineCanvas.MouseMove += TimelineCanvas_MouseMove;

        timelineCanvas.MouseDown -= TimelineCanvas_MouseDown;
        timelineCanvas.MouseDown += TimelineCanvas_MouseDown;

        // The draggable thumb.
        timelineThumb.Fill = TimelineThumbBrush;
        //timelineThumb.Stroke = new SolidColorBrush(Colors.Black);
        //timelineThumb.StrokeThickness = 0.5;
        timelineThumb.VerticalAlignment = VerticalAlignment.Center;
        timelineThumb.Height = timelineThumb.Width = 0.0;
        timelineThumb.Margin = new Thickness(TimelineExpansionFactor * TimelineThickness, 
            timelineCanvas.RenderSize.Height / 2, 0, 0);
        timelineThumb.SnapsToDevicePixels = true;

        timelineThumb.MouseLeftButtonDown -= TimelineThumb_MouseLeftButtonDown;
        timelineThumb.MouseLeftButtonDown += TimelineThumb_MouseLeftButtonDown;

        timelineThumb.MouseLeftButtonUp -= TimelineThumb_MouseLeftButtonUp;
        timelineThumb.MouseLeftButtonUp += TimelineThumb_MouseLeftButtonUp;

        // Preview window.
    }

    private void TimelineCanvas_MouseDown(object sender, MouseButtonEventArgs e)
    {
        Trace.WriteLine("POON");
    }

    private void SetDefaultMeasurements()
    {
        if (timelineCanvas != null)
            __timelineWidth = timelineCanvas.RenderSize.Width - 2 * 2 * TimelineThickness;
    }

    private void TimelineCanvas_MouseEnter(object sender, MouseEventArgs e)
    {
        timelineThumb.ResetAnimation(Ellipse.WidthProperty, Ellipse.HeightProperty);
        timelineProgressBox.ResetAnimation(Rectangle.HeightProperty, Rectangle.MarginProperty);
        timelineSelectionBox.ResetAnimation(Rectangle.HeightProperty, Rectangle.MarginProperty);
        timelineOuterBox.ResetAnimation(Rectangle.HeightProperty, Rectangle.MarginProperty);

        CircleEase easing = new CircleEase();
        easing.EasingMode = EasingMode.EaseOut;

        // Thumb animation.
        Thickness margin = new Thickness(0, 
            (timelineCanvas.RenderSize.Height - 2 * TimelineExpansionFactor * TimelineThickness) / 2, 0, 0);
        EllpiseDiameterAnimation(timelineThumb, TimelineThickness * TimelineExpansionFactor * 2, margin, easing);

        // Timeline animation.
        margin = new Thickness(TimelineExpansionFactor * TimelineThickness,
            (timelineCanvas.RenderSize.Height - (TimelineThickness * TimelineExpansionFactor)) / 2, 0, 0);
        TimelineHeightAnimation(timelineProgressBox, TimelineThickness * TimelineExpansionFactor, margin, easing);
        TimelineHeightAnimation(timelineSelectionBox, TimelineThickness * TimelineExpansionFactor, margin, easing);
        TimelineHeightAnimation(timelineOuterBox, TimelineThickness * TimelineExpansionFactor, margin, easing);

        double selectionWidth = (currentMousePosition.X / RenderSize.Width) * timelineOuterBox.Width;
        timelineSelectionBox.Width = selectionWidth;

        Trace.WriteLine("MouseENTER Canvas");
    }

    private void TimelineCanvas_MouseLeave(object sender, MouseEventArgs e)
    {
        timelineThumb.ResetAnimation(Ellipse.WidthProperty, Ellipse.HeightProperty);
        timelineProgressBox.ResetAnimation(Rectangle.HeightProperty, Rectangle.MarginProperty);
        timelineSelectionBox.ResetAnimation(Rectangle.HeightProperty, Rectangle.MarginProperty);
        timelineOuterBox.ResetAnimation(Rectangle.HeightProperty, Rectangle.MarginProperty);

        CircleEase easing = new CircleEase();
        easing.EasingMode = EasingMode.EaseOut;

        // Thumb animation.
        Thickness margin = new Thickness(TimelineExpansionFactor * TimelineThickness, timelineCanvas.RenderSize.Height / 2, 0, 0);
        EllpiseDiameterAnimation(timelineThumb, 0.0, margin, easing);

        // Timeline animation.
        margin = new Thickness(TimelineExpansionFactor * TimelineThickness,
            (timelineCanvas.RenderSize.Height - TimelineThickness) / 2, 0, 0);
        TimelineHeightAnimation(timelineProgressBox, TimelineThickness, margin, easing);
        TimelineHeightAnimation(timelineSelectionBox, TimelineThickness, margin, easing);
        TimelineHeightAnimation(timelineOuterBox, TimelineThickness, margin, easing);

        if (!isDraggingThumb)
            timelineSelectionBox.Width = 0.0;

        Trace.WriteLine("MouseLeave Canvas");
    }

    private void TimelineCanvas_MouseMove(object sender, MouseEventArgs e)
    {
        Point relativePosition = e.GetPosition(timelineOuterBox);
        double selectionWidth = (relativePosition.X / timelineOuterBox.Width) * timelineOuterBox.Width;
        timelineSelectionBox.Width = selectionWidth.Clamp(0.0, timelineOuterBox.Width);

        if (isDraggingThumb)
        {
            timelineProgressBox.Width = timelineSelectionBox.Width;
            Thickness thumbMargin = new Thickness(TimelineExpansionFactor * TimelineThickness,
                (timelineCanvas.RenderSize.Height - (TimelineThickness * TimelineExpansionFactor)) / 2, 0, 0);
            timelineThumb.Margin = thumbMargin;

        }
    }

    private bool isDraggingThumb = false;

    private void TimelineThumb_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        CaptureMouse();
        isDraggingThumb = true;
        Trace.WriteLine("Dragging Thumb");
    }

    private void TimelineThumb_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        ReleaseMouseCapture();
        isDraggingThumb = false;
        Trace.WriteLine("STOPPED Dragging Thumb");
    }

    #endregion // Drawing Methods and Events.

    #region Animation Methods.
    private void EllpiseDiameterAnimation(Ellipse ellipse, double diameter, Thickness margin, IEasingFunction easing)
    {
        AnimationTimeline widthAnimation = ShapeWidthAnimation(ellipse, diameter, easing);
        AnimationTimeline heightAnimation = ShapeHeightAnimation(ellipse, diameter, easing);
        AnimationTimeline marginAnimation = ShapeMarginAnimation(ellipse, margin, easing);

        Storyboard storyboard = new Storyboard();
        storyboard.Children.Add(widthAnimation);
        storyboard.Children.Add(heightAnimation);
        storyboard.Children.Add(marginAnimation);
        storyboard.Begin(this);
    }

    private void TimelineHeightAnimation(Rectangle rectangle, double height, Thickness margin, IEasingFunction easing)
    {
        AnimationTimeline heightAnimation = ShapeHeightAnimation(rectangle, height, easing);
        AnimationTimeline marginAnimation = ShapeMarginAnimation(rectangle, margin, easing);

        Storyboard storyboard = new Storyboard();
        storyboard.Children.Add(marginAnimation);
        storyboard.Children.Add(heightAnimation);
        storyboard.Begin(this);
    }

    private AnimationTimeline ShapeMarginAnimation(Shape shape, Thickness margin, IEasingFunction easing)
    {
        ThicknessAnimation marginAnimation = new ThicknessAnimation(
            margin, TimeSpan.FromMilliseconds((TIMELINE_ANIMATION_DURATION)));

        if (easing != null)
            marginAnimation.EasingFunction = easing;

        Storyboard.SetTarget(marginAnimation, shape);
        Storyboard.SetTargetProperty(marginAnimation, new PropertyPath(Rectangle.MarginProperty));

        return marginAnimation;
    }

    private AnimationTimeline ShapeWidthAnimation(Shape shape, double width, IEasingFunction easing)
    {
        DoubleAnimation widthAnimation = new DoubleAnimation(
            width, TimeSpan.FromMilliseconds(TIMELINE_ANIMATION_DURATION));

        if (easing != null)
            widthAnimation.EasingFunction = easing;

        Storyboard.SetTarget(widthAnimation, shape);
        Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(Shape.WidthProperty));

        return widthAnimation;
    }

    private AnimationTimeline ShapeHeightAnimation(Shape shape, double height, IEasingFunction easing)
    {
        DoubleAnimation heightAnimation = new DoubleAnimation(
            height, TimeSpan.FromMilliseconds(TIMELINE_ANIMATION_DURATION));

        if (easing != null)
            heightAnimation.EasingFunction = easing;

        Storyboard.SetTarget(heightAnimation, shape);
        Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(Shape.HeightProperty));

        return heightAnimation;
    }
    #endregion // Animation Methods.

    // Lots of DependencyProperties here...
}

XAML样式

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:MediaControlBuilder">
    <Style TargetType="{x:Type local:VideoTimeline}">
        <Setter Property="TimelineProgressBrush" Value="DarkOrange"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:VideoTimeline}">
                    <Border Background="{TemplateBinding Background}"
                       BorderBrush="{TemplateBinding BorderBrush}"
                       BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid>
                            <Grid.RowDefinitions>
                                <!--<RowDefinition Height="*"/>-->
                                <!--<RowDefinition Height="15"/>-->
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="20"/>
                                <!--<RowDefinition Height="*"/>-->
                            </Grid.RowDefinitions>
                            <Canvas Name="PART_PreviewCanvas"
                             Grid.Row="0"
                             ClipToBounds="True"/>
                            <Canvas Name="PART_ThumbCanvas"
                             Grid.Row="1"
                             ClipToBounds="True"/>
                            <Canvas Name="PART_TimelineCanvas"
                             Grid.Row="1"
                             ClipToBounds="True"/>
                            <Canvas Name="PART_WaveformCanvas"
                             Grid.Row="1"
                             ClipToBounds="True"/> 
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

我的问题是:

  1. 我绘制可拖动拇指的方法正确吗?
  2. 如何实际更改代码以使“拇指”的拖动工作?

感谢您的时间。

附:带有工作代码的GitHub项目在此处,因此您可以重现我遇到的问题。如果有人想帮助我开发此控件,那太棒了!

附言:我知道我可以覆盖滑块以获得“时间轴”的功能,但这只是更全面的控件的第一部分,因此需要从头编写。


这不是一个“时间轴”,这样的小工具被称为“传输控件”。它可以帮助您找到类似于此类的东西。虽然框架不对,但也差不多。 - Hans Passant
谢谢你的建议,汉斯。这为我提供了一个很好的研究方向。 - MoonKnight
3个回答

2

在创建新的自定义控件时,不应该“从头开始编写控件”。更好的方法是基于现有控件来实现新的控件。在您的情况下,您想要创建一个自定义滑块控件,因此您的自定义控件可以继承 Slider,利用现有功能如拇指拖动逻辑和开始、结束、值属性。

当扩展现有控件时,您应该从原始控件的默认模板开始,可以使用 VS 获取它。滑块将有一个元素,您应该特别关注:

<Track x:Name="PART_Track" Grid.Column="1">
    <Track.DecreaseRepeatButton>
        <RepeatButton Command="{x:Static Slider.DecreaseLarge}" Style="{StaticResource RepeatButtonTransparent}"/>
    </Track.DecreaseRepeatButton>
    <Track.IncreaseRepeatButton>
        <RepeatButton Command="{x:Static Slider.IncreaseLarge}" Style="{StaticResource RepeatButtonTransparent}"/>
    </Track.IncreaseRepeatButton>
    <Track.Thumb>
        <Thumb x:Name="Thumb" Focusable="False" Height="11" OverridesDefaultStyle="True" Template="{StaticResource SliderThumbVerticalDefault}" VerticalAlignment="Top" Width="18"/>
    </Track.Thumb>
</Track>

通过在模板中使用所需元素,您的基本控件将处理所有基本滑块功能。从这里开始,您可以更改基本控件功能,以您想要的方式样式化滑块部分并添加任何新功能。
如果您不想暴露对于您的时间轴控件不适用的“最小值”等“滑块”属性,只需在模板中使用“滑块”控件即可。

不,我想要 YouTube 媒体控件所能提供的一切功能。这只是实现该目标的第一步。我需要从头开始编写一个全新控件,而不是使用 XAML 编写复合用户控件并覆盖和设置现有控件的样式。感谢您的时间。 - MoonKnight

2

我相信你的问题集中在时间轴滑块上。没有必要自己创建,只需使用Slider控件。您可以重新设计样式,使填充为红色,其余部分为半透明。然后,您可以将Value绑定到MediaElement控件的Position,将Slider的Maximum绑定到Duration。

<Slider Value="{Binding Position.Milliseconds, ElementName=MediaPlayer}" 
        Maximum="{Binding Duration.TimeSpan.Milliseconds, , ElementName=MediaPlayer}"
        Style="{StaticResource YouTubeSliderStyle}" />

当值发生变化时,您可以更新MediaElement的位置。只有在用户更改值时才想这样做(而不是由于Position更新而更改)。为了实现这一点,您可以监听mousedown/up和keydown/up事件。在这些事件期间,您可以(取消)订阅ValueChanged事件并更新位置。

private void UpdatePosition(long time)
{
    MediaPlayer.Position = TimeSpan.FromMilliseconds(time);
}
更新:显示/隐藏滑块的方法。 您可以通过两种方式显示或隐藏滑块。第一种是创建一个新的滑块控件,并在鼠标悬停时显示/隐藏滑块。
class YouTubeSlider : Slider
{
    private Thumb _thumb;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        _thumb = (Thumb)GetTemplateChild("Thumb");
        _thumb.Opacity = 0;
    }

    protected override void OnMouseEnter(MouseEventArgs e)
    {
        base.OnMouseEnter(e);
        _thumb.Opacity = 1;
    }

    protected override void OnMouseLeave(MouseEventArgs e)
    {
        base.OnMouseLeave(e);
        _thumb.Opacity = 0;
    }
}

第二种方法是在控件的样式中处理它。(为了简洁起见,已删除某些部分)
<ControlTemplate x:Key="SliderHorizontal" TargetType="{x:Type Slider}">
    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
    <!-- Elements -->
                <Track.Thumb>
                    <Thumb x:Name="Thumb" Opacity="0" Focusable="False" Height="18" OverridesDefaultStyle="True" Template="{StaticResource SliderThumbHorizontalDefault}" VerticalAlignment="Center" Width="11"/>
                </Track.Thumb>
    <!-- closing tags -->
    </Border>
    <ControlTemplate.Triggers>
        <!-- missing triggers -->
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Opacity" TargetName="Thumb" Value="1"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

感谢您的时间,肖恩,但正如我在上面对Novitchi S的评论中所说,我想从头开始编写控件,尽管您对滑块控件是正确的,但我将向此控件添加所有其他组件(以及更多)。我知道我可以仅仅样式化一个滑块,但这不是我在这种情况下想做的。也许我应该在问题中更清楚地表达这一点。再次感谢... - MoonKnight
为什么编写自己的滑块是必需的?我理解添加其他传输控件,但为什么要编写自定义滑块? - Shawn Kendrot
也许我做得过头了,但我想在滑块控件上添加动画效果。或许我可以使用 TemplatedPart 来创建一个 Slider 并从那里开始,但是滑块控件在功能方面太基础了,所以我想自己编写一个。 - MoonKnight
你需要哪种类型的动画? - Shawn Kendrot
附带的GIF文件中展示了动画。我希望滑块条可以动态扩展,并且当鼠标不在滑块上时,拇指可以消失等。 - MoonKnight
显示剩余2条评论

2

我不确定,但我认为这可以解决你的问题:

    private void TimelineCanvas_MouseMove(object sender, MouseEventArgs e)
    {
        Point relativePosition = e.GetPosition(timelineOuterBox);
        double selectionWidth = (relativePosition.X / timelineOuterBox.Width) * timelineOuterBox.Width;
        timelineSelectionBox.Width = selectionWidth.Clamp(0.0, timelineOuterBox.Width);

        if (isDraggingThumb)
        {
            timelineProgressBox.Width = timelineSelectionBox.Width;
            //Thickness thumbMargin = new Thickness(TimelineThickness * TimelineExpansionFactor,
            //  (timelineCanvas.RenderSize.Height - (TimelineThickness * TimelineExpansionFactor)) / 2, 0, 0);
            //timelineThumb.Margin = thumbMargin;
            Canvas.SetLeft(timelineThumb, timelineProgressBox.Width);
        }
    }

    private bool isDraggingThumb = false;       

    private void TimelineThumb_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        e.Handled = true;
        //CaptureMouse();
        isDraggingThumb = true;
        Trace.WriteLine("Dragging Thumb");
    }

    private void TimelineThumb_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        e.Handled = true;
        //ReleaseMouseCapture();
        isDraggingThumb = false;
        Trace.WriteLine("STOPPED Dragging Thumb");
    }

通过处理事件参数,您可以停止冒泡,这样离开事件就不会被触发。

要更改拇指的位置,您必须设置Canvas的Left附加属性。

此外,您还需要重置isdraggingThumb:

    /// <summary>
    /// Invoked when an unhandled MouseLeftButtonUp routed event reaches an element in 
    /// its route that is derived from this class. Implement this method to add class 
    /// handling for this event.
    /// </summary>
    /// <param name="e">The MouseButtonEventArgs that contains the event data. The event 
    /// data reports that the left mouse button was released.</param>
    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        isDraggingThumb = false;

这个答案是最有帮助的,但赏金已经自动授予了。抱歉。 - MoonKnight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接