Доработан проект UI под новый Core
This commit is contained in:
@@ -1,136 +1,378 @@
|
||||
# Lattice.UI.DragDrop
|
||||
|
||||

|
||||

|
||||

|
||||
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-фреймворка
|
||||
- Шаги для воспроизведения
|
||||
- Ожидаемое и фактическое поведение
|
||||
- Пример кода для демонстрации проблемы
|
||||
Reference in New Issue
Block a user