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