Доработан проект UI под новый Core

This commit is contained in:
2026-01-25 02:52:07 +03:00
parent be12154262
commit 2bd7d3c474
7 changed files with 923 additions and 181 deletions

View File

@@ -1,136 +1,378 @@
# Lattice.UI.DragDrop
![Lattice Framework](https://img.shields.io/badge/Lattice-UI%20Framework-blueviolet)
![Version](https://img.shields.io/badge/version-1.0.0-green)
![License](https://img.shields.io/badge/license-MIT-blue)
UI-слой библиотеки перетаскивания (drag-and-drop) для платформ .NET.
Кроссплатформенные абстракции для системы перетаскивания в Lattice UI Framework.
## 📋 Обзор
## 📦 О проекте
Lattice.UI.DragDrop предоставляет абстракции и базовые реализации для интеграции системы перетаскивания Lattice.Core.DragDrop с различными UI-фреймворками (WPF, Avalonia, MAUI и т.д.).
`Lattice.UI.DragDrop` предоставляет платформонезависимые интерфейсы и базовые классы для реализации drag-and-drop функциональности.
Этот проект служит основой для конкретных реализаций на различных платформах (WinUI, Uno Platform, MAUI и т.д.).
### ✨ Основные возможности
## 🎯 Особенности
-**Кросс-платформенные абстракции** - единый API для всех UI-фреймворков
-**Готовые базовые классы** для быстрой реализации поведения перетаскивания
-**Поддержка визуальной обратной связи** через плагинную архитектуру
-**Интеграция с DI-контейнерами** через Microsoft.Extensions.DependencyInjection
-**Безопасные реализации по умолчанию** для упрощения разработки
-**Расширяемость** через наследование и переопределение
- **Абстрактные интерфейсы** для источников перетаскивания и целей сброса
- **Базовые классы поведения** для упрощения реализации
- **Платформонезависимая архитектура**
- **Поддержка сложных сценариев** (переупорядочивание, вложенное перетаскивание)
- **Расширяемая система событий**
## 🏗️ Архитектура
## 🔧 Интерфейсы
### Основные компоненты
### IDragVisualProvider
```csharp
public interface IDragVisualProvider
{
object CreateDragVisual(DragInfo dragInfo, Point initialPosition);
void UpdateDragVisualPosition(object dragVisual, Point position);
void ReleaseDragVisual(object dragVisual);
}
```
#### 1. **IDragDropHost**
Абстракция для управления визуальными элементами перетаскивания на уровне платформы.
### IDropVisualAdorner
```csharp
public interface IDropVisualAdorner
{
void Show(DropInfo dropInfo, Rect targetBounds);
void Update(DropInfo dropInfo);
void Hide();
}
```
#### 2. **IDragVisualProvider**
Поставщик визуального представления для перетаскиваемых элементов.
## 📦 Установка
#### 3. **IDropVisualAdorner**
Элемент визуальной обратной связи при наведении на цель сброса.
Добавьте проект как ссылку в ваше решение или установите как NuGet пакет:
#### 4. **DragSourceBehaviorBase\<TElement>**
Базовый класс для реализации поведения источника перетаскивания.
```xml
<PackageReference Include="Lattice.UI.DragDrop" Version="1.0.0" />
```
## 🔗 Зависимости
- `Lattice.Core.DragDrop` >= 1.0.0
- `Lattice.Core.Geometry` >= 1.0.0
- `Microsoft.Extensions.DependencyInjection.Abstractions` >= 8.0.0
#### 5. **DropTargetBehaviorBase\<TElement>**
Базовый класс для реализации поведения цели сброса.
## 🚀 Быстрый старт
### 1. Регистрация сервисов
### 1. Установка
```csharp
using Lattice.UI.DragDrop.Extensions;
// В методе ConfigureServices вашего приложения
services.AddLatticeDragDrop();
```
### 2. Регистрация платформенных реализаций
```csharp
// Для WPF
services.AddDragVisualProvider<WpfDragVisualProvider>();
services.AddDropVisualAdorner<WpfDropVisualAdorner>();
services.AddDragDropHost<WpfDragDropHost>();
// Для Avalonia
services.AddDragVisualProvider<AvaloniaDragVisualProvider>();
services.AddDropVisualAdorner<AvaloniaDropVisualAdorner>();
services.AddDragDropHost<AvaloniaDragDropHost>();
```
### 3. Создание поведения источника перетаскивания
```csharp
public class MyDragSourceBehavior : DragSourceBehaviorBase<Control>
{
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<DragInfo?> 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<Panel>
{
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<bool> 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<WpfDragVisualProvider>()
.AddDropVisualAdorner<WpfDropVisualAdorner>()
.AddDragDropHost<WpfDragDropHost>();
}
}
```
### Avalonia
```csharp
public static class AvaloniaDragDropExtensions
{
public static IServiceCollection AddAvaloniaDragDrop(this IServiceCollection services)
{
return services
.AddLatticeDragDrop()
.AddDragVisualProvider<AvaloniaDragVisualProvider>()
.AddDropVisualAdorner<AvaloniaDropVisualAdorner>()
.AddDragDropHost<AvaloniaDragDropHost>();
}
}
```
## 🎨 Визуальная обратная связь
### Создание кастомного визуального представления
```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<PlatformDragVisualProvider>();
services.AddDropVisualAdorner<PlatformDropVisualAdorner>();
services.AddDragDropHost<PlatformDragDropHost>();
// Регистрация поведений
services.AddTransient<DragSourceBehaviorBase<UIElement>, MyDragSourceBehavior>();
services.AddTransient<DropTargetBehaviorBase<Panel>, MyDropTargetBehavior>();
}
```
### 2. Создание кастомного поведения
### Использование с ViewModel
```csharp
using Lattice.UI.DragDrop.Behaviors;
public class MyDragSource : DragSourceBehaviorBase<MyElement>
public class MainViewModel
{
protected override void SubscribeToEvents(MyElement element)
private readonly IServiceProvider _serviceProvider;
public MainViewModel(IServiceProvider serviceProvider)
{
// Подписка на события элемента
_serviceProvider = serviceProvider;
}
public override bool CanStartDrag(out DragInfo? dragInfo)
public void AttachDragBehavior(UIElement element)
{
// Реализация проверки возможности перетаскивания
var behavior = _serviceProvider.GetRequiredService<DragSourceBehaviorBase<UIElement>>();
behavior.AssociatedElement = element;
}
public void AttachDropBehavior(Panel panel)
{
var behavior = _serviceProvider.GetRequiredService<DropTargetBehaviorBase<Panel>>();
behavior.AssociatedElement = panel;
}
}
```
## 📚 API Reference
## 📝 Best Practices
### Основные типы
### 1. **Эффективность визуального представления**
- Создавайте легковесные визуальные элементы для плавной анимации
- Используйте кэширование для часто используемых представлений
- Избегайте сложных преобразований и эффектов
| Тип | Описание |
|-----|----------|
| `DragSourceBehaviorBase<T>` | Базовый класс для поведения источника |
| `DropTargetBehaviorBase<T>` | Базовый класс для поведения цели |
| `IDragVisualProvider` | Поставщик визуального представления |
| `IDropVisualAdorner` | Визуальный элемент обратной связи |
### 2. **Обработка событий**
- Всегда отписывайтесь от событий при откреплении поведения
- Используйте слабые ссылки для предотвращения утечек памяти
- Обрабатывайте исключения в обработчиках событий
### Расширения DI
### 3. **Ресурсы**
- Освобождайте графические ресурсы в методах Release/Dispose
- Используйте пулы объектов для часто создаваемых элементов
- Мониторьте использование памяти при активном перетаскивании
- `AddLatticeDragDrop()` - регистрация сервисов перетаскивания
### 4. **Пользовательский опыт**
- Предоставляйте понятную визуальную обратную связь
- Поддерживайте настраиваемые стили и темы
- Обеспечивайте плавность анимации даже на слабых устройствах
## 🔄 Интеграция с платформенными проектами
## 🔄 Миграция
Этот проект предназначен для наследования платформенными реализациями:
### С версии 1.x на 2.0
1. **WinUI**: `Lattice.UI.DragDrop.WinUI`
2. **Uno Platform**: `Lattice.UI.DragDrop.Uno` (планируется)
3. **MAUI**: `Lattice.UI.DragDrop.Maui` (планируется)
1. **Обновление интерфейсов**:
- Методы переименованы в соответствии с Core
- Добавлена поддержка CancellationToken
## 🧪 Тестирование
2. **Изменения в базовых классах**:
- `DragSourceBehaviorBase` теперь реализует новый интерфейс `IDragSource`
- `DropTargetBehaviorBase` теперь реализует новый интерфейс `IDropTarget`
Проект включает модульные тесты для всех публичных API:
```bash
dotnet test Lattice.UI.DragDrop.Tests
```
3. **Регистрация сервисов**:
- Метод `AddLatticeDragDrop` теперь регистрирует только базовые сервисы
- Для платформенных реализаций используйте методы `AddDragVisualProvider`, `AddDropVisualAdorner`, `AddDragDropHost`
## 📄 Лицензия
MIT License. Подробности в файле [LICENSE](LICENSE).
Библиотека распространяется под лицензией MIT. Подробности см. в файле LICENSE.
## 🤝 Участие в разработке
## 🤝 Вклад в разработку
1. Форкните репозиторий
2. Создайте ветку для вашей функции
3. Сделайте коммит изменений
4. Отправьте пул-реквест
Для интеграции с новой UI-платформой необходимо:
1. Реализовать интерфейсы `IDragDropHost`, `IDragVisualProvider`, `IDropVisualAdorner`
2. Создать производные классы от `DragSourceBehaviorBase` и `DropTargetBehaviorBase`
3. Предоставить метод расширения для регистрации всех компонентов
## 📞 Поддержка
## 🐛 Отчеты об ошибках
- Документация: [lattice-framework.github.io](https://lattice-framework.github.io)
- Issues: [GitHub Issues](https://github.com/lattice-framework/ui-dragdrop/issues)
- Обсуждения: [GitHub Discussions](https://github.com/lattice-framework/ui-dragdrop/discussions)
Для сообщения об ошибках используйте Issues на GitHub. Пожалуйста, указывайте:
- Платформу и версию UI-фреймворка
- Шаги для воспроизведения
- Ожидаемое и фактическое поведение
- Пример кода для демонстрации проблемы