WPF视频传输控制 [英] WPF Video Transport Control

查看:58
本文介绍了WPF视频传输控制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对自定义控件还是比较陌生的(在代码中从头开始编写控件-不仅仅是样式化现有控件).我正在复制YouTube视频控件,你知道吗...

I am relatively new to custom controls (writing control from scratch in code - not merely styling existing controls). I am having a go at replicating the YouTube video control, you know the one...

首先,我要开发时间轴"(透明的灰色条,它显示视频的当前位置并允许用户拖动以更改位置).有了预览面板,其余所有内容都将在以后...

To start with I want to develop the "timeline" (the transparent grey bar, which displays the current position of the video and allows the user to drag to change position). With the preview panel and all the rest coming later on...

我目前部分渲染了控件,并且悬停动画和缩放效果很好...

I currently have the control partially rendered and the hover animations and scale working very well...

但是,我正在努力编写正确的代码以允许我拖动拇指".当我尝试处理代表拇指的 Ellipse 的左键时,根据WPF文档,将触发包含 Canvas 的离开事件,因此不会产生任何投诉,我只是不知道如何实现自己想要的东西,实际上,如果我已经做过的事是正确的方法,那我就不知道了.

However, I am struggling to write the correct code to allow me to drag the "thumb". When I try and handle my left click on the Ellipse that is representing my thumb, the leave event of the containing Canvas fires, in accordance with the WPF documentation, so no complaints, I just don;t know how to achieve what I want and indeed if what I have done already is the correct approach.

代码:

[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. 如何实际更改代码以使拇指"拖动正常工作?

感谢您的时间.

Ps.具有工作代码的GitHub项目位于此处,因此您可以重现我遇到的问题.如果有人想帮助我开发此控件,那就太好了!

Ps. the GitHub project with the working code is here so you can reproduce the problem I am having. If anyone wants to help me develop this control, that would be awesome!

Pps.我知道我可以重写滑块来获得时间轴"的功能,但这只是更全面的控件的第一部分,因此需要从头开始编写.

Pps. I am aware I could override a slider to get my functionality for the "timeline", but this is just the first part of a much more comprehensive control and hence needs to be written from scratch.

推荐答案

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

I'm not sure but I think that can resolve your problem :

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

您可以通过处理事件args来停止冒泡,并且不会触发请假事件.

You can stop the bubbling by handling the event args, and the leave event won't be fired.

要更改拇指的位置,必须设置画布"的左连接"属性.

To change the position of the thumb, you have to set the Left attached property of the Canvas.

此外,您将不得不重置isdraggingThumb:

Additionnaly you will have to reset 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;

这篇关于WPF视频传输控制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆