using Lattice.Core.Geometry; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using System; namespace Lattice.UI.DragDrop.WinUI.Controls; /// /// Визуальный элемент для отображения перетаскиваемого объекта. /// public class DragAdorner : Control { /// /// Идентификатор свойства для данных перетаскивания. /// public static readonly DependencyProperty DragDataProperty = DependencyProperty.Register( "DragData", typeof(object), typeof(DragAdorner), new PropertyMetadata(null, OnDragDataChanged)); /// /// Идентификатор свойства для смещения относительно курсора. /// public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register( "Offset", typeof(Point), typeof(DragAdorner), new PropertyMetadata(new Point(0, 0))); /// /// Идентификатор свойства для угла поворота. /// public static readonly DependencyProperty RotationAngleProperty = DependencyProperty.Register( "RotationAngle", typeof(double), typeof(DragAdorner), new PropertyMetadata(0.0)); /// /// Идентификатор свойства для прозрачности. /// public static readonly DependencyProperty OpacityLevelProperty = DependencyProperty.Register( "OpacityLevel", typeof(double), typeof(DragAdorner), new PropertyMetadata(0.7)); private ContentPresenter? _contentPresenter; /// /// Инициализирует новый экземпляр класса . /// public DragAdorner() { DefaultStyleKey = typeof(DragAdorner); // Устанавливаем свойства для корректного отображения поверх других элементов IsHitTestVisible = false; UseSystemFocusVisuals = false; } /// /// Получает или задает данные перетаскивания. /// public object DragData { get => GetValue(DragDataProperty); set => SetValue(DragDataProperty, value); } /// /// Получает или задает смещение относительно курсора. /// public Point Offset { get => (Point)GetValue(OffsetProperty); set => SetValue(OffsetProperty, value); } /// /// Получает или задает угол поворота. /// public double RotationAngle { get => (double)GetValue(RotationAngleProperty); set => SetValue(RotationAngleProperty, value); } /// /// Получает или задает уровень прозрачности. /// public double OpacityLevel { get => (double)GetValue(OpacityLevelProperty); set => SetValue(OpacityLevelProperty, value); } /// protected override void OnApplyTemplate() { base.OnApplyTemplate(); _contentPresenter = GetTemplateChild("PART_ContentPresenter") as ContentPresenter; UpdateContent(); } /// /// Обновляет позицию элемента относительно курсора. /// /// Позиция курсора в экранных координатах. public void UpdatePosition(Point cursorPosition) { var transform = new TranslateTransform { X = cursorPosition.X + Offset.X, Y = cursorPosition.Y + Offset.Y }; RenderTransform = new TransformGroup { Children = { transform, new RotateTransform { Angle = RotationAngle } } }; } /// /// Показывает элемент с анимацией. /// public void Show() { Visibility = Visibility.Visible; // Анимация появления var animation = new Microsoft.UI.Xaml.Media.Animation.DoubleAnimation { From = 0, To = OpacityLevel, Duration = TimeSpan.FromMilliseconds(150), EasingFunction = new Microsoft.UI.Xaml.Media.Animation.CubicEase { EasingMode = Microsoft.UI.Xaml.Media.Animation.EasingMode.EaseOut } }; var storyboard = new Microsoft.UI.Xaml.Media.Animation.Storyboard(); storyboard.Children.Add(animation); Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTarget(animation, this); Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTargetProperty(animation, "Opacity"); storyboard.Begin(); } /// /// Скрывает элемент с анимацией. /// public void Hide() { var animation = new Microsoft.UI.Xaml.Media.Animation.DoubleAnimation { From = Opacity, To = 0, Duration = TimeSpan.FromMilliseconds(100), EasingFunction = new Microsoft.UI.Xaml.Media.Animation.CubicEase { EasingMode = Microsoft.UI.Xaml.Media.Animation.EasingMode.EaseIn } }; animation.Completed += (s, e) => { Visibility = Visibility.Collapsed; }; var storyboard = new Microsoft.UI.Xaml.Media.Animation.Storyboard(); storyboard.Children.Add(animation); Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTarget(animation, this); Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTargetProperty(animation, "Opacity"); storyboard.Begin(); } private static void OnDragDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is DragAdorner adorner) { adorner.UpdateContent(); } } private void UpdateContent() { if (_contentPresenter != null) { // Можно добавить DataTemplateSelector для разных типов данных _contentPresenter.Content = DragData; // Автоматически вычисляем смещение для приятного вида if (DragData is FrameworkElement element) { Offset = new Point(-element.ActualWidth / 2, -element.ActualHeight / 2); } } } }