Переработаны методы.

This commit is contained in:
2026-01-25 02:37:16 +03:00
parent a6ee6fcb36
commit be12154262
17 changed files with 897 additions and 1116 deletions

View File

@@ -1,547 +1,313 @@
# Lattice.UI.DragDrop.WinUI
# Lattice.Core.DragDrop
![Lattice Framework](https://img.shields.io/badge/Lattice-UI%20Framework-blueviolet)
![WinUI 3](https://img.shields.io/badge/WinUI-3.0%2B-blue)
![Windows 10+](https://img.shields.io/badge/Windows-10%2B-success)
![Version](https://img.shields.io/badge/version-1.0.0-green)
![License](https://img.shields.io/badge/license-MIT-blue)
Библиотека для реализации drag-and-drop (перетаскивания) в приложениях на .NET.
Полнофункциональная реализация системы перетаскивания для WinUI 3 в составе Lattice UI Framework.
## 📋 Обзор
## 🎉 Демо
Lattice.Core.DragDrop предоставляет полнофункциональную, асинхронную и потокобезопасную систему для реализации операций перетаскивания в пользовательских интерфейсах. Библиотека построена на принципах разделения ответственности и поддерживает сложные сценарии перетаскивания с минимальными усилиями со стороны разработчика.
![Drag & Drop Demo](https://raw.githubusercontent.com/lattice-framework/ui-dragdrop/main/docs/demo.gif)
### ✨ Основные возможности
*Перетаскивание элементов между контейнерами и переупорядочивание списка*
-**Полностью асинхронный API** - все операции поддерживают async/await
-**Потокобезопасность** - безопасная работа в многопоточных средах
-**Расширяемая архитектура** - легко добавлять новые типы источников и целей
-**Подробные события** - полный контроль над жизненным циклом операций
-**Статистика и мониторинг** - встроенный сбор метрик использования
-**Поддержка CancellationToken** - корректная отмена длительных операций
-**Независимость от UI-фреймворков** - может использоваться с любым представлением
## 📦 Особенности
## 🏗️ Архитектура
**Готовое решение для WinUI 3** - работает из коробки
**Attached Behaviors** - легко подключается к любым UIElement
**Визуальная обратная связь** - анимации и подсветка
**Переупорядочивание элементов** - drag-and-drop в списках
**Кастомизация стилей** - полный контроль над внешним видом
**Поддержка сложных сценариев** - вложенные элементы, зоны сброса
**Производительность** - оптимизировано для плавной работы
### Основные компоненты
#### 1. **IDragSource**
Интерфейс для объектов, которые могут быть источником данных при перетаскивании. Определяет:
- Возможность начала перетаскивания
- Подготовку данных для передачи
- Реакцию на завершение или отмену операции
#### 2. **IDropTarget**
Интерфейс для объектов, которые могут принимать сброшенные данные. Определяет:
- Проверку совместимости данных
- Визуальную обратную связь при наведении
- Обработку сброшенных данных
#### 3. **DragDropService**
Центральный сервис, координирующий все операции. Отвечает за:
- Регистрацию и управление целями сброса
- Оркестрацию жизненного цикла операций
- Распространение событий между компонентами
- Сбор статистики и обработку ошибок
#### 4. **Модели данных**
- **DragInfo** - информация о начале перетаскивания
- **DropInfo** - информация о потенциальном сбросе
- **DragDropEffects** - перечисление возможных эффектов
## 🚀 Быстрый старт
### 1. Установка
Добавьте пакет через NuGet:
```powershell
Install-Package Lattice.UI.DragDrop.WinUI
```csharp
// Пример регистрации в DI-контейнере
services.AddSingleton<IDragDropService, DragDropService>();
```
Или через Package Manager:
```xml
<PackageReference Include="Lattice.UI.DragDrop.WinUI" Version="1.0.0" />
```
### 2. Инициализация в приложении
### 2. Создание источника перетаскивания
```csharp
using Lattice.UI.DragDrop.WinUI.Extensions;
using Lattice.UI.DragDrop.WinUI.Helpers;
public MainWindow()
public class ItemDragSource : IDragSource
{
InitializeComponent();
// Инициализация ресурсов
ResourceHelper.InitializeDragDropResources();
// Настройка примеров перетаскивания
SetupDragDropExamples();
}
private readonly Item _item;
private void SetupDragDropExamples()
{
// Пример 1: Перетаскивание текста
var textBlock = new TextBlock { Text = "Перетащи меня" };
textBlock.MakeDragSource("Пример данных");
// Пример 2: Цель сброса
var border = new Border { Background = new SolidColorBrush(Colors.LightGray) };
border.MakeDropTarget(typeof(string));
// Пример 3: Стилизация
var button = new Button { Content = "Кнопка" };
button.ApplyDragStyle();
button.EnableDragVisualFeedback();
}
```
## 🎨 Использование
### Базовое перетаскивание
```csharp
// Создаем перетаскиваемый элемент
var dragSource = new Border
{
Background = new SolidColorBrush(Colors.LightBlue),
Child = new TextBlock { Text = "Drag me" }
};
// Делаем элемент перетаскиваемым
dragSource.MakeDragSource(dragSource); // Можно передать любые данные
// Создаем цель сброса
var dropTarget = new Border
{
Background = new SolidColorBrush(Colors.LightGreen)
};
// Делаем элемент целью сброса
dropTarget.MakeDropTarget(typeof(Border));
```
### Переупорядочивание элементов в списке
```csharp
// Создаем контейнер с поддержкой переупорядочивания
var reorderContainer = new StackPanel();
reorderContainer.MakeDropTarget(typeof(UIElement));
// Добавляем перетаскиваемые элементы
for (int i = 0; i < 5; i++)
{
var item = new Border
public ItemDragSource(Item item)
{
Background = new SolidColorBrush(Colors.White),
BorderBrush = new SolidColorBrush(Colors.Gray),
BorderThickness = new Thickness(1),
Margin = new Thickness(0, 0, 0, 5),
Child = new TextBlock { Text = $"Item {i + 1}" }
};
item.MakeDragSource(item);
reorderContainer.Children.Add(item);
}
```
_item = item;
}
### Кастомизация визуальной обратной связи
```csharp
// Создаем кастомный стиль
var customStyle = new Style(typeof(Control));
customStyle.Setters.Add(new Setter(Control.BackgroundProperty,
new SolidColorBrush(Colors.Yellow)));
customStyle.Setters.Add(new Setter(Control.BorderBrushProperty,
new SolidColorBrush(Colors.Red)));
// Применяем стиль к элементу
var element = new Button { Content = "Custom Style" };
element.SetDropFeedbackStyle(customStyle);
element.MakeDragSource("data");
```
## 📁 Структура API
### Behaviors (Поведения)
| Класс | Описание |
|-------|----------|
| `WinUIDragSourceBehavior` | Прикрепляемое поведение для источников |
| `WinUIDropTargetBehavior` | Прикрепляемое поведение для целей |
### Controls (Контролы)
| Контрол | Назначение |
|---------|------------|
| `DragAdorner` | Визуальное представление перетаскивания |
| `DropPreviewAdorner` | Предпросмотр области сброса |
| `DragDropOverlay` | Оверлей для визуальных элементов |
### Services (Сервисы)
| Сервис | Назначение |
|--------|------------|
| `WinUIDragVisualProvider` | Создание визуальных элементов |
| `DragDropConfigurationService` | Централизованная настройка |
### Extensions (Расширения)
```csharp
// Основные методы расширения
element.MakeDragSource(data); // Сделать перетаскиваемым
element.MakeDropTarget(types); // Сделать целью сброса
control.ApplyDragStyle(); // Применить стиль перетаскивания
control.SetDragVisualState("Dragging"); // Установить визуальное состояние
```
## 🎯 Примеры использования
### Пример 1: Файловый менеджер
```csharp
// Перетаскивание файлов
var fileItem = new ListViewItem { Content = "Document.pdf" };
fileItem.MakeDragSource(new FileData { Path = "C:\\Files\\Document.pdf" });
// Папка - цель сброса
var folderItem = new ListViewItem { Content = "Downloads" };
folderItem.MakeDropTarget(typeof(FileData));
folderItem.SetDropHandler(new FileDropHandler());
```
### Пример 2: Конструктор UI
```csharp
// Панель инструментов
var toolbox = new StackPanel();
toolbox.MakeChildrenDraggable(element => new ControlTemplate
{
Type = element.GetType(),
Name = element.Name
});
// Область дизайна
var designArea = new Canvas();
designArea.MakeDropTarget(typeof(ControlTemplate));
// Обработчик сброса
designArea.Drop += (sender, e) =>
{
var template = e.DataView.GetData<ControlTemplate>();
var control = Activator.CreateInstance(template.Type);
Canvas.SetLeft(control, e.GetPosition(designArea).X);
Canvas.SetTop(control, e.GetPosition(designArea).Y);
designArea.Children.Add(control);
};
```
### Пример 3: Календарь с событиями
```csharp
// Событие календаря
var calendarEvent = new Border
{
Background = new SolidColorBrush(Colors.CornflowerBlue),
Child = new TextBlock { Text = "Meeting at 10:00" }
};
calendarEvent.MakeDragSource(new CalendarEvent
{
Id = 1,
Title = "Meeting",
StartTime = DateTime.Now
});
// Ячейка календаря
var timeSlot = new Border
{
Background = new SolidColorBrush(Colors.White),
BorderBrush = new SolidColorBrush(Colors.LightGray)
};
timeSlot.MakeDropTarget(typeof(CalendarEvent));
```
## 🎨 Темы и стилизация
### Встроенные стили
Проект включает готовые стили в папке `Themes/`:
- `DragAdorner.xaml` - стиль для визуального элемента перетаскивания
- `DropPreviewAdorner.xaml` - стиль для предпросмотра сброса
- `DragDropStyles.xaml` - общие стили для элементов
### Кастомизация через ресурсы
```xml
<!-- App.xaml или Generic.xaml -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///Lattice.UI.DragDrop.WinUI/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Переопределение стилей -->
<Style x:Key="CustomDragAdornerStyle" TargetType="dragDrop:DragAdorner">
<Setter Property="Background" Value="Red" />
<Setter Property="Opacity" Value="0.9" />
</Style>
</ResourceDictionary>
```
### Визуальные состояния
Элементы поддерживают следующие визуальные состояния:
- `Normal` - обычное состояние
- `Dragging` - элемент перетаскивается
- `DragOver` - над элементом перетаскивают объект
```csharp
// Переключение состояний
control.SetDragVisualState("Dragging", true); // С анимацией
control.SetDragVisualState("Normal", false); // Без анимации
```
## 🔧 Интеграция с Docking System
Система идеально интегрируется с Lattice Docking:
```csharp
using Lattice.UI.DragDrop.WinUI.Extensions;
using Lattice.UI.Docking.WinUI;
public class DockPane : ContentControl
{
public DockPane()
public async Task<DragInfo?> TryStartDragAsync(Point startPosition, CancellationToken ct)
{
// Делаем панель перетаскиваемой
this.MakeDragSource(new DockPaneDragData
// Проверяем, можно ли начать перетаскивание
if (!_item.CanBeDragged)
return null;
// Создаем информацию о перетаскивании
return new DragInfo(
data: _item,
allowedEffects: DragDropEffects.Copy | DragDropEffects.Move,
startPosition: startPosition,
source: this
);
}
public async Task OnDragCompletedAsync(DragInfo dragInfo, DragDropEffects effects, CancellationToken ct)
{
if (effects == DragDropEffects.Move)
{
Pane = this,
ContentType = Content?.GetType(),
Title = Title
});
// Устанавливаем визуальную обратную связь
this.ApplyDragStyle();
this.EnableDragVisualFeedback();
// Удаляем элемент при перемещении
await _repository.DeleteAsync(_item.Id, ct);
}
}
}
public class DockArea : ContentControl
{
public DockArea()
public Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken ct)
{
// Область докинга принимает панели
this.MakeDropTarget(typeof(DockPaneDragData));
// Кастомный обработчик
this.SetDropHandler(new DockDropHandler());
// Очистка ресурсов при отмене
return Task.CompletedTask;
}
}
```
## 📊 Производительность
### Оптимизации
1. **Минимальные перерисовки** - обновление только при необходимости
2. **Кэширование визуальных элементов** - повторное использование
3. **Эффективные алгоритмы поиска** - быстрый поиск целей сброса
4. **Асинхронная обработка** - не блокирует UI поток
### Рекомендации
### 3. Создание цели сброса
```csharp
// ✅ Правильно
element.MakeDragSource(data);
public class ContainerDropTarget : IDropTarget
{
private readonly ObservableCollection<Item> _items;
// ❌ Избегать
element.PointerPressed += (s, e) => { /* сложная логика */ };
element.PointerMoved += (s, e) => { /* частые обновления */ };
public async Task<bool> CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct)
{
// Проверяем тип данных
if (dropInfo.Data is not Item item)
return false;
// Проверяем бизнес-правила
return await _validator.CanAddItemAsync(item, ct);
}
public async Task OnDragOverAsync(DropInfo dropInfo, CancellationToken ct)
{
// Обновляем визуальную обратную связь
dropInfo.SuggestedEffects = DragDropEffects.Move;
dropInfo.ShowVisualFeedback = true;
}
public async Task OnDropAsync(DropInfo dropInfo, CancellationToken ct)
{
var item = (Item)dropInfo.Data;
await _items.AddAsync(item, ct);
// Помечаем как обработанное
dropInfo.MarkAsHandled();
}
public Task OnDragLeaveAsync(CancellationToken ct)
{
// Очищаем визуальную обратную связь
return Task.CompletedTask;
}
}
```
## 🧪 Тестирование
### Модульные тесты
### 4. Регистрация и использование сервиса
```csharp
[TestClass]
public class DragDropTests
public class MainViewModel
{
[TestMethod]
public void MakeDragSource_EnablesDragging()
private readonly IDragDropService _dragDropService;
public MainViewModel(IDragDropService dragDropService)
{
var element = new Border();
element.MakeDragSource("test");
_dragDropService = dragDropService;
Assert.IsTrue(element.IsDragSource());
// Регистрация цели сброса
_targetId = _dragDropService.RegisterDropTarget(
target: new ContainerDropTarget(_items),
bounds: new Rect(0, 0, 400, 300),
priority: 0,
group: "main-container"
);
// Подписка на события
_dragDropService.DragStarted += OnDragStarted;
_dragDropService.DragCompleted += OnDragCompleted;
_dragDropService.ErrorOccurred += OnError;
}
// Обработка мышиных событий
public async Task OnMouseDown(Point position)
{
var source = new ItemDragSource(selectedItem);
await _dragDropService.StartDragAsync(source, position);
}
public async Task OnMouseMove(Point position)
{
await _dragDropService.UpdateDragAsync(position);
}
public async Task OnMouseUp(Point position)
{
var effects = await _dragDropService.EndDragAsync(position);
// Обработка результатов
}
}
```
## 📊 Статистика и мониторинг
```csharp
// Получение статистики использования
var stats = _dragDropService.GetStats();
Console.WriteLine($"Всего операций: {stats.TotalDragOperations}");
Console.WriteLine($"Успешных сбросов: {stats.SuccessfulDrops}");
Console.WriteLine($"Отменено операций: {stats.CancelledOperations}");
Console.WriteLine($"Среднее время операции: {stats.AverageOperationTime}");
```
## ⚙️ Конфигурация
### Параметры сервиса
```csharp
// Настройка через свойства сервиса
_dragDropService.DragStartThreshold = 5.0; // Порог в пикселях
_dragDropService.EnableAsyncOperations = true;
_dragDropService.AsyncOperationTimeout = 3000; // 3 секунды
```
### Константы по умолчанию
Все значения по умолчанию определены в классе `DragDropConstants`:
- `DefaultDragThreshold`: 3.0 пикселей
- `DefaultAsyncTimeout`: 5000 миллисекунд
- `TargetLifetimeMinutes`: 10 минут
## 🔧 Расширенные сценарии
### Группировка целей сброса
```csharp
// Регистрация группы целей
_dragDropService.RegisterDropTarget(target1, bounds1, group: "panel");
_dragDropService.RegisterDropTarget(target2, bounds2, group: "panel");
// Массовая отмена регистрации
_dragDropService.UnregisterDropTargetsInGroup("panel");
```
### Обработка ошибок
```csharp
private void OnError(object sender, DragDropErrorEventArgs e)
{
_logger.LogError(e.Exception,
"Ошибка в операции {Operation}",
e.Operation);
[TestMethod]
public void MakeDropTarget_AcceptsCorrectTypes()
{
var element = new Border();
element.MakeDropTarget(typeof(string), typeof(int));
Assert.IsTrue(element.IsDropTarget());
}
// Уведомление пользователя
_notificationService.ShowError("Ошибка при перетаскивании");
}
```
### UI тесты
### Кастомизация эффектов
```csharp
[TestClass]
public class DragDropUITests
// Использование расширений для работы с эффектами
if (effects.CanCopy())
{
[UITestMethod]
public async Task DragAndDrop_BetweenElements()
{
await UITestHelper.Run(async window =>
{
var source = new Border { Background = new SolidColorBrush(Colors.Blue) };
var target = new Border { Background = new SolidColorBrush(Colors.Green) };
source.MakeDragSource("data");
target.MakeDropTarget(typeof(string));
// Симуляция перетаскивания
await SimulateDrag(source, target);
// Проверка результатов
Assert.IsTrue(dropOccurred);
});
}
// Логика для копирования
}
```
## 🔍 Отладка
### Включение логов
```csharp
// Включение подробного логирования
#if DEBUG
DragDropDebugger.EnableLogging = true;
DragDropDebugger.LogLevel = LogLevel.Verbose;
#endif
```
### Визуальные подсказки
```csharp
// Показать границы целей сброса
DebugDropTargets.ShowBounds = true;
// Показать траекторию перетаскивания
DebugDragTrail.Enabled = true;
DebugDragTrail.Color = Colors.Red;
```
## 📈 Производительность в production
### Мониторинг
```csharp
// Сбор метрик
var metrics = DragDropPerformanceCollector.Collect();
Console.WriteLine($"Drag operations: {metrics.DragCount}");
Console.WriteLine($"Average drag time: {metrics.AverageDragTimeMs}ms");
Console.WriteLine($"Drop success rate: {metrics.DropSuccessRate:P}");
```
### Оптимизация для больших списков
```csharp
// Виртуализация для списков
var virtualizingList = new ListView
if (effects.CanMove())
{
ItemsSource = largeCollection,
VirtualizingStackPanel.VirtualizationMode = VirtualizationMode.Recycling
};
// Логика для перемещения
}
// Оптимизация перетаскивания
virtualizingList.MakeDropTarget(typeof(DataItem));
virtualizingList.SetDragDropOptimization(true);
// Определение эффекта по модификаторам клавиш
var effect = DragDropEffectsExtensions.GetEffectFromKeys(
controlKey: Keyboard.IsControlDown,
shiftKey: Keyboard.IsShiftDown,
altKey: Keyboard.IsAltDown
);
```
## 🤝 Интеграция с другими компонентами Lattice
## 📝 Best Practices
### Toolbox
```csharp
toolboxItem.MakeDragSource(new ToolboxItem
{
Type = typeof(Button),
Icon = "🔘"
});
```
1. **Производительность**
- Методы `CanAcceptDropAsync` и `OnDragOverAsync` вызываются часто, оптимизируйте их
- Избегайте синхронных операций в обработчиках
- Используйте кэширование при проверке типов данных
### Property Grid
```csharp
propertyItem.MakeDragSource(new PropertyValue
{
Name = "Background",
Value = Colors.Blue
});
```
2. **Безопасность**
- Всегда проверяйте тип данных в `CanAcceptDropAsync`
- Валидируйте бизнес-правила перед обработкой сброса
- Используйте CancellationToken для отмены длительных операций
### Layout System
```csharp
layoutPanel.MakeDropTarget(typeof(UIElement));
layoutPanel.SetDropPositionHandler((element, position) =>
{
return CalculateDropZone(position);
});
```
3. **Пользовательский опыт**
- Предоставляйте визуальную обратную связь через `DropInfo.ShowVisualFeedback`
- Используйте `DropInfo.VisualFeedbackData` для кастомизации отображения
- Обрабатывайте отмену операций для очистки временных данных
## 📚 Дополнительные ресурсы
## 🔄 Миграция
### Документация
- [Полная документация API](https://lattice-framework.github.io/ui-dragdrop/api/)
- [Примеры использования](https://lattice-framework.github.io/ui-dragdrop/examples/)
- [Руководство по стилизации](https://lattice-framework.github.io/ui-dragdrop/styling/)
### С версии 1.x на 2.0
### Видео туториалы
- [Быстрый старт](https://youtube.com/playlist?list=...) - 15 минут
- [Продвинутые техники](https://youtube.com/playlist?list=...) - 45 минут
- [Интеграция с Docking](https://youtube.com/playlist?list=...) - 30 минут
1. **Интерфейсы переименованы:**
- `CanStartDragAsync``TryStartDragAsync`
- `DragCompletedAsync``OnDragCompletedAsync`
- `DragCancelledAsync``OnDragCancelledAsync`
### Сообщество
- [GitHub Discussions](https://github.com/lattice-framework/ui-dragdrop/discussions) - вопросы и обсуждения
- [Discord](https://discord.gg/lattice) - живое общение
- [Stack Overflow](https://stackoverflow.com/questions/tagged/lattice-ui-dragdrop) - технические вопросы
2. **Классы EventArgs объединены:**
- Все события наследуются от `DragEventArgs`
- Упрощена иерархия классов событий
## 🐛 Отчет об ошибках
Нашли ошибку? [Создайте issue](https://github.com/lattice-framework/ui-dragdrop/issues) с подробным описанием:
1. Шаги для воспроизведения
2. Ожидаемое поведение
3. Фактическое поведение
4. Скриншоты или видео
5. Версии: Windows, WinUI, Lattice
## 🚀 Roadmap
### Версия 1.1 (Q2 2024)
- [ ] Поддержка touch-жестов
- [ ] Анимации с физикой
- [ ] Расширенная визуализация
### Версия 1.2 (Q3 2024)
- [ ] Интеграция с Windows Shell
- [ ] Поддержка виртуальных данных
- [ ] Улучшенная доступность
### Версия 2.0 (Q4 2024)
- [ ] Кроссплатформенная поддержка (Uno Platform)
- [ ] WebAssembly поддержка
- [ ] Расширенный набор контролов
3. **Удалены устаревшие компоненты:**
- `AsyncDragDropUtilities` удален
- `ServiceCollectionExtensions` удален
## 📄 Лицензия
MIT License. Подробности в файле [LICENSE](LICENSE).
Библиотека распространяется под лицензией MIT. Подробности см. в файле LICENSE.
## 👥 Авторы
## 🤝 Вклад в разработку
- **Команда Lattice Framework** - [@lattice-framework](https://github.com/lattice-framework)
- **Главный разработчик** - [Ваше имя](https://github.com/yourusername)
Мы приветствуем вклад в развитие библиотеки. Перед отправкой pull request ознакомьтесь с руководством по контрибьютингу.
## 🙏 Благодарности
## 🐛 Отчеты об ошибках
Спасибо сообществу WinUI и всем контрибьюторам, которые помогают улучшать проект!
---
<div align="center">
<p>
<strong>Lattice.UI.DragDrop.WinUI</strong> - часть <a href="https://github.com/lattice-framework">Lattice UI Framework</a>
</p>
<p>
<a href="https://github.com/lattice-framework/ui-dragdrop">GitHub</a> •
<a href="https://lattice-framework.github.io">Документация</a> •
<a href="https://discord.gg/lattice">Discord</a> •
<a href="https://twitter.com/latticefw">Twitter</a>
</p>
</div>
Для сообщения об ошибках используйте Issues на GitHub. Пожалуйста, указывайте:
- Версию библиотеки
- Шаги для воспроизведения
- Ожидаемое и фактическое поведение
- Пример кода для демонстрации проблемы