167 lines
5.7 KiB
C#
167 lines
5.7 KiB
C#
using Microsoft.UI.Xaml;
|
||
using Microsoft.UI.Xaml.Controls;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
namespace Lattice.UI.DragDrop.WinUI.Controls;
|
||
|
||
/// <summary>
|
||
/// Оверлейный слой для отображения всех визуальных элементов перетаскивания.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// Этот элемент добавляется поверх всего содержимого окна и содержит:
|
||
/// - Drag-визуализации (элементы, следующие за курсором)
|
||
/// - Drop-превью (подсветка областей сброса)
|
||
/// </para>
|
||
/// <para>
|
||
/// Элемент имеет <see cref="Canvas.IsHitTestVisible"/> = false, чтобы не перехватывать
|
||
/// пользовательский ввод во время операций перетаскивания.
|
||
/// </para>
|
||
/// </remarks>
|
||
public class DragDropOverlay : Canvas
|
||
{
|
||
private readonly List<UIElement> _dragVisuals = new();
|
||
private readonly List<DropPreviewAdorner> _dropAdorners = new();
|
||
|
||
/// <summary>
|
||
/// Инициализирует новый экземпляр класса <see cref="DragDropOverlay"/>.
|
||
/// </summary>
|
||
public DragDropOverlay()
|
||
{
|
||
IsHitTestVisible = false;
|
||
Background = null;
|
||
|
||
// Устанавливаем высокий Z-Index, чтобы быть поверх всего
|
||
Canvas.SetZIndex(this, 10000);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Показывает визуальное представление перетаскивания.
|
||
/// </summary>
|
||
/// <param name="dragVisual">Визуальное представление.</param>
|
||
/// <param name="initialX">Начальная позиция X.</param>
|
||
/// <param name="initialY">Начальная позиция Y.</param>
|
||
public void ShowDragVisual(UIElement dragVisual, double initialX, double initialY)
|
||
{
|
||
if (!Children.Contains(dragVisual))
|
||
{
|
||
Children.Add(dragVisual);
|
||
_dragVisuals.Add(dragVisual);
|
||
}
|
||
|
||
SetLeft(dragVisual, initialX);
|
||
SetTop(dragVisual, initialY);
|
||
dragVisual.Visibility = Visibility.Visible;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обновляет позицию визуального представления перетаскивания.
|
||
/// </summary>
|
||
/// <param name="dragVisual">Визуальное представление.</param>
|
||
/// <param name="x">Новая позиция X.</param>
|
||
/// <param name="y">Новая позиция Y.</param>
|
||
public void UpdateDragVisualPosition(UIElement dragVisual, double x, double y)
|
||
{
|
||
if (Children.Contains(dragVisual))
|
||
{
|
||
SetLeft(dragVisual, x);
|
||
SetTop(dragVisual, y);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Скрывает визуальное представление перетаскивания.
|
||
/// </summary>
|
||
/// <param name="dragVisual">Визуальное представление.</param>
|
||
public void HideDragVisual(UIElement dragVisual)
|
||
{
|
||
dragVisual.Visibility = Visibility.Collapsed;
|
||
if (Children.Contains(dragVisual))
|
||
{
|
||
Children.Remove(dragVisual);
|
||
_dragVisuals.Remove(dragVisual);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Показывает предварительный просмотр области сброса.
|
||
/// </summary>
|
||
/// <param name="bounds">Границы области.</param>
|
||
/// <returns>Созданный элемент предварительного просмотра.</returns>
|
||
public DropPreviewAdorner ShowDropPreview(Core.Geometry.Rect bounds)
|
||
{
|
||
var adorner = new DropPreviewAdorner
|
||
{
|
||
PreviewColor = Windows.UI.Color.FromArgb(100, 0, 120, 215),
|
||
PreviewThickness = 2.0
|
||
};
|
||
|
||
Children.Add(adorner);
|
||
_dropAdorners.Add(adorner);
|
||
|
||
adorner.Show(bounds);
|
||
return adorner;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обновляет предварительный просмотр области сброса.
|
||
/// </summary>
|
||
/// <param name="adorner">Элемент предварительного просмотра.</param>
|
||
/// <param name="bounds">Новые границы.</param>
|
||
public void UpdateDropPreview(DropPreviewAdorner adorner, Core.Geometry.Rect bounds)
|
||
{
|
||
adorner.UpdatePosition(bounds);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Скрывает все предварительные просмотры областей сброса.
|
||
/// </summary>
|
||
public void HideAllDropPreviews()
|
||
{
|
||
foreach (var adorner in _dropAdorners.ToList())
|
||
{
|
||
adorner.Hide();
|
||
|
||
var timer = new DispatcherTimer
|
||
{
|
||
Interval = TimeSpan.FromMilliseconds(200)
|
||
};
|
||
|
||
timer.Tick += (s, e) =>
|
||
{
|
||
timer.Stop();
|
||
if (Children.Contains(adorner))
|
||
{
|
||
Children.Remove(adorner);
|
||
}
|
||
_dropAdorners.Remove(adorner);
|
||
};
|
||
|
||
timer.Start();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Скрывает все визуальные элементы.
|
||
/// </summary>
|
||
public void ClearAllVisuals()
|
||
{
|
||
foreach (var visual in _dragVisuals.ToList())
|
||
{
|
||
HideDragVisual(visual);
|
||
}
|
||
|
||
HideAllDropPreviews();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает текущий элемент перетаскивания.
|
||
/// </summary>
|
||
/// <returns>Элемент перетаскивания или null.</returns>
|
||
public UIElement? GetCurrentDragVisual()
|
||
{
|
||
return _dragVisuals.FirstOrDefault();
|
||
}
|
||
} |