/// <summary> /// Handles the <see cref="UIElement.PreviewDragEnter"/> routed event for the <see cref="tracksListBox"/>. /// </summary> /// <remarks>Begins a dragging operation for a <see cref="VideoTimelineSegment"/>.</remarks> /// <inheritdoc cref="DragEventHandler"/> private void OnTracksListBoxPreviewDragEnter(object sender, DragEventArgs e) { if (_segmentDragAdorner == null) { Size?adornerRectangleSize = null; if (e.Data.GetDataPresent(typeof(SegmentViewModelWeakReference)) && e.Data.GetDataPresent(typeof(Size))) { adornerRectangleSize = (Size)e.Data.GetData(typeof(Size)); _isDraggingSegment = true; } else if (e.Data.GetDataPresent(typeof(WeakReference <AddTrackSegmentCommandParameters>))) { if (!(e.Data.GetData(typeof(WeakReference <AddTrackSegmentCommandParameters>)) is WeakReference <AddTrackSegmentCommandParameters> addTrackSegmentActionDataRef) || !addTrackSegmentActionDataRef.TryGetTarget(out AddTrackSegmentCommandParameters addTrackSegmentActionData)) { throw new InvalidOperationException($"Invalid reference to a {nameof(AddTrackSegmentCommandParameters)} object."); } Debug.Assert(tracksListBox.HasItems); ListBoxItem listBoxItem = tracksListBox.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem; Debug.Assert(listBoxItem != null); VideoTimelineTrack trackElement = listBoxItem.FindVisualChild <VideoTimelineTrack>(); Debug.Assert(trackElement != null); double trackPixelsPerFrame = trackElement.PixelsPerFrame; adornerRectangleSize = new Size(trackPixelsPerFrame * addTrackSegmentActionData.FrameDuration, trackElement.ActualHeight); _segmentDragOrigin = trackElement.TranslatePoint(new Point((addTrackSegmentActionData.FrameDuration / 2) * trackPixelsPerFrame, 0d), tracksListBox); _isDraggingSegment = true; } else { _isDraggingSegment = false; } if (_isDraggingSegment && adornerRectangleSize.HasValue) { _segmentDragAdorner = new TrackSegmentDragAdorner(adornedElement: tracksListBox, segmentElementSize: adornerRectangleSize.Value); _segmentDragAdorner.Attach(); } } }
/// <summary> /// Handles the <see cref="VideoTimelineSegment.HorizontalResizeDragCompletedEvent"/> attached event /// for a <see cref="VideoTimelineSegment"/> element. /// </summary> /// <inheritdoc cref="HorizontalResizeDragCompletedEventHandler"/> private void OnTrackSegmentHorizontalResizeDragCompleted(object sender, HorizontalResizeDragCompletedEventArgs e) { if (_isResizingSegment) { VideoTimelineSegment segmentElement = e.OriginalSource as VideoTimelineSegment; Debug.Assert(segmentElement != null); VideoTimelineTrack trackElement = segmentElement.ParentTrackElement; SegmentViewModelBase segmentViewModel = segmentElement.DataContext as SegmentViewModelBase; Debug.Assert(segmentViewModel != null); int frameDiff = (int)Math.Round(_segmentResizeAdorner.HorizontalOffset / trackElement.PixelsPerFrame); if (frameDiff != 0) { if (e.HorizontalSide == HorizontalSide.Left) { int newStartFrame = segmentViewModel.StartFrame + frameDiff; ViewModel.ChangeTrackSegmentStartFrame(segmentViewModel, newStartFrame); } else // e.HorizontalSide == HorizontalSide.Right { int newEndFrame = segmentViewModel.EndFrame + frameDiff; ViewModel.ChangeTrackSegmentEndFrame(segmentViewModel, newEndFrame); } } segmentElement.Clip = null; _segmentResizeAdorner?.Detach(); _segmentResizeAdorner = null; _isResizingSegment = false; } e.Handled = true; }
/// <summary> /// Tries to find the <see cref="VideoTimelineTrack"/> element and closest frame number /// to the current drag location. /// </summary> /// <param name="dragEventArgs">(In) The <see cref="DragEventArgs"/> for the drag operation.</param> /// <param name="draggedItemStartFrame">(In) The Start Frame of the <see cref="VideoTimelineSegment"/> being dragged.</param> /// <param name="timelineTrackElement">(Out) If successful, the <see cref="VideoTimelineTrack"/> element.</param> /// <param name="closestFrameNumber">(Out) If successful, the closest frame number.</param> /// <returns>A <see cref="bool"/> value indicating success or failure.</returns> private bool TryFindTrackElementAndClosestFrameNumber(DragEventArgs dragEventArgs, int draggedItemStartFrame, out VideoTimelineTrack timelineTrackElement, out int closestFrameNumber) { closestFrameNumber = -1; timelineTrackElement = dragEventArgs.OriginalSource as VideoTimelineTrack; if (timelineTrackElement == null) { if (!(tracksListBox.ContainerFromElement((DependencyObject)dragEventArgs.OriginalSource) is ListBoxItem listBoxItem) || (timelineTrackElement = listBoxItem.FindVisualChild <VideoTimelineTrack>()) == null) { return(false); } } Point mousePositionInListBox = dragEventArgs.GetPosition(tracksListBox); Vector dragPositionOffset = mousePositionInListBox - _segmentDragOrigin.Value; double trackPixelsPerFrame = timelineTrackElement.PixelsPerFrame; double segmentPixelLeftPosition = trackPixelsPerFrame * draggedItemStartFrame; closestFrameNumber = (int)Math.Round((segmentPixelLeftPosition + dragPositionOffset.X) / trackPixelsPerFrame); return(true); }
/// <summary> /// Handles the <see cref="VideoTimelineSegment.HorizontalResizeDraggingEvent"/> attached event /// for a <see cref="VideoTimelineSegment"/> element. /// </summary> /// <inheritdoc cref="HorizontalResizeDraggingEventHandler"/> private void OnTrackSegmentHorizontalResizeDragging(object sender, HorizontalResizeDragEventArgs e) { e.Handled = true; VideoTimelineSegment segmentElement = e.OriginalSource as VideoTimelineSegment; Debug.Assert(segmentElement != null); VideoTimelineTrack trackElement = segmentElement.ParentTrackElement; SegmentViewModelBase segmentViewModel = segmentElement.DataContext as SegmentViewModelBase; Debug.Assert(segmentViewModel != null); int frameDiff = (int)Math.Round(e.HorizontalChange / trackElement.PixelsPerFrame); if (frameDiff == 0) { return; } int proposedStartFrame = segmentViewModel.StartFrame; int proposedEndFrame = segmentViewModel.EndFrame; double framePixelOffset = trackElement.PixelsPerFrame * frameDiff; bool isContracting = false; Rect segmentElementClipRect; if (e.HorizontalSide == HorizontalSide.Left) { proposedStartFrame += frameDiff; if (!ViewModel.CanChangeTrackSegmentStartFrame(segmentViewModel, proposedStartFrame)) { return; } if (frameDiff > 0) { isContracting = true; segmentElementClipRect = new Rect(framePixelOffset, 0d, segmentElement.ActualWidth - framePixelOffset, segmentElement.ActualHeight); } } else // e.HorizontalSide == HorizontalSide.Right { proposedEndFrame += frameDiff; if (!ViewModel.CanChangeTrackSegmentEndFrame(segmentViewModel, proposedEndFrame)) { return; } if (frameDiff < 0) { isContracting = true; segmentElementClipRect = new Rect( new Size(segmentElement.ActualWidth + framePixelOffset, segmentElement.ActualHeight) ); } } if (isContracting) { if (segmentElement.Clip is RectangleGeometry rectangleGeometry) { rectangleGeometry.Rect = segmentElementClipRect; } else { segmentElement.Clip = new RectangleGeometry(segmentElementClipRect); } } else { segmentElement.Clip = null; } if (_segmentResizeAdorner == null) { UIElement segmentElementContainer = segmentElement.TemplatedParent as UIElement; Debug.Assert(segmentElementContainer != null); _segmentResizeAdorner = new TrackSegmentResizeAdorner(adornedElement: trackElement, segmentElement, segmentElementLeftCoordinate: Canvas.GetLeft(segmentElementContainer), attachPosition: e.HorizontalSide); _segmentResizeAdorner.Attach(); _isResizingSegment = true; } _segmentResizeAdorner.HorizontalOffset = framePixelOffset; _segmentResizeAdorner.ToolTipData.StartFrame = proposedStartFrame; _segmentResizeAdorner.ToolTipData.EndFrame = proposedEndFrame; }