# Lattice.UI.DragDrop UI-слой библиотеки перетаскивания (drag-and-drop) для платформ .NET. ## 📋 Обзор Lattice.UI.DragDrop предоставляет абстракции и базовые реализации для интеграции системы перетаскивания Lattice.Core.DragDrop с различными UI-фреймворками (WPF, Avalonia, MAUI и т.д.). ### ✨ Основные возможности - ✅ **Кросс-платформенные абстракции** - единый API для всех UI-фреймворков - ✅ **Готовые базовые классы** для быстрой реализации поведения перетаскивания - ✅ **Поддержка визуальной обратной связи** через плагинную архитектуру - ✅ **Интеграция с DI-контейнерами** через Microsoft.Extensions.DependencyInjection - ✅ **Безопасные реализации по умолчанию** для упрощения разработки - ✅ **Расширяемость** через наследование и переопределение ## 🏗️ Архитектура ### Основные компоненты #### 1. **IDragDropHost** Абстракция для управления визуальными элементами перетаскивания на уровне платформы. #### 2. **IDragVisualProvider** Поставщик визуального представления для перетаскиваемых элементов. #### 3. **IDropVisualAdorner** Элемент визуальной обратной связи при наведении на цель сброса. #### 4. **DragSourceBehaviorBase\** Базовый класс для реализации поведения источника перетаскивания. #### 5. **DropTargetBehaviorBase\** Базовый класс для реализации поведения цели сброса. ## 🚀 Быстрый старт ### 1. Установка ```csharp // В методе ConfigureServices вашего приложения services.AddLatticeDragDrop(); ``` ### 2. Регистрация платформенных реализаций ```csharp // Для WPF services.AddDragVisualProvider(); services.AddDropVisualAdorner(); services.AddDragDropHost(); // Для Avalonia services.AddDragVisualProvider(); services.AddDropVisualAdorner(); services.AddDragDropHost(); ``` ### 3. Создание поведения источника перетаскивания ```csharp public class MyDragSourceBehavior : DragSourceBehaviorBase { public MyDragSourceBehavior(IServiceProvider serviceProvider) : base(serviceProvider) { } protected override void SubscribeToEvents(Control element) { element.MouseDown += OnMouseDown; element.MouseMove += OnMouseMove; element.MouseUp += OnMouseUp; } protected override void UnsubscribeFromEvents(Control element) { element.MouseDown -= OnMouseDown; element.MouseMove -= OnMouseMove; element.MouseUp -= OnMouseUp; } public override async Task TryStartDragAsync(Point startPosition, CancellationToken ct) { // Проверяем, можно ли начать перетаскивание if (!CanDrag()) return null; // Создаем информацию о перетаскивании return new DragInfo( data: GetDragData(), allowedEffects: DragDropEffects.Copy | DragDropEffects.Move, startPosition: startPosition, source: this ); } // ... остальная реализация } ``` ### 4. Создание поведения цели сброса ```csharp public class MyDropTargetBehavior : DropTargetBehaviorBase { public MyDropTargetBehavior(IServiceProvider serviceProvider) : base(serviceProvider) { } protected override void SubscribeToEvents(Panel element) { element.SizeChanged += OnSizeChanged; element.LayoutUpdated += OnLayoutUpdated; } protected override void UnsubscribeFromEvents(Panel element) { element.SizeChanged -= OnSizeChanged; element.LayoutUpdated -= OnLayoutUpdated; } public override async Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct) { // Проверяем тип данных if (dropInfo.Data is not MyDataType data) return false; // Проверяем бизнес-правила return await ValidateDropAsync(data, ct); } public override async Task OnDropAsync(DropInfo dropInfo, CancellationToken ct) { var data = (MyDataType)dropInfo.Data; await ProcessDropAsync(data, ct); dropInfo.MarkAsHandled(); } // ... остальная реализация } ``` ## 🔌 Интеграция с UI-фреймворками ### WPF ```csharp public static class WpfDragDropExtensions { public static IServiceCollection AddWpfDragDrop(this IServiceCollection services) { return services .AddLatticeDragDrop() .AddDragVisualProvider() .AddDropVisualAdorner() .AddDragDropHost(); } } ``` ### Avalonia ```csharp public static class AvaloniaDragDropExtensions { public static IServiceCollection AddAvaloniaDragDrop(this IServiceCollection services) { return services .AddLatticeDragDrop() .AddDragVisualProvider() .AddDropVisualAdorner() .AddDragDropHost(); } } ``` ## 🎨 Визуальная обратная связь ### Создание кастомного визуального представления ```csharp public class CustomDragVisualProvider : IDragVisualProvider { public object CreateDragVisual(DragInfo dragInfo, Point initialPosition) { var border = new Border { Background = new SolidColorBrush(Colors.LightBlue), BorderBrush = new SolidColorBrush(Colors.Blue), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(4), Padding = new Thickness(8), Child = new TextBlock { Text = $"Dragging: {dragInfo.Data}", Foreground = new SolidColorBrush(Colors.Black) }, Opacity = 0.8 }; return border; } public void UpdateDragVisualPosition(object dragVisual, Point position) { if (dragVisual is FrameworkElement element) { element.SetValue(Canvas.LeftProperty, position.X); element.SetValue(Canvas.TopProperty, position.Y); } } public void ReleaseDragVisual(object dragVisual) { if (dragVisual is IDisposable disposable) disposable.Dispose(); } } ``` ### Создание кастомной обратной связи ```csharp public class CustomDropVisualAdorner : IDropVisualAdorner { private Border? _adorner; public void Show(DropInfo dropInfo, Rect targetBounds) { _adorner = new Border { Background = new SolidColorBrush(Colors.Transparent), BorderBrush = new SolidColorBrush(Colors.Green), BorderThickness = new Thickness(2), CornerRadius = new CornerRadius(4) }; // Установка позиции и размера Canvas.SetLeft(_adorner, targetBounds.X); Canvas.SetTop(_adorner, targetBounds.Y); _adorner.Width = targetBounds.Width; _adorner.Height = targetBounds.Height; // Добавление в визуальное дерево AdornerLayer.GetAdornerLayer(targetElement)?.Add(_adorner); } public void Update(DropInfo dropInfo) { if (_adorner != null) { // Обновление стиля в зависимости от эффекта var brush = dropInfo.SuggestedEffects.HasFlag(DragDropEffects.Move) ? Colors.Green : Colors.Blue; _adorner.BorderBrush = new SolidColorBrush(brush); } } public void Hide() { if (_adorner != null) { var layer = AdornerLayer.GetAdornerLayer(_adorner); layer?.Remove(_adorner); _adorner = null; } } } ``` ## ⚙️ Конфигурация ### Настройка сервисов ```csharp public void ConfigureServices(IServiceCollection services) { // Базовая регистрация services.AddLatticeDragDrop(); // Платформенные реализации services.AddDragVisualProvider(); services.AddDropVisualAdorner(); services.AddDragDropHost(); // Регистрация поведений services.AddTransient, MyDragSourceBehavior>(); services.AddTransient, MyDropTargetBehavior>(); } ``` ### Использование с ViewModel ```csharp public class MainViewModel { private readonly IServiceProvider _serviceProvider; public MainViewModel(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public void AttachDragBehavior(UIElement element) { var behavior = _serviceProvider.GetRequiredService>(); behavior.AssociatedElement = element; } public void AttachDropBehavior(Panel panel) { var behavior = _serviceProvider.GetRequiredService>(); behavior.AssociatedElement = panel; } } ``` ## 📝 Best Practices ### 1. **Эффективность визуального представления** - Создавайте легковесные визуальные элементы для плавной анимации - Используйте кэширование для часто используемых представлений - Избегайте сложных преобразований и эффектов ### 2. **Обработка событий** - Всегда отписывайтесь от событий при откреплении поведения - Используйте слабые ссылки для предотвращения утечек памяти - Обрабатывайте исключения в обработчиках событий ### 3. **Ресурсы** - Освобождайте графические ресурсы в методах Release/Dispose - Используйте пулы объектов для часто создаваемых элементов - Мониторьте использование памяти при активном перетаскивании ### 4. **Пользовательский опыт** - Предоставляйте понятную визуальную обратную связь - Поддерживайте настраиваемые стили и темы - Обеспечивайте плавность анимации даже на слабых устройствах ## 🔄 Миграция ### С версии 1.x на 2.0 1. **Обновление интерфейсов**: - Методы переименованы в соответствии с Core - Добавлена поддержка CancellationToken 2. **Изменения в базовых классах**: - `DragSourceBehaviorBase` теперь реализует новый интерфейс `IDragSource` - `DropTargetBehaviorBase` теперь реализует новый интерфейс `IDropTarget` 3. **Регистрация сервисов**: - Метод `AddLatticeDragDrop` теперь регистрирует только базовые сервисы - Для платформенных реализаций используйте методы `AddDragVisualProvider`, `AddDropVisualAdorner`, `AddDragDropHost` ## 📄 Лицензия Библиотека распространяется под лицензией MIT. Подробности см. в файле LICENSE. ## 🤝 Вклад в разработку Для интеграции с новой UI-платформой необходимо: 1. Реализовать интерфейсы `IDragDropHost`, `IDragVisualProvider`, `IDropVisualAdorner` 2. Создать производные классы от `DragSourceBehaviorBase` и `DropTargetBehaviorBase` 3. Предоставить метод расширения для регистрации всех компонентов ## 🐛 Отчеты об ошибках Для сообщения об ошибках используйте Issues на GitHub. Пожалуйста, указывайте: - Платформу и версию UI-фреймворка - Шаги для воспроизведения - Ожидаемое и фактическое поведение - Пример кода для демонстрации проблемы