359 lines
16 KiB
C#
359 lines
16 KiB
C#
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;
|
||
}
|
||
}
|
||
} |