Files
AohDrllTools/DragDropHelper.cs
2025-12-07 20:25:27 +08:00

359 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using WinForms = System.Windows.Forms;
namespace DrillTools
{
/// <summary>
/// 拖放帮助类
/// </summary>
public static class DragDropHelper
{
private static System.Windows.Point _startPoint;
private static bool _isDragging;
private static DragToolTipWindow? _dragToolTipWindow;
private static Window? _ownerWindow;
private static System.Windows.Shapes.Rectangle? _insertionIndicator;
private static int _insertionIndex = -1;
private static DateTime? _lastUpdateTime;
/// <summary>
/// 启用 ListView 的拖放功能
/// </summary>
/// <typeparam name="T">数据项类型</typeparam>
/// <param name="listView">ListView 控件</param>
/// <param name="insertionIndicator">插入位置指示器</param>
public static void EnableDragDrop<T>(System.Windows.Controls.ListView listView, System.Windows.Shapes.Rectangle insertionIndicator) where T : class
{
_insertionIndicator = insertionIndicator;
_ownerWindow = Window.GetWindow(listView);
listView.PreviewMouseLeftButtonDown += (sender, e) =>
{
_startPoint = e.GetPosition(null);
_isDragging = false;
};
listView.PreviewMouseMove += (sender, e) =>
{
if (e.LeftButton == MouseButtonState.Pressed && !_isDragging)
{
System.Windows.Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
_isDragging = true;
var listViewItem = FindAncestor<System.Windows.Controls.ListViewItem>((DependencyObject)e.OriginalSource);
if (listViewItem != null)
{
var item = listView.ItemContainerGenerator.ItemFromContainer(listViewItem) as T;
if (item != null)
{
// 获取屏幕坐标
var screenPosition = listView.PointToScreen(e.GetPosition(listView));
// 创建并显示拖动提示
CreateDragToolTip(item, screenPosition);
// 使用 QueryContinueDrag 事件来更新位置
System.Windows.QueryContinueDragEventHandler queryContinueHandler = null;
queryContinueHandler = (sender, e) =>
{
if (_dragToolTipWindow != null)
{
// 获取当前鼠标位置
var currentPos = WinForms.Control.MousePosition;
_dragToolTipWindow.UpdatePosition(new System.Windows.Point(currentPos.X, currentPos.Y));
}
// 如果拖放结束,移除事件处理
if (e.Action == System.Windows.DragAction.Drop || e.Action == System.Windows.DragAction.Cancel)
{
DragDrop.RemoveQueryContinueDragHandler(listView, queryContinueHandler);
}
};
DragDrop.AddQueryContinueDragHandler(listView, queryContinueHandler);
System.Windows.DataObject data = new System.Windows.DataObject(typeof(T), item);
DragDrop.DoDragDrop(listViewItem, data, System.Windows.DragDropEffects.Move);
// 清理拖动提示
CleanupDragToolTip();
}
}
}
}
};
listView.DragOver += (sender, e) =>
{
if (e.Data.GetDataPresent(typeof(T)))
{
e.Effects = System.Windows.DragDropEffects.Move;
// 更新插入位置指示器
UpdateInsertionIndicator(listView, e.GetPosition(listView));
}
else
{
e.Effects = System.Windows.DragDropEffects.None;
}
e.Handled = true;
};
listView.DragLeave += (sender, e) =>
{
HideInsertionIndicator();
};
listView.Drop += (sender, e) =>
{
try
{
// 隐藏插入指示器
HideInsertionIndicator();
Debug.WriteLine($"[DragDrop] Drop事件触发 - OriginalSource类型: {e.OriginalSource?.GetType().Name}");
Debug.WriteLine($"[DragDrop] Drop位置: ({e.GetPosition(listView).X}, {e.GetPosition(listView).Y})");
if (e.Data.GetDataPresent(typeof(T)))
{
var droppedItem = e.Data.GetData(typeof(T)) as T;
if (droppedItem != null)
{
Debug.WriteLine($"[DragDrop] 拖放项: {droppedItem}");
// 检查是否是机台码刀具,如果是则不允许移动
if (droppedItem is ToolItem toolItem && toolItem.ToolType == ToolType.MachineCode)
{
Debug.WriteLine("[DragDrop] 机台码刀具不允许移动");
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
_isDragging = false;
return;
}
var targetItem = FindAncestor<System.Windows.Controls.ListViewItem>((DependencyObject)e.OriginalSource);
string ss = targetItem == null ? "NULL" : "找到";
Debug.WriteLine($"[DragDrop] 目标ListViewItem: {ss}");
if (targetItem == null)
{
Debug.WriteLine("[DragDrop] 警告: 目标项为空,可能是拖放到列表外部");
_isDragging = false;
return;
}
var targetIndex = listView.ItemContainerGenerator.IndexFromContainer(targetItem);
Debug.WriteLine($"[DragDrop] 目标索引: {targetIndex}");
var sourceIndex = listView.Items.IndexOf(droppedItem);
Debug.WriteLine($"[DragDrop] 源索引: {sourceIndex}");
// 检查目标位置是否是机台码刀具
var viewModel = listView.DataContext as MainWindowViewModel;
if (viewModel != null && viewModel.Tools.Count > targetIndex)
{
var targetTool = viewModel.Tools[targetIndex];
if (targetTool.ToolType == ToolType.MachineCode)
{
Debug.WriteLine("[DragDrop] 不能移动到机台码刀具位置");
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
_isDragging = false;
return;
}
}
if (sourceIndex != -1 && targetIndex != -1 && sourceIndex != targetIndex)
{
viewModel?.ReorderTools(sourceIndex, targetIndex);
Debug.WriteLine($"[DragDrop] 重新排序完成: {sourceIndex} -> {targetIndex}");
}
else
{
Debug.WriteLine($"[DragDrop] 跳过重新排序 - 源索引: {sourceIndex}, 目标索引: {targetIndex}");
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"[DragDrop] 异常: {ex.GetType().Name} - {ex.Message}");
Debug.WriteLine($"[DragDrop] 堆栈跟踪: {ex.StackTrace}");
System.Windows.MessageBox.Show($"拖放操作发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
_isDragging = false;
}
};
}
/// <summary>
/// 查找指定类型的父元素
/// </summary>
/// <typeparam name="T">父元素类型</typeparam>
/// <param name="current">当前元素</param>
/// <returns>找到的父元素,如果未找到则返回 null</returns>
private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
{
Debug.WriteLine($"[FindAncestor] 开始查找类型 {typeof(T).Name},起始元素类型: {current?.GetType().Name ?? "NULL"}");
int depth = 0;
while (current != null)
{
Debug.WriteLine($"[FindAncestor] 深度 {depth}: 当前类型 {current.GetType().Name}");
if (current is T ancestor)
{
Debug.WriteLine($"[FindAncestor] 找到匹配的祖先类型 {typeof(T).Name}");
return ancestor;
}
current = VisualTreeHelper.GetParent(current);
depth++;
if (depth > 10) // 防止无限循环
{
Debug.WriteLine("[FindAncestor] 警告: 搜索深度超过10层停止搜索");
break;
}
}
Debug.WriteLine($"[FindAncestor] 未找到类型 {typeof(T).Name} 的祖先");
return null;
}
/// <summary>
/// 处理主窗口鼠标移动事件,更新拖动提示位置
/// </summary>
private static void OnOwnerWindowMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (_dragToolTipWindow != null && _isDragging)
{
// 获取屏幕坐标
var windowPosition = e.GetPosition(_ownerWindow);
var screenPosition = _ownerWindow.PointToScreen(windowPosition);
_dragToolTipWindow.UpdatePosition(screenPosition);
}
}
/// <summary>
/// 创建拖动提示窗口
/// </summary>
private static void CreateDragToolTip<T>(T item, System.Windows.Point position) where T : class
{
if (item is ToolItem toolItem && _ownerWindow != null)
{
_dragToolTipWindow = new DragToolTipWindow();
_dragToolTipWindow.SetToolInfo(toolItem);
_dragToolTipWindow.UpdatePosition(position);
// 不设置 Owner这样窗口可以独立定位
_dragToolTipWindow.Show();
}
}
/// <summary>
/// 清理拖动提示窗口
/// </summary>
private static void CleanupDragToolTip()
{
if (_dragToolTipWindow != null)
{
_dragToolTipWindow.Close();
_dragToolTipWindow = null;
}
if (_ownerWindow != null)
{
_ownerWindow.MouseMove -= OnOwnerWindowMouseMove;
}
}
/// <summary>
/// 更新插入位置指示器
/// </summary>
private static void UpdateInsertionIndicator(System.Windows.Controls.ListView listView, System.Windows.Point position)
{
if (_insertionIndicator == null) return;
// 使用节流机制,避免过于频繁的更新
if (_lastUpdateTime != null && DateTime.Now - _lastUpdateTime < TimeSpan.FromMilliseconds(16))
return;
_lastUpdateTime = DateTime.Now;
try
{
// 使用 VisualTreeHelper 找到鼠标下的元素
var element = listView.InputHitTest(position) as DependencyObject;
var listViewItem = FindAncestor<System.Windows.Controls.ListViewItem>(element);
if (listViewItem != null)
{
var index = listView.ItemContainerGenerator.IndexFromContainer(listViewItem);
var itemBounds = listViewItem.TransformToAncestor(listView)
.TransformBounds(new Rect(0, 0, listViewItem.ActualWidth, listViewItem.ActualHeight));
// 判断是在上半部分还是下半部分
bool insertAfter = position.Y > itemBounds.Top + itemBounds.Height / 2;
int targetIndex = insertAfter ? index + 1 : index;
if (targetIndex != _insertionIndex)
{
_insertionIndex = targetIndex;
// 更新指示器位置
_insertionIndicator.Visibility = Visibility.Visible;
// 计算指示器位置
double indicatorTop = insertAfter ? itemBounds.Bottom : itemBounds.Top;
// 使用 VerticalAlignment 和 Margin 结合来定位指示器
_insertionIndicator.VerticalAlignment = VerticalAlignment.Top;
_insertionIndicator.Margin = new Thickness(0, indicatorTop, 0, 0);
}
}
else
{
// 如果没有找到目标项,检查是否在列表末尾
if (position.Y > listView.ActualHeight - 20)
{
_insertionIndex = listView.Items.Count;
// 在列表末尾显示指示器
_insertionIndicator.Visibility = Visibility.Visible;
_insertionIndicator.VerticalAlignment = VerticalAlignment.Top;
_insertionIndicator.Margin = new Thickness(0, listView.ActualHeight - 2, 0, 0);
}
else
{
HideInsertionIndicator();
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"[DragDrop] 更新插入指示器时发生错误: {ex.Message}");
HideInsertionIndicator();
}
}
/// <summary>
/// 隐藏插入位置指示器
/// </summary>
private static void HideInsertionIndicator()
{
if (_insertionIndicator != null)
{
_insertionIndicator.Visibility = Visibility.Collapsed;
}
_insertionIndex = -1;
}
}
}