using Lattice.Core.DragDrop.Services; using Lattice.Themes; using Lattice.UI.DragDrop.WinUI.Factories; using Lattice.UI.DragDrop.WinUI.Services; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; namespace Lattice.Example.DragDrop; public sealed partial class MainWindow : Window { private readonly ObservableCollection _sourceItems = new(); private readonly ObservableCollection _droppedItems = new(); private WinUIDragDropManager? _dragDropManager; private IDragDropService? _dragDropService; private int _dragOperationCount = 0; public MainWindow() { // Создаем менеджер перетаскивания _dragDropManager = WinUIDragDropFactory.CreateManager(this, manager => { // Настраиваем смещение визуального элемента manager.DragVisualOffset = new Core.Geometry.Point(-15, -15); }); _dragDropService = _dragDropManager.DragDropService; InitializeComponent(); // Устанавливаем размер окна SetWindowSize(1200, 800); // Инициализируем данные InitializeData(); // Инициализируем drag & drop систему InitializeDragDrop(); // Устанавливаем контекст данных SourceItemsControl.ItemsSource = _sourceItems; DroppedItemsControl.ItemsSource = _droppedItems; Closed += MainWindow_Closing; // Обновляем видимость инструкций UpdateDropInstructions(); } private void MainWindow_Closing(object sender, WindowEventArgs args) { // Очищаем ресурсы _dragDropManager?.Dispose(); } private void InitializeData() { // Создаем тестовые данные для перетаскивания _sourceItems.Add(new DragDropItem { Name = "Document", Description = "Microsoft Word document", Icon = "Document", Type = "File" }); _sourceItems.Add(new DragDropItem { Name = "Image", Description = "PNG image file", Icon = "Pictures", Type = "File" }); _sourceItems.Add(new DragDropItem { Name = "Music", Description = "MP3 audio file", Icon = "Audio", Type = "File" }); _sourceItems.Add(new DragDropItem { Name = "Video", Description = "MP4 video file", Icon = "Video", Type = "File" }); _sourceItems.Add(new DragDropItem { Name = "Contact", Description = "Contact information", Icon = "Contact", Type = "Data" }); _sourceItems.Add(new DragDropItem { Name = "Link", Description = "Web URL", Icon = "Link", Type = "Link" }); _sourceItems.Add(new DragDropItem { Name = "Folder", Description = "File system folder", Icon = "Folder", Type = "Folder" }); } private void InitializeDragDrop() { try { // Настраиваем кастомный источник перетаскивания _dragDropManager.MakeDragSource(CustomDragSource, new DragDropItem { Name = "Custom Element", Description = "This is a custom drag source example", Icon = "Favorite", Type = "Custom" }); // Настраиваем обработчик для области сброса _dragDropManager.MakeDropTarget(DropTargetArea); // Подписываемся на события перетаскивания _dragDropService.DragStarted += OnDragStarted; _dragDropService.DragUpdated += OnDragUpdated; _dragDropService.DragCompleted += OnDragCompleted; _dragDropService.DragCancelled += OnDragCancelled; _dragDropService.DropTargetChanged += OnDropTargetChanged; _dragDropService.ErrorOccurred += OnDragDropError; UpdateStats(); StatusText.Text = "Drag & Drop system initialized"; } catch (Exception ex) { StatusText.Text = $"Failed to initialize Drag & Drop: {ex.Message}"; ShowErrorDialog("Initialization Error", ex.Message); } } private void OnDragStarted(object? sender, Core.DragDrop.Services.DragStartedEventArgs e) { _dragOperationCount++; UpdateStats(); StatusText.Text = $"Dragging started: {GetItemName(e.DragInfo.Data)}"; } private void OnDragUpdated(object? sender, Core.DragDrop.Services.DragUpdatedEventArgs e) { StatusText.Text = $"Dragging: {GetItemName(e.DragInfo.Data)} at ({e.Position.X:F0}, {e.Position.Y:F0})"; } private void OnDragCompleted(object? sender, Core.DragDrop.Services.DragCompletedEventArgs e) { // Проверяем, что элемент сброшен на нашу область if (e.DragInfo.Data is DragDropItem item) { // Добавляем элемент в список сброшенных DispatcherQueue.TryEnqueue(() => { if (!_droppedItems.Contains(item)) { _droppedItems.Add(item); // Удаляем из источников, если это не кастомный элемент if (_sourceItems.Contains(item) && item.Name != "Custom Element") { _sourceItems.Remove(item); } UpdateStats(); UpdateDropInstructions(); StatusText.Text = $"Item dropped: {item.Name}"; // Показываем уведомление ShowDropNotification(item); } }); } else { StatusText.Text = "Drag completed"; } } private async void ShowDropNotification(DragDropItem item) { var notification = new TeachingTip { Title = "Item Dropped", Subtitle = $"Successfully dropped '{item.Name}'", IsOpen = true, PreferredPlacement = TeachingTipPlacementMode.TopRight, Target = DropTargetArea }; // Автоматически скрываем через 3 секунды await Task.Delay(3000); notification.IsOpen = false; } private void OnDragCancelled(object? sender, Core.DragDrop.Services.DragCancelledEventArgs e) { StatusText.Text = "Drag cancelled"; } private void OnDropTargetChanged(object? sender, Core.DragDrop.Services.DropTargetChangedEventArgs e) { if (e.Target != null) { StatusText.Text = "Over drop target"; } else { StatusText.Text = "Not over drop target"; } } private async void OnDragDropError(object? sender, Core.DragDrop.Services.DragDropErrorEventArgs e) { StatusText.Text = $"Error: {e.Exception.Message}"; await ShowErrorDialog("Drag & Drop Error", e.Exception.Message); } private string GetItemName(object? data) { if (data is DragDropItem item) { return item.Name; } return data?.ToString() ?? "Unknown"; } private void UpdateStats() { DispatcherQueue.TryEnqueue(() => { DragStatsText.Text = $"Drag operations: {_dragOperationCount}"; DropStatsText.Text = $"Dropped items: {_droppedItems.Count}"; }); } private void UpdateDropInstructions() { DropInstructionsBorder.Visibility = _droppedItems.Count == 0 ? Visibility.Visible : Visibility.Collapsed; } private void OnThemeToggleToggled(object sender, RoutedEventArgs e) { if (sender is ToggleSwitch toggle) { try { var themeManager = ThemeManager.Current; if (toggle.IsOn) { themeManager.ApplyTheme("Fluent Dark"); } else { themeManager.ApplyTheme("Fluent"); } StatusText.Text = $"Theme changed to {(toggle.IsOn ? "Dark" : "Light")}"; } catch (Exception ex) { StatusText.Text = $"Failed to change theme: {ex.Message}"; } } } private async void OnStatsButtonClick(object sender, RoutedEventArgs e) { if (_dragDropService != null) { var stats = _dragDropService.GetStats(); var dialog = new ContentDialog { Title = "Drag & Drop Statistics", Content = new ScrollViewer { Content = new StackPanel { Spacing = 8, Children = { CreateStatRow("Total Drag Operations:", stats.TotalDragOperations.ToString()), CreateStatRow("Successful Drops:", stats.SuccessfulDrops.ToString()), CreateStatRow("Cancelled Operations:", stats.CancelledOperations.ToString()), CreateStatRow("Errors:", stats.ErrorCount.ToString()), CreateStatRow("Registered Targets:", stats.RegisteredTargets.ToString()), CreateStatRow("Average Operation Time:", stats.AverageOperationTime.TotalMilliseconds.ToString("F0") + "ms") } } }, PrimaryButtonText = "OK", XamlRoot = Content.XamlRoot }; await dialog.ShowAsync(); } } private StackPanel CreateStatRow(string label, string value) { return new StackPanel { Orientation = Orientation.Horizontal, Spacing = 10, Children = { new TextBlock { Text = label, FontWeight = Microsoft.UI.Text.FontWeights.SemiBold, Width = 200 }, new TextBlock { Text = value } } }; } private void OnRemoveItemClick(object sender, RoutedEventArgs e) { if (sender is Button button && button.Tag is DragDropItem item) { _droppedItems.Remove(item); UpdateStats(); UpdateDropInstructions(); // Добавляем обратно в источники, если это не кастомный элемент if (item.Name != "Custom Element" && !_sourceItems.Contains(item)) { _sourceItems.Add(item); } StatusText.Text = $"Item removed: {item.Name}"; } } private void OnResetPanelsClick(object sender, RoutedEventArgs e) { // Очищаем все сброшенные элементы foreach (var item in _droppedItems.ToList()) { if (item.Name != "Custom Element" && !_sourceItems.Contains(item)) { _sourceItems.Add(item); } } _droppedItems.Clear(); UpdateStats(); UpdateDropInstructions(); StatusText.Text = "Panels reset"; } private async Task ShowErrorDialog(string title, string message) { var dialog = new ContentDialog { Title = title, Content = message, PrimaryButtonText = "OK", XamlRoot = Content.XamlRoot }; await dialog.ShowAsync(); } // Метод для установки размера окна private void SetWindowSize(int width, int height) { try { var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd); var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId); appWindow.Resize(new Windows.Graphics.SizeInt32(width, height)); // Центрируем окно var displayArea = Microsoft.UI.Windowing.DisplayArea.GetFromWindowId(windowId, Microsoft.UI.Windowing.DisplayAreaFallback.Primary); var centerX = (displayArea.WorkArea.Width - width) / 2; var centerY = (displayArea.WorkArea.Height - height) / 2; appWindow.Move(new Windows.Graphics.PointInt32(centerX, centerY)); } catch (Exception ex) { // Игнорируем ошибки при установке размера окна System.Diagnostics.Debug.WriteLine($"Failed to set window size: {ex.Message}"); } } }