修改经常崩溃的问题,最大原因是并行路径计算(步骤中调用了NW的API)
This commit is contained in:
parent
d046e31d6c
commit
b449cf08ad
@ -225,6 +225,9 @@
|
||||
<Compile Include="src\UI\WPF\Collections\ThreadSafeObservableCollection.cs" />
|
||||
<Compile Include="src\UI\WPF\Collections\VirtualizedObservableCollection.cs" />
|
||||
|
||||
<!-- UI - WPF Interfaces -->
|
||||
<Compile Include="src\UI\WPF\Interfaces\IPropertyChangeNotifier.cs" />
|
||||
|
||||
<!-- UI - WPF Services -->
|
||||
<Compile Include="src\UI\WPF\Services\BindingExpressionOptimizer.cs" />
|
||||
<Compile Include="src\UI\WPF\Services\CrossViewModelSynchronizer.cs" />
|
||||
|
||||
@ -681,39 +681,17 @@ namespace NavisworksTransport
|
||||
// 1. 停用ToolPlugin并清理事件订阅
|
||||
DeactivateToolPlugin();
|
||||
|
||||
// 2. 额外的事件订阅清理 - 确保所有可能的重复订阅都被清除
|
||||
LogManager.WriteLog("[事件清理] 执行额外的事件订阅清理");
|
||||
// 2. 简化的事件订阅清理 - 移除危险的反射操作
|
||||
LogManager.WriteLog("[事件清理] 执行安全的事件订阅清理");
|
||||
try
|
||||
{
|
||||
// 移除所有可能的OnToolPluginMouseClicked订阅
|
||||
var mouseClickedEvent = typeof(PathClickToolPlugin).GetEvent("MouseClicked");
|
||||
if (mouseClickedEvent != null)
|
||||
{
|
||||
var eventField = typeof(PathClickToolPlugin).GetField("MouseClicked",
|
||||
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
|
||||
if (eventField != null)
|
||||
{
|
||||
var currentDelegate = (MulticastDelegate)eventField.GetValue(null);
|
||||
if (currentDelegate != null)
|
||||
{
|
||||
var invocationList = currentDelegate.GetInvocationList();
|
||||
LogManager.WriteLog($"[事件清理] 发现 {invocationList.Length} 个事件订阅者");
|
||||
|
||||
foreach (var handler in invocationList)
|
||||
{
|
||||
if (handler.Target == this && handler.Method.Name == "OnToolPluginMouseClicked")
|
||||
{
|
||||
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
|
||||
LogManager.WriteLog($"[事件清理] 移除了PathPlanningManager.OnToolPluginMouseClicked订阅");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 安全地移除事件订阅,多次取消订阅同一处理程序是安全的
|
||||
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
|
||||
LogManager.WriteLog("[事件清理] 已安全移除PathPlanningManager.OnToolPluginMouseClicked订阅");
|
||||
}
|
||||
catch (Exception cleanupEx)
|
||||
{
|
||||
LogManager.WriteLog($"[事件清理] 额外清理过程异常: {cleanupEx.Message}");
|
||||
LogManager.WriteLog($"[事件清理] 事件清理过程异常: {cleanupEx.Message}");
|
||||
}
|
||||
|
||||
// 注意:移除了PathEditState设置,StopClickTool只管理工具插件状态,不修改业务逻辑状态
|
||||
@ -2702,6 +2680,47 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制重新初始化ToolPlugin(公共接口,避免反射调用)
|
||||
/// </summary>
|
||||
/// <param name="subscribeToEvents">是否订阅事件</param>
|
||||
/// <returns>初始化是否成功</returns>
|
||||
public bool ForceReinitializeToolPlugin(bool subscribeToEvents = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog($"[工具插件重初始化] 开始强制重新初始化ToolPlugin,订阅事件: {subscribeToEvents}");
|
||||
|
||||
// 1. 停用当前ToolPlugin
|
||||
bool deactivated = DeactivateToolPlugin();
|
||||
LogManager.WriteLog($"[工具插件重初始化] 停用结果: {deactivated}");
|
||||
|
||||
// 2. 强制重置激活状态
|
||||
_isToolPluginActive = false;
|
||||
LogManager.WriteLog("[工具插件重初始化] 已重置激活状态标志");
|
||||
|
||||
// 3. 重新激活ToolPlugin
|
||||
bool activated = ActivateToolPlugin(subscribeToEvents);
|
||||
LogManager.WriteLog($"[工具插件重初始化] 激活结果(事件订阅: {subscribeToEvents}): {activated}");
|
||||
|
||||
if (activated)
|
||||
{
|
||||
LogManager.WriteLog("[工具插件重初始化] ToolPlugin重新初始化成功,已获得鼠标焦点");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[工具插件重初始化] ToolPlugin激活失败");
|
||||
}
|
||||
|
||||
return activated;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[工具插件重初始化] 重新初始化ToolPlugin失败: {ex.Message}", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 自动路径规划辅助方法
|
||||
@ -3157,6 +3176,14 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否有任何网格可视化已启用
|
||||
/// </summary>
|
||||
public bool IsAnyGridVisualizationEnabled
|
||||
{
|
||||
get { return _showWalkableGrid || _showObstacleGrid || _showUnknownGrid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新网格可视化(根据当前设置重新显示)
|
||||
/// </summary>
|
||||
|
||||
@ -436,25 +436,56 @@ namespace NavisworksTransport
|
||||
/// <param name="excludedPathIds">要保留的路径ID列表</param>
|
||||
public void ClearPathsExcept(params string[] excludedPathIds)
|
||||
{
|
||||
lock (_lockObject)
|
||||
try
|
||||
{
|
||||
var excludedSet = new HashSet<string>(excludedPathIds);
|
||||
var toRemove = _pathVisualizations.Keys.Where(id => !excludedSet.Contains(id)).ToList();
|
||||
|
||||
int removedCount = 0;
|
||||
foreach (var pathId in toRemove)
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_pathVisualizations.Remove(pathId))
|
||||
// 防御性编程:处理空参数
|
||||
if (excludedPathIds == null)
|
||||
{
|
||||
removedCount++;
|
||||
excludedPathIds = new string[0];
|
||||
}
|
||||
|
||||
var excludedSet = new HashSet<string>(excludedPathIds.Where(id => !string.IsNullOrEmpty(id)));
|
||||
var toRemove = _pathVisualizations.Keys.Where(id => !excludedSet.Contains(id)).ToList();
|
||||
|
||||
// 记录当前状态
|
||||
LogManager.WriteLog($"[选择性清空] 当前路径数: {_pathVisualizations.Count}, 排除路径数: {excludedSet.Count}, 待清理路径数: {toRemove.Count}");
|
||||
|
||||
// 检查排除的路径是否实际存在
|
||||
var existingExcluded = excludedSet.Where(id => _pathVisualizations.ContainsKey(id)).ToList();
|
||||
var nonExistingExcluded = excludedSet.Where(id => !_pathVisualizations.ContainsKey(id)).ToList();
|
||||
|
||||
if (nonExistingExcluded.Any())
|
||||
{
|
||||
LogManager.WriteLog($"[选择性清空] 注意:尝试保留的路径不存在: {string.Join(", ", nonExistingExcluded)}");
|
||||
}
|
||||
|
||||
if (existingExcluded.Any())
|
||||
{
|
||||
LogManager.WriteLog($"[选择性清空] 将保留的现有路径: {string.Join(", ", existingExcluded)}");
|
||||
}
|
||||
|
||||
int removedCount = 0;
|
||||
foreach (var pathId in toRemove)
|
||||
{
|
||||
if (_pathVisualizations.Remove(pathId))
|
||||
{
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[选择性清空] 成功清空{removedCount}个路径,保留{_pathVisualizations.Count}个路径");
|
||||
if (removedCount > 0)
|
||||
{
|
||||
RequestViewRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[选择性清空] 清空{removedCount}个路径,保留{excludedSet.Count}个路径");
|
||||
if (removedCount > 0)
|
||||
{
|
||||
RequestViewRefresh();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[选择性清空] 清理路径时发生异常: {ex.Message}", ex);
|
||||
// 即使发生异常也不抛出,避免影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@ using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NavisworksTransport.UI.WPF.Interfaces;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.Core.UIUpdate.Updates
|
||||
{
|
||||
@ -89,28 +91,30 @@ namespace NavisworksTransport.Core.UIUpdate.Updates
|
||||
|
||||
await uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
// 通过反射调用OnPropertyChanged方法(如果ViewModel是ViewModelBase的子类)
|
||||
var onPropertyChangedMethod = ViewModel.GetType().GetMethod("OnPropertyChanged",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Protected);
|
||||
|
||||
if (onPropertyChangedMethod != null)
|
||||
// 优先使用IPropertyChangeNotifier接口(安全方式)
|
||||
if (ViewModel is IPropertyChangeNotifier notifier)
|
||||
{
|
||||
onPropertyChangedMethod.Invoke(ViewModel, new object[] { PropertyName });
|
||||
notifier.NotifyPropertyChanged(PropertyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有找到OnPropertyChanged方法,尝试直接触发PropertyChanged事件
|
||||
var eventField = ViewModel.GetType().GetField("PropertyChanged",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
|
||||
if (eventField != null)
|
||||
// 如果ViewModel不实现IPropertyChangeNotifier,直接触发PropertyChanged事件
|
||||
// 这是最后的备用方案,避免使用反射
|
||||
try
|
||||
{
|
||||
var eventDelegate = eventField.GetValue(ViewModel) as PropertyChangedEventHandler;
|
||||
eventDelegate?.Invoke(ViewModel, new PropertyChangedEventArgs(PropertyName));
|
||||
// 创建PropertyChangedEventArgs并尝试通过事件触发
|
||||
var eventArgs = new PropertyChangedEventArgs(PropertyName);
|
||||
|
||||
// 如果ViewModel有公共的PropertyChanged事件,我们可以通过显式访问来触发
|
||||
// 但是由于事件的限制,我们无法从外部直接调用事件
|
||||
// 这种情况下记录警告,提醒开发者ViewModel应该实现IPropertyChangeNotifier
|
||||
LogManager.Warning($"ViewModel ({ViewModel.GetType().Name}) 未实现IPropertyChangeNotifier接口,无法安全触发属性变更通知");
|
||||
throw new InvalidOperationException($"ViewModel必须实现IPropertyChangeNotifier接口才能使用ViewModelRefreshOperation");
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException("无法找到触发PropertyChanged事件的方法");
|
||||
LogManager.Error($"触发PropertyChanged事件失败: {ex.Message}");
|
||||
throw new InvalidOperationException("无法找到触发PropertyChanged事件的方法,请确保ViewModel实现IPropertyChangeNotifier接口");
|
||||
}
|
||||
}
|
||||
}, context.TimeoutMilliseconds);
|
||||
|
||||
@ -1120,103 +1120,105 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
LogManager.Info($"[包围盒障碍物处理] 输入统计 - 总模型项: {totalItems}, 将排除通道元素: {channelItemsSet.Count}");
|
||||
|
||||
// 临时修改:注释掉并行处理以避免线程安全问题
|
||||
// 使用并行处理提高性能,使用50%的CPU内核以平衡性能和稳定性
|
||||
var lockObject = new object();
|
||||
Parallel.ForEach(allItems, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) }, item =>
|
||||
// var lockObject = new object();
|
||||
// Parallel.ForEach(allItems, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) }, item =>
|
||||
|
||||
// 改为单线程处理
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
try
|
||||
{
|
||||
Interlocked.Increment(ref processedItems);
|
||||
processedItems++; // 改为普通递增
|
||||
|
||||
// 1. 排除通道项(包括容器节点和子节点)
|
||||
if (channelItemsSet.Contains(item) || IsChildOfChannelItems(item, channelItemsSet))
|
||||
{
|
||||
Interlocked.Increment(ref channelExcludedItems);
|
||||
return;
|
||||
channelExcludedItems++; // 改为普通递增
|
||||
continue; // 改为continue
|
||||
}
|
||||
|
||||
// 2. 检查是否有几何体,只对有几何体的元素进行处理
|
||||
if (!item.HasGeometry)
|
||||
{
|
||||
Interlocked.Increment(ref noGeometryItems);
|
||||
return;
|
||||
noGeometryItems++; // 改为普通递增
|
||||
continue; // 改为continue
|
||||
}
|
||||
|
||||
// 2.5 检查是否是容器节点(有子节点的元素通常是组/层级)
|
||||
// 容器节点的包围盒会包含所有子元素,不应作为障碍物
|
||||
if (item.Children.Any())
|
||||
{
|
||||
Interlocked.Increment(ref containerItems);
|
||||
return; // 跳过容器节点
|
||||
containerItems++; // 改为普通递增
|
||||
continue; // 跳过容器节点,改为continue
|
||||
}
|
||||
|
||||
// 3. 获取包围盒
|
||||
var bbox = item.BoundingBox();
|
||||
if (bbox == null)
|
||||
{
|
||||
Interlocked.Increment(ref noBoundsItems);
|
||||
return;
|
||||
noBoundsItems++; // 改为普通递增
|
||||
continue; // 改为continue
|
||||
}
|
||||
|
||||
// 4. 检查高度范围(重要优化:提前过滤不相关的模型项)
|
||||
if (!IsInScanHeightRange(bbox, gridMap, scanHeight))
|
||||
{
|
||||
Interlocked.Increment(ref outOfHeightRangeItems);
|
||||
return;
|
||||
outOfHeightRangeItems++; // 改为普通递增
|
||||
continue; // 改为continue
|
||||
}
|
||||
|
||||
// 5. 计算包围盒覆盖的网格单元
|
||||
var coveredCells = CalculateBoundingBoxGridCoverage(bbox, gridMap);
|
||||
if (coveredCells.Count == 0)
|
||||
{
|
||||
return; // 没有覆盖任何网格单元
|
||||
continue; // 没有覆盖任何网格单元,改为continue
|
||||
}
|
||||
|
||||
// 6. 更新网格状态和高度信息(线程安全)
|
||||
// 6. 更新网格状态和高度信息(移除线程安全锁)
|
||||
var updatedCells = 0;
|
||||
foreach (var (x, y) in coveredCells)
|
||||
{
|
||||
lock (lockObject) // 确保网格更新的线程安全
|
||||
// 移除lock,单线程不需要锁
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 只更新通道网格,跳过已是障碍物的网格
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
// 标记为障碍物
|
||||
cell.IsWalkable = false;
|
||||
cell.CellType = CategoryAttributeManager.LogisticsElementType.障碍物;
|
||||
cell.Cost = double.MaxValue;
|
||||
cell.IsInChannel = false;
|
||||
cell.RelatedModelItem = item;
|
||||
|
||||
// 只更新通道网格,跳过已是障碍物的网格
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel)
|
||||
{
|
||||
// 标记为障碍物
|
||||
cell.IsWalkable = false;
|
||||
cell.CellType = CategoryAttributeManager.LogisticsElementType.障碍物;
|
||||
cell.Cost = double.MaxValue;
|
||||
cell.IsInChannel = false;
|
||||
cell.RelatedModelItem = item;
|
||||
|
||||
// 记录高度信息(支持未来多楼层扩展)
|
||||
if (cell.PassableHeights == null)
|
||||
cell.PassableHeights = new List<HeightInterval>();
|
||||
|
||||
// 计算相对于通道地面的高度区间
|
||||
var groundHeight = cell.WorldPosition.Z; // 假设WorldPosition已设置为地面高度
|
||||
cell.PassableHeights.Add(new HeightInterval(
|
||||
bbox.Min.Z - groundHeight,
|
||||
bbox.Max.Z - groundHeight
|
||||
));
|
||||
|
||||
gridMap.Cells[x, y] = cell;
|
||||
updatedCells++;
|
||||
}
|
||||
// 记录高度信息(支持未来多楼层扩展)
|
||||
if (cell.PassableHeights == null)
|
||||
cell.PassableHeights = new List<HeightInterval>();
|
||||
|
||||
// 计算相对于通道地面的高度区间
|
||||
var groundHeight = cell.WorldPosition.Z; // 假设WorldPosition已设置为地面高度
|
||||
cell.PassableHeights.Add(new HeightInterval(
|
||||
bbox.Min.Z - groundHeight,
|
||||
bbox.Max.Z - groundHeight
|
||||
));
|
||||
|
||||
gridMap.Cells[x, y] = cell;
|
||||
updatedCells++;
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedCells > 0)
|
||||
{
|
||||
Interlocked.Increment(ref obstacleItems);
|
||||
obstacleItems++; // 改为普通递增
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Debug($"[包围盒障碍物处理] 处理模型项失败: {item?.DisplayName ?? "NULL"}, {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 输出详细统计信息
|
||||
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
|
||||
|
||||
17
src/UI/WPF/Interfaces/IPropertyChangeNotifier.cs
Normal file
17
src/UI/WPF/Interfaces/IPropertyChangeNotifier.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供属性变更通知接口,允许外部组件安全地触发属性变更事件
|
||||
/// 用于替代反射调用,提供更安全的属性变更通知机制
|
||||
/// </summary>
|
||||
public interface IPropertyChangeNotifier : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// 触发指定属性的变更通知
|
||||
/// </summary>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
void NotifyPropertyChanged(string propertyName);
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,8 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using NavisworksTransport.UI.WPF.ViewModels;
|
||||
using NavisworksTransport.UI.WPF.Interfaces;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Services
|
||||
{
|
||||
@ -468,13 +470,20 @@ namespace NavisworksTransport.UI.WPF.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用反射获取PropertyChanged事件并触发
|
||||
var propertyChangedField = target.GetType().GetField("PropertyChanged",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
|
||||
if (propertyChangedField?.GetValue(target) is PropertyChangedEventHandler handler)
|
||||
// 优先使用IPropertyChangeNotifier接口(安全方式,避免反射)
|
||||
if (target is IPropertyChangeNotifier notifier)
|
||||
{
|
||||
handler.Invoke(target, new PropertyChangedEventArgs(propertyName));
|
||||
notifier.NotifyPropertyChanged(propertyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果目标对象不实现IPropertyChangeNotifier接口,记录警告
|
||||
// 我们无法安全地触发PropertyChanged事件,因为事件只能从内部触发
|
||||
LogManager.Warning($"目标对象 ({target.GetType().Name}) 未实现IPropertyChangeNotifier接口,无法触发属性变更通知: {propertyName}");
|
||||
LogManager.Info("建议更新ViewModel以实现IPropertyChangeNotifier接口,以支持智能数据绑定优化功能");
|
||||
|
||||
// 不使用反射,而是直接跳过这个属性的变更通知
|
||||
// 这是更安全的做法,避免潜在的线程安全问题
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -832,8 +832,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
ClearTemporaryAutoPathMarkers();
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle"); // 清理历史路径但保留网格可视化
|
||||
LogManager.WriteLog("[自动路径规划] 已清理历史路径对象(保留网格可视化)");
|
||||
// 检查是否有网格可视化启用,只在启用时保留网格可视化路径
|
||||
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle");
|
||||
LogManager.WriteLog("[自动路径规划] 已清理历史路径对象(保留网格可视化)");
|
||||
}
|
||||
else
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
|
||||
LogManager.WriteLog("[自动路径规划] 已清理历史路径对象(无网格可视化需保留)");
|
||||
}
|
||||
}
|
||||
|
||||
// 调用PathPlanningManager的自动路径规划功能
|
||||
@ -910,8 +919,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 确保失败后也清理所有可能的残留路径对象
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle");
|
||||
LogManager.WriteLog("[自动路径规划] 规划失败后已清理渲染对象(保留网格可视化)");
|
||||
// 检查是否有网格可视化启用,只在启用时保留网格可视化路径
|
||||
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle");
|
||||
LogManager.WriteLog("[自动路径规划] 规划失败后已清理渲染对象(保留网格可视化)");
|
||||
}
|
||||
else
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
|
||||
LogManager.WriteLog("[自动路径规划] 规划失败后已清理渲染对象(无网格可视化需保留)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "自动路径规划");
|
||||
@ -1902,40 +1920,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
LogManager.WriteLog("[事件清理] 开始清理自动路径事件订阅");
|
||||
|
||||
// 移除所有可能的OnAutoPathMouseClicked订阅
|
||||
// 简单安全地移除事件订阅
|
||||
// 由于C#事件处理机制,多次取消订阅同一个处理程序是安全的
|
||||
// 即使处理程序未订阅,取消操作也不会抛出异常
|
||||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
|
||||
|
||||
// 使用反射检查并清理重复订阅
|
||||
var mouseClickedEvent = typeof(PathClickToolPlugin).GetEvent("MouseClicked");
|
||||
if (mouseClickedEvent != null)
|
||||
{
|
||||
var eventField = typeof(PathClickToolPlugin).GetField("MouseClicked",
|
||||
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
|
||||
if (eventField != null)
|
||||
{
|
||||
var currentDelegate = (MulticastDelegate)eventField.GetValue(null);
|
||||
if (currentDelegate != null)
|
||||
{
|
||||
var invocationList = currentDelegate.GetInvocationList();
|
||||
LogManager.WriteLog($"[事件清理] 发现 {invocationList.Length} 个事件订阅者");
|
||||
|
||||
foreach (var handler in invocationList)
|
||||
{
|
||||
if (handler.Target == this && handler.Method.Name == "OnAutoPathMouseClicked")
|
||||
{
|
||||
PathClickToolPlugin.MouseClicked -= (EventHandler<PickItemResult>)handler;
|
||||
LogManager.WriteLog($"[事件清理] 移除了PathEditingViewModel.OnAutoPathMouseClicked订阅");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.WriteLog("[事件清理] 已安全移除PathClickToolPlugin.MouseClicked事件订阅");
|
||||
LogManager.WriteLog("[事件清理] 自动路径事件订阅清理完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[事件清理] 清理自动路径事件订阅失败: {ex.Message}");
|
||||
LogManager.Error($"[事件清理] 清理自动路径事件订阅失败: {ex.Message}", ex);
|
||||
// 记录异常但不抛出,避免影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
@ -2466,48 +2462,36 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// <returns>初始化是否成功</returns>
|
||||
private bool ForceReinitializeToolPlugin(bool subscribeToEvents = false)
|
||||
{
|
||||
LogManager.Info($"开始强制重新初始化ToolPlugin以确保获得鼠标焦点,订阅事件: {subscribeToEvents}");
|
||||
try
|
||||
try
|
||||
{
|
||||
// 使用反射调用私有方法强制重新激活ToolPlugin
|
||||
var deactivateMethod = _pathPlanningManager.GetType().GetMethod("DeactivateToolPlugin",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (deactivateMethod != null)
|
||||
{
|
||||
deactivateMethod.Invoke(_pathPlanningManager, null);
|
||||
LogManager.Info("已停用ToolPlugin");
|
||||
}
|
||||
LogManager.Info($"开始强制重新初始化ToolPlugin以确保获得鼠标焦点,订阅事件: {subscribeToEvents}");
|
||||
|
||||
// 重置激活状态标志
|
||||
var isActiveField = _pathPlanningManager.GetType().GetField("_isToolPluginActive",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (isActiveField != null)
|
||||
// 使用PathPlanningManager的公共方法,避免反射调用
|
||||
if (_pathPlanningManager != null)
|
||||
{
|
||||
isActiveField.SetValue(_pathPlanningManager, false);
|
||||
LogManager.Info("已重置ToolPlugin激活状态标志");
|
||||
}
|
||||
|
||||
// 重新激活ToolPlugin,根据参数决定是否订阅事件
|
||||
var activateMethod = _pathPlanningManager.GetType().GetMethod("ActivateToolPlugin",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (activateMethod != null)
|
||||
{
|
||||
// 调用带参数的方法,传递subscribeToEvents参数
|
||||
var result = activateMethod.Invoke(_pathPlanningManager, new object[] { subscribeToEvents });
|
||||
LogManager.Info($"ToolPlugin重新激活结果(事件订阅: {subscribeToEvents}): {result}");
|
||||
if (!(bool)result)
|
||||
bool result = _pathPlanningManager.ForceReinitializeToolPlugin(subscribeToEvents);
|
||||
LogManager.Info($"ToolPlugin重新初始化结果(事件订阅: {subscribeToEvents}): {result}");
|
||||
|
||||
if (result)
|
||||
{
|
||||
LogManager.Error("ToolPlugin激活失败");
|
||||
return false;
|
||||
LogManager.Info("ToolPlugin重新初始化成功,已获得鼠标焦点");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Error("ToolPlugin初始化失败");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Error("PathPlanningManager为null,无法重新初始化ToolPlugin");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogManager.Info("ToolPlugin重新初始化成功,已获得鼠标焦点");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"重新初始化ToolPlugin失败: {ex.Message}");
|
||||
LogManager.Error($"重新初始化ToolPlugin失败: {ex.Message}", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using NavisworksTransport.Core;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.UI.WPF.Services;
|
||||
using NavisworksTransport.UI.WPF.Interfaces;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
@ -15,7 +16,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// 集成UIStateManager提供线程安全的属性更新和防重入机制
|
||||
/// 集成智能数据绑定优化功能,支持延迟更新、批量更新和条件更新
|
||||
/// </summary>
|
||||
public abstract class ViewModelBase : INotifyPropertyChanged
|
||||
public abstract class ViewModelBase : IPropertyChangeNotifier
|
||||
{
|
||||
#region 字段和属性
|
||||
|
||||
@ -145,6 +146,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 公共接口:触发指定属性的变更通知(供外部组件使用,替代反射调用)
|
||||
/// </summary>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
public virtual void NotifyPropertyChanged(string propertyName)
|
||||
{
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步触发属性变更通知(确保在UI线程上执行完成)
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user