diff --git a/Lattice.IDE/Controls/EditorView.xaml.cs b/Lattice.IDE/Controls/EditorView.xaml.cs
index 1f45a18..6fb6fd7 100644
--- a/Lattice.IDE/Controls/EditorView.xaml.cs
+++ b/Lattice.IDE/Controls/EditorView.xaml.cs
@@ -1,17 +1,4 @@
-using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Controls.Primitives;
-using Microsoft.UI.Xaml.Data;
-using Microsoft.UI.Xaml.Input;
-using Microsoft.UI.Xaml.Media;
-using Microsoft.UI.Xaml.Navigation;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices.WindowsRuntime;
-using Windows.Foundation;
-using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
diff --git a/Lattice.IDE/Controls/SolutionExplorerView.xaml.cs b/Lattice.IDE/Controls/SolutionExplorerView.xaml.cs
index ed3a3e8..21dd5c8 100644
--- a/Lattice.IDE/Controls/SolutionExplorerView.xaml.cs
+++ b/Lattice.IDE/Controls/SolutionExplorerView.xaml.cs
@@ -1,17 +1,4 @@
-using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Controls.Primitives;
-using Microsoft.UI.Xaml.Data;
-using Microsoft.UI.Xaml.Input;
-using Microsoft.UI.Xaml.Media;
-using Microsoft.UI.Xaml.Navigation;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices.WindowsRuntime;
-using Windows.Foundation;
-using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
diff --git a/Lattice.UI.Docking.WinUI/Services/WinUIDragDropService.cs b/Lattice.UI.Docking.WinUI/Services/WinUIDragDropService.cs
index 225a4c9..3315100 100644
--- a/Lattice.UI.Docking.WinUI/Services/WinUIDragDropService.cs
+++ b/Lattice.UI.Docking.WinUI/Services/WinUIDragDropService.cs
@@ -1,5 +1,4 @@
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
+using Lattice.Core.Geometry;
using Lattice.UI.Docking.Abstractions;
using Lattice.UI.Docking.Models;
using Lattice.UI.Docking.Services;
diff --git a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDragSourceBehavior.cs b/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDragSourceBehavior.cs
index d1f97e6..6a35e8a 100644
--- a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDragSourceBehavior.cs
+++ b/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDragSourceBehavior.cs
@@ -1,7 +1,8 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Models;
+using Lattice.Core.DragDrop.Models;
using Lattice.Core.DragDrop.Services;
using Lattice.Core.Geometry;
+using Lattice.UI.DragDrop.Abstractions;
+using Lattice.UI.DragDrop.Behaviors;
using Lattice.UI.DragDrop.WinUI.Services;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
@@ -13,29 +14,54 @@ namespace Lattice.UI.DragDrop.WinUI.Behaviors;
///
/// Реализация поведения источника перетаскивания для элементов WinUI.
+/// Наследуется от для использования
+/// общей логики управления операциями перетаскивания и интеграции с системой .
///
///
///
-/// Этот класс обрабатывает события мыши/тач и преобразует их в операции перетаскивания.
-/// Он реализует интерфейс для интеграции с системой перетаскивания.
+/// Этот класс предоставляет конкретную реализацию для платформы WinUI, обрабатывая события
+/// указателя (мышь, тач, перо) и преобразуя их в операции перетаскивания через центральный
+/// сервис .
///
///
-/// Поведение автоматически отслеживает начало перемещения мыши, проверяет порог
-/// перетаскивания и инициирует операцию через .
+/// Основные функции:
+///
+/// - Обработка событий PointerPressed, PointerMoved, PointerReleased
+/// - Автоматическое отслеживание порога начала перетаскивания
+/// - Создание информации о перетаскивании на основе данных элемента
+/// - Интеграция с визуальной обратной связью через
+/// - Преобразование координат между локальной системой элемента и экранными координатами
+///
///
+///
+/// Для использования необходимо:
+///
+/// - Создать экземпляр поведения через фабрику
+/// - Прикрепить к элементу с помощью метода
+/// - Указать данные для перетаскивания (опционально, по умолчанию используется DataContext)
+///
+///
+///
+///
+/// // Создание поведения
+/// var behavior = WinUIDragDropFactory.CreateDragSourceBehavior(dragDropService, host);
+///
+/// // Прикрепление к элементу
+/// behavior.Attach(myElement, myData);
+///
+/// // Или через attached properties
+/// <Border x:Name="DragElement"
+/// local:DragDropProperties.IsDragSource="True"
+/// local:DragDropProperties.DragData="{Binding MyData}" />
+///
+///
///
-public sealed class WinUIDragSourceBehavior : IDragSource
+public sealed class WinUIDragSourceBehavior : DragSourceBehaviorBase
{
#region Поля
- private readonly IDragDropService _dragDropService;
- private readonly WinUIDragDropHost _host;
-
- private FrameworkElement? _element;
+ private readonly IDragDropHost _host;
private object? _dragData;
- private Point _dragStartPosition;
- private bool _isDragging;
- private CancellationTokenSource? _cancellationTokenSource;
#endregion
@@ -44,14 +70,27 @@ public sealed class WinUIDragSourceBehavior : IDragSource
///
/// Инициализирует новый экземпляр класса .
///
- /// Сервис для управления операциями перетаскивания.
- /// Хост для отображения визуальных элементов.
+ ///
+ /// Сервис управления операциями перетаскивания. Используется для координации
+ /// между источниками и целями перетаскивания.
+ ///
+ ///
+ /// Хост для управления визуальными элементами перетаскивания. Обеспечивает
+ /// отображение визуальной обратной связи во время операции.
+ ///
///
- /// Выбрасывается, если любой из параметров равен null.
+ /// Выбрасывается, если или
+ /// равны null.
///
- public WinUIDragSourceBehavior(IDragDropService dragDropService, WinUIDragDropHost host)
+ ///
+ /// Конструктор инициализирует базовый класс
+ /// и сохраняет ссылки на необходимые сервисы для последующего использования.
+ ///
+ public WinUIDragSourceBehavior(
+ Core.DragDrop.Services.IDragDropService dragDropService,
+ WinUIDragDropHost host)
+ : base(dragDropService)
{
- _dragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
_host = host ?? throw new ArgumentNullException(nameof(host));
}
@@ -60,207 +99,202 @@ public sealed class WinUIDragSourceBehavior : IDragSource
#region Публичные методы
///
- /// Прикрепляет поведение к указанному элементу.
+ /// Прикрепляет поведение к указанному элементу WinUI.
///
- /// Элемент, к которому прикрепляется поведение.
- /// Данные для перетаскивания. Если не указано, используются
- /// DataContext или Tag элемента.
+ ///
+ /// Элемент , который должен стать источником перетаскивания.
+ /// Не может быть null.
+ ///
+ ///
+ /// Данные, которые будут перетаскиваться. Может быть null.
+ /// Если не указано, используется или
+ /// элемента.
+ ///
///
/// Выбрасывается, если равен null.
///
///
- /// После вызова этого метода элемент начинает обрабатывать события перетаскивания.
+ ///
+ /// После вызова этого метода:
+ ///
+ /// - Элементу автоматически подписываются обработчики событий указателя
+ /// - Поведение начинает отслеживать взаимодействия с элементом
+ /// - При превышении порога перетаскивания инициируется операция через
+ ///
+ ///
+ ///
+ /// Для открепления поведения используйте метод .
+ ///
///
public void Attach(FrameworkElement element, object? dragData = null)
{
- Detach();
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
- _element = element ?? throw new ArgumentNullException(nameof(element));
_dragData = dragData ?? element.DataContext ?? element.Tag;
-
- SubscribeToEvents();
+ AssociatedElement = element;
}
///
- /// Открепляет поведение от элемента.
+ /// Открепляет поведение от текущего элемента.
///
- public void Detach()
+ ///
+ ///
+ /// Этот метод выполняет следующие действия:
+ ///
+ /// - Отписывается от всех событий элемента
+ /// - Сбрасывает внутреннее состояние
+ /// - Освобождает ссылки на связанные объекты
+ ///
+ ///
+ ///
+ /// Если в момент вызова активна операция перетаскивания, она будет автоматически отменена.
+ ///
+ ///
+ /// После вызова этого метода поведение может быть повторно прикреплено к другому элементу.
+ ///
+ ///
+ public new void Detach()
{
- if (_element == null) return;
-
- UnsubscribeFromEvents();
-
- if (_isDragging)
- {
- _dragDropService.CancelDragAsync().ConfigureAwait(false);
- }
-
- _element = null;
+ base.Detach();
_dragData = null;
- _isDragging = false;
-
- _cancellationTokenSource?.Dispose();
- _cancellationTokenSource = null;
}
#endregion
- #region Обработчики событий
+ #region Реализация абстрактных методов DragSourceBehaviorBase
- private void SubscribeToEvents()
+ ///
+ protected override void SubscribeToEvents(FrameworkElement element)
{
- if (_element == null) return;
+ if (element == null) return;
- _element.PointerPressed += OnPointerPressed;
- _element.PointerMoved += OnPointerMoved;
- _element.PointerReleased += OnPointerReleased;
- _element.PointerCanceled += OnPointerCanceled;
- _element.PointerCaptureLost += OnPointerCaptureLost;
+ element.PointerPressed += OnPointerPressed;
+ element.PointerMoved += OnPointerMoved;
+ element.PointerReleased += OnPointerReleased;
+ element.PointerCanceled += OnPointerCanceled;
+ element.PointerCaptureLost += OnPointerCaptureLost;
}
- private void UnsubscribeFromEvents()
+ ///
+ protected override void UnsubscribeFromEvents(FrameworkElement element)
{
- if (_element == null) return;
+ if (element == null) return;
- _element.PointerPressed -= OnPointerPressed;
- _element.PointerMoved -= OnPointerMoved;
- _element.PointerReleased -= OnPointerReleased;
- _element.PointerCanceled -= OnPointerCanceled;
- _element.PointerCaptureLost -= OnPointerCaptureLost;
+ element.PointerPressed -= OnPointerPressed;
+ element.PointerMoved -= OnPointerMoved;
+ element.PointerReleased -= OnPointerReleased;
+ element.PointerCanceled -= OnPointerCanceled;
+ element.PointerCaptureLost -= OnPointerCaptureLost;
}
- private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
+ ///
+ protected override Point ConvertToScreenCoordinates(Point point)
{
- if (_element == null || _isDragging) return;
-
- var point = e.GetCurrentPoint(_element);
- _dragStartPosition = new Point(point.Position.X, point.Position.Y);
-
- _cancellationTokenSource?.Dispose();
- _cancellationTokenSource = new CancellationTokenSource();
- }
-
- private async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
- {
- if (_element == null || _isDragging || _cancellationTokenSource?.IsCancellationRequested == true)
- return;
-
- var point = e.GetCurrentPoint(_element);
- var currentPosition = new Point(point.Position.X, point.Position.Y);
-
- var distance = CalculateDistance(_dragStartPosition, currentPosition);
- if (distance > _dragDropService.DragStartThreshold)
- {
- await StartDragAsync(currentPosition);
- }
- }
-
- private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
- {
- ResetState();
- }
-
- private void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
- {
- ResetState();
- }
-
- private void OnPointerCaptureLost(object sender, PointerRoutedEventArgs e)
- {
- ResetState();
- }
-
- private double CalculateDistance(Point p1, Point p2)
- {
- var dx = p2.X - p1.X;
- var dy = p2.Y - p1.Y;
- return Math.Sqrt(dx * dx + dy * dy);
- }
-
- ///
- /// Преобразует координаты элемента в экранные координаты.
- ///
- /// Точка в координатах элемента.
- /// Точка в экранных координатах.
- private Point ConvertToScreenCoordinates(Point point)
- {
- if (_element == null) return point;
-
- try
- {
- var transform = _element.TransformToVisual(Window.Current.Content);
- var screenPoint = transform.TransformPoint(new Windows.Foundation.Point(point.X, point.Y));
- return new Point(screenPoint.X, screenPoint.Y);
- }
- catch
- {
+ if (AssociatedElement == null)
return point;
- }
- }
- private async Task StartDragAsync(Point position)
- {
- if (_element == null || _dragData == null || _isDragging) return;
-
- try
- {
- var screenPosition = ConvertToScreenCoordinates(position);
- var started = await _dragDropService.StartDragAsync(this, screenPosition);
- _isDragging = started;
- }
- catch
- {
- ResetState();
- }
- }
-
- private void ResetState()
- {
- _isDragging = false;
- _dragStartPosition = default;
-
- _cancellationTokenSource?.Cancel();
- _cancellationTokenSource?.Dispose();
- _cancellationTokenSource = null;
+ return WinUIWindowHelper.ConvertToScreenCoordinates(AssociatedElement, point);
}
#endregion
- #region IDragSource Implementation
+ #region Реализация интерфейса IDragSource
- public async Task TryStartDragAsync(Point startPosition, CancellationToken cancellationToken = default)
+ ///
+ public override async Task TryStartDragAsync(
+ Point startPosition,
+ CancellationToken cancellationToken = default)
{
- if (_element == null || _dragData == null)
+ if (AssociatedElement == null || _dragData == null)
return null;
try
{
+ // Создаем информацию о перетаскивании
var dragInfo = new DragInfo(
data: _dragData,
allowedEffects: Core.DragDrop.Enums.DragDropEffects.Copy |
- Core.DragDrop.Enums.DragDropEffects.Move,
+ Core.DragDrop.Enums.DragDropEffects.Move |
+ Core.DragDrop.Enums.DragDropEffects.Link,
startPosition: startPosition,
source: this
);
- return dragInfo;
+ // Добавляем дополнительные параметры
+ dragInfo.SetParameter("SourceElement", AssociatedElement);
+ dragInfo.SetParameter("SourceType", AssociatedElement.GetType().Name);
+
+ return await Task.FromResult(dragInfo);
}
- catch
+ catch (Exception ex)
{
+ // Логирование ошибки создания информации о перетаскивании
+ System.Diagnostics.Debug.WriteLine(
+ $"Ошибка создания DragInfo: {ex.Message}");
return null;
}
}
- public Task OnDragCompletedAsync(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects, CancellationToken cancellationToken = default)
+ #endregion
+
+ #region Обработчики событий WinUI
+
+ private async void OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
- _isDragging = false;
- return Task.CompletedTask;
+ if (AssociatedElement == null) return;
+
+ var point = e.GetCurrentPoint(AssociatedElement);
+ var position = new Point(point.Position.X, point.Position.Y);
+
+ await OnInteractionStarted(position);
}
- public Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
+ private async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
- _isDragging = false;
- return Task.CompletedTask;
+ if (AssociatedElement == null) return;
+
+ var point = e.GetCurrentPoint(AssociatedElement);
+ var position = new Point(point.Position.X, point.Position.Y);
+
+ await OnInteractionMoved(position);
+ }
+
+ private async void OnPointerReleased(object sender, PointerRoutedEventArgs e)
+ {
+ await OnInteractionEnded();
+ }
+
+ private async void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
+ {
+ await OnInteractionCancelled();
+ }
+
+ private async void OnPointerCaptureLost(object sender, PointerRoutedEventArgs e)
+ {
+ await OnInteractionCancelled();
+ }
+
+ #endregion
+
+ #region Переопределение виртуальных методов
+
+ ///
+ protected override void OnDragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
+ {
+ base.OnDragCompleted(dragInfo, effects);
+
+ // Дополнительная логика для WinUI может быть добавлена здесь
+ // Например, обновление состояния элемента после успешного перетаскивания
+ }
+
+ ///
+ protected override void OnDragCancelled(DragInfo dragInfo)
+ {
+ base.OnDragCancelled(dragInfo);
+
+ // Дополнительная логика для WinUI может быть добавлена здесь
+ // Например, восстановление визуального состояния элемента после отмены
}
#endregion
diff --git a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs b/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs
index 23bb6f7..902504e 100644
--- a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs
+++ b/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs
@@ -1,9 +1,11 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Models;
+using Lattice.Core.DragDrop.Models;
using Lattice.Core.Geometry;
-using Lattice.UI.DragDrop.WinUI.Services;
+using Lattice.UI.DragDrop.Abstractions;
+using Lattice.UI.DragDrop.Behaviors;
using Microsoft.UI.Xaml;
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -11,27 +13,52 @@ namespace Lattice.UI.DragDrop.WinUI.Behaviors;
///
/// Реализация поведения цели сброса для элементов WinUI.
+/// Наследуется от для использования
+/// общей логики регистрации целей и обработки операций сброса.
///
///
///
-/// Этот класс обрабатывает события перетаскивания WinUI и преобразует их в вызовы
-/// методов интерфейса .
+/// Этот класс предоставляет конкретную реализацию для платформы WinUI, обрабатывая события
+/// перетаскивания WinUI и преобразуя их в вызовы методов интерфейса .
///
///
-/// Поведение автоматически регистрирует элемент в системе перетаскивания,
-/// обновляет его границы при изменении размера и обрабатывает все этапы операции сброса.
+/// Основные функции:
+///
+/// - Автоматическая регистрация в при прикреплении к элементу
+/// - Обработка событий DragEnter, DragOver, DragLeave, Drop
+/// - Автоматическое обновление границ элемента при изменении размера или позиции
+/// - Поддержка фильтрации принимаемых типов данных
+/// - Интеграция с визуальной обратной связью
+///
///
+///
+/// Для использования необходимо:
+///
+/// - Создать экземпляр поведения через фабрику
+/// - Прикрепить к элементу с помощью метода
+/// - Настроить фильтры принимаемых данных (опционально)
+///
+///
+///
+///
+/// // Создание поведения с фильтрацией типов
+/// var behavior = WinUIDragDropFactory.CreateDropTargetBehavior(dragDropService, host);
+/// behavior.AcceptTypes(typeof(MyDataModel), typeof(string));
+/// behavior.Attach(myDropArea);
+///
+/// // Или через attached properties
+/// <Border x:Name="DropArea"
+/// local:DragDropProperties.IsDropTarget="True" />
+///
+///
///
-public sealed class WinUIDropTargetBehavior : IDropTarget
+public sealed class WinUIDropTargetBehavior : DropTargetBehaviorBase
{
#region Поля
- private readonly Lattice.Core.DragDrop.Services.IDragDropService _dragDropService;
- private readonly WinUIDragDropHost _host;
-
- private FrameworkElement? _element;
- private string? _registrationId;
- private Rect _currentBounds;
+ private readonly IDragDropHost _host;
+ private readonly List _acceptedTypes = new();
+ private readonly HashSet _acceptedFormats = new();
#endregion
@@ -40,14 +67,28 @@ public sealed class WinUIDropTargetBehavior : IDropTarget
///
/// Инициализирует новый экземпляр класса .
///
- /// Сервис для управления операциями перетаскивания.
- /// Хост для отображения визуальных элементов.
+ ///
+ /// Сервис управления операциями перетаскивания. Используется для регистрации
+ /// цели и координации операций сброса.
+ ///
+ ///
+ /// Хост для управления визуальной обратной связью. Обеспечивает отображение
+ /// индикаторов возможности сброса.
+ ///
///
- /// Выбрасывается, если любой из параметров равен null.
+ /// Выбрасывается, если или
+ /// равны null.
///
- public WinUIDropTargetBehavior(Lattice.Core.DragDrop.Services.IDragDropService dragDropService, WinUIDragDropHost host)
+ ///
+ /// Конструктор инициализирует базовый класс и сохраняет ссылки на сервисы.
+ /// По умолчанию цель принимает все типы данных. Для настройки фильтрации
+ /// используйте методы и .
+ ///
+ public WinUIDropTargetBehavior(
+ Core.DragDrop.Services.IDragDropService dragDropService,
+ IDragDropHost host)
+ : base(dragDropService)
{
- _dragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
_host = host ?? throw new ArgumentNullException(nameof(host));
}
@@ -56,106 +97,301 @@ public sealed class WinUIDropTargetBehavior : IDropTarget
#region Публичные методы
///
- /// Прикрепляет поведение к указанному элементу.
+ /// Прикрепляет поведение к указанному элементу WinUI.
///
- /// Элемент, к которому прикрепляется поведение.
+ ///
+ /// Элемент , который должен стать целью сброса.
+ /// Не может быть null.
+ ///
///
/// Выбрасывается, если равен null.
///
///
+ ///
/// После вызова этого метода:
- /// 1. Элементу устанавливается = true
- /// 2. Поведение подписывается на события перетаскивания
- /// 3. Элемент регистрируется в системе перетаскивания
+ ///
+ /// - Элементу устанавливается свойство = true
+ /// - Поведение подписывается на события перетаскивания WinUI
+ /// - Элемент регистрируется в системе перетаскивания с текущими границами
+ /// - Начинается отслеживание изменений размера и позиции элемента
+ ///
+ ///
+ ///
+ /// Для открепления поведения используйте метод .
+ ///
///
public void Attach(FrameworkElement element)
{
- Detach();
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
- _element = element ?? throw new ArgumentNullException(nameof(element));
element.AllowDrop = true;
-
- SubscribeToEvents();
- UpdateBounds();
- RegisterToService();
+ AssociatedElement = element;
}
///
- /// Открепляет поведение от элемента.
+ /// Настраивает поведение для приема только указанных типов данных.
///
- public void Detach()
+ ///
+ /// Типы данных, которые может принимать цель. Если пусто, принимаются все типы.
+ ///
+ ///
+ ///
+ /// Этот метод позволяет ограничить типы данных, которые могут быть сброшены на цель.
+ /// Проверка выполняется в методе путем сравнения
+ /// типа сбрасываемых данных с указанными типами.
+ ///
+ ///
+ /// Если метод не вызывался или передан пустой список, цель будет принимать данные любого типа.
+ ///
+ ///
+ ///
+ /// // Принимать только строки и объекты MyModel
+ /// behavior.AcceptTypes(typeof(string), typeof(MyModel));
+ ///
+ ///
+ ///
+ public void AcceptTypes(params Type[] types)
{
- if (_element == null) return;
+ _acceptedTypes.Clear();
+ if (types != null && types.Length > 0)
+ {
+ _acceptedTypes.AddRange(types);
+ }
+ }
- UnsubscribeFromEvents();
- UnregisterFromService();
+ ///
+ /// Настраивает поведение для приема только указанных форматов данных.
+ ///
+ ///
+ /// Форматы данных (например, "Text", "Bitmap", "FileDrop"), которые может принимать цель.
+ /// Если пусто, формат не проверяется.
+ ///
+ ///
+ ///
+ /// Этот метод позволяет ограничить форматы данных, которые могут быть сброшены на цель.
+ /// Актуально для межпроцессного перетаскивания или работы с системными форматами.
+ ///
+ ///
+ /// Если метод не вызывался или передан пустой список, проверка формата не выполняется.
+ ///
+ ///
+ public void AcceptFormats(params string[] formats)
+ {
+ _acceptedFormats.Clear();
+ if (formats != null && formats.Length > 0)
+ {
+ foreach (var format in formats)
+ {
+ _acceptedFormats.Add(format);
+ }
+ }
+ }
- _element.AllowDrop = false;
- _element = null;
- _currentBounds = Rect.Empty;
+ ///
+ /// Открепляет поведение от текущего элемента.
+ ///
+ ///
+ ///
+ /// Этот метод выполняет следующие действия:
+ ///
+ /// - Отписывается от всех событий элемента
+ /// - Отменяет регистрацию цели в системе перетаскивания
+ /// - Сбрасывает свойство = false
+ /// - Освобождает ссылки на связанные объекты
+ ///
+ ///
+ ///
+ /// После вызова этого метода поведение может быть повторно прикреплено к другому элементу.
+ ///
+ ///
+ public new void Detach()
+ {
+ if (AssociatedElement != null)
+ {
+ AssociatedElement.AllowDrop = false;
+ }
+ base.Detach();
}
#endregion
- #region Обработчики событий
+ #region Реализация абстрактных методов DropTargetBehaviorBase
- private void SubscribeToEvents()
+ ///
+ protected override void SubscribeToEvents(FrameworkElement element)
{
- if (_element == null) return;
+ if (element == null) return;
- _element.DragEnter += OnDragEnter;
- _element.DragOver += OnDragOver;
- _element.DragLeave += OnDragLeave;
- _element.Drop += OnDrop;
- _element.SizeChanged += OnSizeChanged;
+ element.DragEnter += OnDragEnter;
+ element.DragOver += OnDragOver;
+ element.DragLeave += OnDragLeave;
+ element.Drop += OnDrop;
+ element.SizeChanged += OnSizeChanged;
+ element.LayoutUpdated += OnLayoutUpdated;
}
- private void UnsubscribeFromEvents()
+ ///
+ protected override void UnsubscribeFromEvents(FrameworkElement element)
{
- if (_element == null) return;
+ if (element == null) return;
- _element.DragEnter -= OnDragEnter;
- _element.DragOver -= OnDragOver;
- _element.DragLeave -= OnDragLeave;
- _element.Drop -= OnDrop;
- _element.SizeChanged -= OnSizeChanged;
+ element.DragEnter -= OnDragEnter;
+ element.DragOver -= OnDragOver;
+ element.DragLeave -= OnDragLeave;
+ element.Drop -= OnDrop;
+ element.SizeChanged -= OnSizeChanged;
+ element.LayoutUpdated -= OnLayoutUpdated;
}
- private async void OnDragEnter(object sender, DragEventArgs e)
+ ///
+ protected override Rect GetScreenBounds(FrameworkElement element)
{
- if (_element == null) return;
+ if (element == null || !element.IsLoaded)
+ return Rect.Empty;
try
{
- var position = e.GetPosition(_element);
+ var window = Window.Current;
+ if (window?.Content == null)
+ return Rect.Empty;
+
+ // Преобразуем локальные координаты элемента в координаты окна
+ var transform = element.TransformToVisual(window.Content);
+ var position = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
+
+ return new Rect(
+ position.X,
+ position.Y,
+ element.ActualWidth,
+ element.ActualHeight
+ );
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(
+ $"Ошибка получения границ элемента: {ex.Message}");
+ return Rect.Empty;
+ }
+ }
+
+ #endregion
+
+ #region Реализация интерфейса IDropTarget
+
+ ///
+ public override async Task CanAcceptDropAsync(
+ DropInfo dropInfo,
+ CancellationToken cancellationToken = default)
+ {
+ // Проверяем, есть ли данные для сброса
+ if (dropInfo.Data == null)
+ return false;
+
+ // Проверяем фильтр по типам
+ if (_acceptedTypes.Count > 0)
+ {
+ var dataType = dropInfo.Data.GetType();
+ if (!_acceptedTypes.Any(t => t.IsAssignableFrom(dataType)))
+ {
+ return false;
+ }
+ }
+
+ // Проверяем фильтр по форматам (если данные предоставляют информацию о формате)
+ if (_acceptedFormats.Count > 0 && dropInfo.Data is Windows.ApplicationModel.DataTransfer.DataPackageView dataView)
+ {
+ var availableFormats = dataView.AvailableFormats;
+ if (!_acceptedFormats.Any(f => availableFormats.Contains(f)))
+ {
+ return false;
+ }
+ }
+
+ // Дополнительная проверка может быть добавлена в производных классах
+ return await Task.FromResult(true);
+ }
+
+ ///
+ public override async Task OnDragOverAsync(
+ DropInfo dropInfo,
+ CancellationToken cancellationToken = default)
+ {
+ await base.OnDragOverAsync(dropInfo, cancellationToken);
+
+ // Дополнительная логика для WinUI может быть добавлена здесь
+ // Например, обновление визуальной обратной связи через хост
+ }
+
+ ///
+ public override async Task OnDropAsync(
+ DropInfo dropInfo,
+ CancellationToken cancellationToken = default)
+ {
+ // Базовая реализация вызывает CanAcceptDropAsync и помечает как обработанное
+ if (await CanAcceptDropAsync(dropInfo, cancellationToken))
+ {
+ dropInfo.MarkAsHandled();
+
+ // Здесь может быть добавлена логика обработки сброшенных данных
+ // Например, вызов события или обновление модели данных
+ }
+ }
+
+ ///
+ public override async Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
+ {
+ await base.OnDragLeaveAsync(cancellationToken);
+
+ // Дополнительная логика для WinUI может быть добавлена здесь
+ // Например, скрытие визуальной обратной связи
+ }
+
+ #endregion
+
+ #region Обработчики событий WinUI
+
+ private async void OnDragEnter(object sender, DragEventArgs e)
+ {
+ if (AssociatedElement == null) return;
+
+ try
+ {
+ var position = e.GetPosition(AssociatedElement);
var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
if (await CanAcceptDropAsync(dropInfo))
{
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
+ e.Handled = true;
}
}
- catch { }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"Ошибка в OnDragEnter: {ex.Message}");
+ }
}
private async void OnDragOver(object sender, DragEventArgs e)
{
- if (_element == null) return;
+ if (AssociatedElement == null) return;
try
{
- var position = e.GetPosition(_element);
+ var position = e.GetPosition(AssociatedElement);
var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
if (await CanAcceptDropAsync(dropInfo))
{
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
await OnDragOverAsync(dropInfo);
+ e.Handled = true;
}
-
- e.Handled = true;
}
- catch { }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"Ошибка в OnDragOver: {ex.Message}");
+ }
}
private async void OnDragLeave(object sender, DragEventArgs e)
@@ -165,131 +401,114 @@ public sealed class WinUIDropTargetBehavior : IDropTarget
private async void OnDrop(object sender, DragEventArgs e)
{
- if (_element == null) return;
+ if (AssociatedElement == null) return;
try
{
- var position = e.GetPosition(_element);
+ var position = e.GetPosition(AssociatedElement);
var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
if (await CanAcceptDropAsync(dropInfo))
{
await OnDropAsync(dropInfo);
- dropInfo.MarkAsHandled();
e.Handled = true;
}
}
- catch { }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"Ошибка в OnDrop: {ex.Message}");
+ }
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
- UpdateBounds();
+ OnElementLayoutChanged();
}
+ private void OnLayoutUpdated(object sender, object e)
+ {
+ OnElementLayoutChanged();
+ }
+
+ #endregion
+
+ #region Вспомогательные методы
+
+ ///
+ /// Создает объект на основе события перетаскивания WinUI.
+ ///
+ /// Аргументы события перетаскивания WinUI.
+ /// Локальная позиция курсора относительно элемента.
+ ///
+ /// Экземпляр , содержащий информацию о потенциальном сбросе.
+ ///
+ ///
+ ///
+ /// Этот метод извлекает данные из события перетаскивания и преобразует их
+ /// в формат, понятный системе .
+ ///
+ ///
+ /// Поддерживаются как пользовательские данные (через свойство "DragData"),
+ /// так и стандартные форматы данных WinUI.
+ ///
+ ///
private DropInfo CreateDropInfo(DragEventArgs e, Point position)
{
object? data = null;
+ // Пытаемся получить пользовательские данные
if (e.DataView.Properties.TryGetValue("DragData", out var dragData))
{
data = dragData;
}
+ // Или получаем данные из DataPackage
+ else if (e.DataView.Contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.Text))
+ {
+ // Для текстовых данных можем установить асинхронную загрузку
+ data = new AsyncDataProvider(async () =>
+ {
+ return await e.DataView.GetTextAsync();
+ });
+ }
+
+ // Определяем разрешенные эффекты на основе модификаторов клавиатуры
+ var allowedEffects = Core.DragDrop.Enums.DragDropEffects.None;
+ if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy))
+ allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Copy;
+ if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move))
+ allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Move;
+ if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Link))
+ allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Link;
return new DropInfo(
data: data,
position: position,
- allowedEffects: Core.DragDrop.Enums.DragDropEffects.Copy |
- Core.DragDrop.Enums.DragDropEffects.Move,
- target: _element
+ allowedEffects: allowedEffects,
+ target: this
);
}
- ///
- /// Получает границы элемента в экранных координатах.
- ///
- /// Прямоугольник с границами элемента или , если
- /// элемент не загружен или не может быть преобразован.
- private Rect GetScreenBounds()
- {
- if (_element == null || !_element.IsLoaded)
- return Rect.Empty;
-
- try
- {
- var transform = _element.TransformToVisual(Window.Current.Content);
- var position = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
-
- return new Rect(
- position.X,
- position.Y,
- _element.ActualWidth,
- _element.ActualHeight
- );
- }
- catch
- {
- return Rect.Empty;
- }
- }
-
- private void UpdateBounds()
- {
- if (_element == null) return;
-
- _currentBounds = GetScreenBounds();
-
- if (_registrationId != null && _currentBounds != Rect.Empty)
- {
- _dragDropService.UpdateDropTargetBounds(_registrationId, _currentBounds);
- }
- }
-
- private void RegisterToService()
- {
- if (_element == null) return;
-
- _currentBounds = GetScreenBounds();
-
- if (_currentBounds != Rect.Empty && _registrationId == null)
- {
- _registrationId = _dragDropService.RegisterDropTarget(this, _currentBounds);
- }
- }
-
- private void UnregisterFromService()
- {
- if (_registrationId != null)
- {
- _dragDropService.UnregisterDropTarget(_registrationId);
- _registrationId = null;
- }
- }
-
#endregion
+}
- #region IDropTarget Implementation
+///
+/// Предоставляет асинхронный доступ к данным перетаскивания.
+///
+///
+/// Этот класс используется для отложенной загрузки данных перетаскивания,
+/// что особенно важно для больших данных или данных, требующих обработки.
+///
+internal class AsyncDataProvider
+{
+ private readonly Func> _dataLoader;
- public Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct = default)
+ public AsyncDataProvider(Func> dataLoader)
{
- return Task.FromResult(true); // Принимаем все по умолчанию
+ _dataLoader = dataLoader;
}
- public Task OnDragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
+ public async Task