using System.Windows; using System.Windows.Input; using System.Windows.Media; using HandyControl.Data; using HandyControl.Tools; namespace HandyControl.Interactivity; /// /// 鼠标拖动行为,该类是反编译微软的System.Windows.Interactivity程序集得到的,并对其做了些扩展 /// internal class MouseDragElementBehaviorEx : Behavior { public static readonly DependencyProperty XProperty = DependencyProperty.Register(nameof(X), typeof(double), typeof(MouseDragElementBehaviorEx), new PropertyMetadata(double.NaN, OnXChanged)); public static readonly DependencyProperty YProperty = DependencyProperty.Register(nameof(Y), typeof(double), typeof(MouseDragElementBehaviorEx), new PropertyMetadata(double.NaN, OnYChanged)); public static readonly DependencyProperty ConstrainToParentBoundsProperty = DependencyProperty.Register(nameof(ConstrainToParentBounds), typeof(bool), typeof(MouseDragElementBehaviorEx), new PropertyMetadata(ValueBoxes.FalseBox, OnConstrainToParentBoundsChanged)); private Transform _cachedRenderTransform; private Point _relativePosition; private bool _settingPosition; /// /// 是否固定住Y轴 /// public bool LockY { get; set; } /// /// 是否固定住X轴 /// public bool LockX { get; set; } public double X { get => (double) GetValue(MouseDragElementBehavior.XProperty); set => SetValue(MouseDragElementBehavior.XProperty, value); } public double Y { get => (double) GetValue(MouseDragElementBehavior.YProperty); set => SetValue(MouseDragElementBehavior.YProperty, value); } public bool ConstrainToParentBounds { get => (bool) GetValue(MouseDragElementBehavior.ConstrainToParentBoundsProperty); set => SetValue(MouseDragElementBehavior.ConstrainToParentBoundsProperty, ValueBoxes.BooleanBox(value)); } private Rect ElementBounds { get { var layoutRect = ArithmeticHelper.GetLayoutRect(AssociatedObject); return new Rect(new Point(0.0, 0.0), new Size(layoutRect.Width, layoutRect.Height)); } } private FrameworkElement ParentElement => AssociatedObject.Parent as FrameworkElement; private UIElement RootElement { get { DependencyObject reference = AssociatedObject; for (var dependencyObject = reference; dependencyObject != null; dependencyObject = VisualTreeHelper.GetParent(reference)) reference = dependencyObject; return reference as UIElement; } } private Transform RenderTransform { get { if (_cachedRenderTransform == null || !ReferenceEquals(_cachedRenderTransform, AssociatedObject.RenderTransform)) RenderTransform = CloneTransform(AssociatedObject.RenderTransform); return _cachedRenderTransform; } set { if (Equals(_cachedRenderTransform, value)) return; _cachedRenderTransform = value; AssociatedObject.RenderTransform = value; } } public event MouseEventHandler DragBegun; public event MouseEventHandler Dragging; public event MouseEventHandler DragFinished; private static void OnXChanged(object sender, DependencyPropertyChangedEventArgs args) { var dragElementBehavior = (MouseDragElementBehaviorEx) sender; dragElementBehavior.UpdatePosition(new Point((double) args.NewValue, dragElementBehavior.Y)); } private static void OnYChanged(object sender, DependencyPropertyChangedEventArgs args) { var dragElementBehavior = (MouseDragElementBehaviorEx) sender; dragElementBehavior.UpdatePosition(new Point(dragElementBehavior.X, (double) args.NewValue)); } private static void OnConstrainToParentBoundsChanged(object sender, DependencyPropertyChangedEventArgs args) { var dragElementBehavior = (MouseDragElementBehaviorEx) sender; dragElementBehavior.UpdatePosition(new Point(dragElementBehavior.X, dragElementBehavior.Y)); } private void UpdatePosition(Point point) { if (_settingPosition || AssociatedObject == null) return; var transformOffset = GetTransformOffset(AssociatedObject.TransformToVisual(RootElement)); ApplyTranslation(double.IsNaN(point.X) ? 0.0 : point.X - transformOffset.X, double.IsNaN(point.Y) ? 0.0 : point.Y - transformOffset.Y); } private void ApplyTranslation(double x, double y) { if (ParentElement == null) return; var point = TransformAsVector(RootElement.TransformToVisual(ParentElement), x, y); x = point.X; y = point.Y; if (ConstrainToParentBounds) { var parentElement = ParentElement; var rect1 = new Rect(0.0, 0.0, parentElement.ActualWidth, parentElement.ActualHeight); var rect2 = AssociatedObject.TransformToVisual(parentElement).TransformBounds(ElementBounds); rect2.X += x; rect2.Y += y; if (!RectContainsRect(rect1, rect2)) { if (rect2.X < rect1.Left) { var num = rect2.X - rect1.Left; x -= num; } else if (rect2.Right > rect1.Right) { var num = rect2.Right - rect1.Right; x -= num; } if (rect2.Y < rect1.Top) { var num = rect2.Y - rect1.Top; y -= num; } else if (rect2.Bottom > rect1.Bottom) { var num = rect2.Bottom - rect1.Bottom; y -= num; } } } ApplyTranslationTransform(x, y); } internal void ApplyTranslationTransform(double x, double y) { var renderTransform = RenderTransform; var translateTransform = renderTransform as TranslateTransform; if (translateTransform == null) { var matrixTransform = renderTransform as MatrixTransform; if (renderTransform is TransformGroup transformGroup1) { if (transformGroup1.Children.Count > 0) translateTransform = transformGroup1.Children[transformGroup1.Children.Count - 1] as TranslateTransform; if (translateTransform == null) { translateTransform = new TranslateTransform(); transformGroup1.Children.Add(translateTransform); } } else { if (matrixTransform != null) { var matrix = matrixTransform.Matrix; //在该处对微软的类进行了修改 if (!LockX) { matrix.OffsetX += x; } if (!LockY) { matrix.OffsetY += y; } //修改结束 RenderTransform = new MatrixTransform { Matrix = matrix }; return; } var transformGroup2 = new TransformGroup(); translateTransform = new TranslateTransform(); if (renderTransform != null) transformGroup2.Children.Add(renderTransform); transformGroup2.Children.Add(translateTransform); RenderTransform = transformGroup2; } } //在该处对微软的类进行了修改 if (!LockX) { translateTransform.X += x; } if (!LockY) { translateTransform.Y += y; } //修改结束 } internal static Transform CloneTransform(Transform transform) { if (transform == null) return null; if (transform is ScaleTransform scaleTransform) return new ScaleTransform { CenterX = scaleTransform.CenterX, CenterY = scaleTransform.CenterY, ScaleX = scaleTransform.ScaleX, ScaleY = scaleTransform.ScaleY }; if (transform is RotateTransform rotateTransform) return new RotateTransform { Angle = rotateTransform.Angle, CenterX = rotateTransform.CenterX, CenterY = rotateTransform.CenterY }; if (transform is SkewTransform skewTransform) return new SkewTransform { AngleX = skewTransform.AngleX, AngleY = skewTransform.AngleY, CenterX = skewTransform.CenterX, CenterY = skewTransform.CenterY }; if (transform is TranslateTransform translateTransform) return new TranslateTransform { X = translateTransform.X, Y = translateTransform.Y }; if (transform is MatrixTransform matrixTransform) return new MatrixTransform { Matrix = matrixTransform.Matrix }; if (!(transform is TransformGroup transformGroup1)) return null; var transformGroup2 = new TransformGroup(); foreach (var child in transformGroup1.Children) transformGroup2.Children.Add(CloneTransform(child)); return transformGroup2; } private void UpdatePosition() { var transformOffset = GetTransformOffset(AssociatedObject.TransformToVisual(RootElement)); X = transformOffset.X; Y = transformOffset.Y; } internal void StartDrag(Point positionInElementCoordinates) { _relativePosition = positionInElementCoordinates; AssociatedObject.CaptureMouse(); AssociatedObject.MouseMove += OnMouseMove; AssociatedObject.LostMouseCapture += OnLostMouseCapture; AssociatedObject.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp), false); } internal void HandleDrag(Point newPositionInElementCoordinates) { var point = TransformAsVector(AssociatedObject.TransformToVisual(RootElement), newPositionInElementCoordinates.X - _relativePosition.X, newPositionInElementCoordinates.Y - _relativePosition.Y); _settingPosition = true; ApplyTranslation(point.X, point.Y); UpdatePosition(); _settingPosition = false; } internal void EndDrag() { AssociatedObject.MouseMove -= OnMouseMove; AssociatedObject.LostMouseCapture -= OnLostMouseCapture; AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp)); } private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { StartDrag(e.GetPosition(AssociatedObject)); DragBegun?.Invoke(this, e); } private void OnLostMouseCapture(object sender, MouseEventArgs e) { EndDrag(); DragFinished?.Invoke(this, e); } private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { AssociatedObject.ReleaseMouseCapture(); } private void OnMouseMove(object sender, MouseEventArgs e) { HandleDrag(e.GetPosition(AssociatedObject)); Dragging?.Invoke(this, e); } private static bool RectContainsRect(Rect rect1, Rect rect2) { if (rect1.IsEmpty || rect2.IsEmpty || rect1.X > rect2.X || rect1.Y > rect2.Y || rect1.X + rect1.Width < rect2.X + rect2.Width) return false; return rect1.Y + rect1.Height >= rect2.Y + rect2.Height; } private static Point TransformAsVector(GeneralTransform transform, double x, double y) { var point1 = transform.Transform(new Point(0.0, 0.0)); var point2 = transform.Transform(new Point(x, y)); return new Point(point2.X - point1.X, point2.Y - point1.Y); } private static Point GetTransformOffset(GeneralTransform transform) { return transform.Transform(new Point(0.0, 0.0)); } protected override void OnAttached() { AssociatedObject.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), false); } protected override void OnDetaching() { AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown)); } }