diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj index e524645..795647e 100644 --- a/NavisworksTransportPlugin.csproj +++ b/NavisworksTransportPlugin.csproj @@ -225,6 +225,9 @@ + + + diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs index 184b923..35c420c 100644 --- a/src/Core/PathPlanningManager.cs +++ b/src/Core/PathPlanningManager.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 } } + /// + /// 强制重新初始化ToolPlugin(公共接口,避免反射调用) + /// + /// 是否订阅事件 + /// 初始化是否成功 + 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 } } + /// + /// 检查是否有任何网格可视化已启用 + /// + public bool IsAnyGridVisualizationEnabled + { + get { return _showWalkableGrid || _showObstacleGrid || _showUnknownGrid; } + } + /// /// 刷新网格可视化(根据当前设置重新显示) /// diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs index 96d4cd8..47020b4 100644 --- a/src/Core/PathPointRenderPlugin.cs +++ b/src/Core/PathPointRenderPlugin.cs @@ -436,25 +436,56 @@ namespace NavisworksTransport /// 要保留的路径ID列表 public void ClearPathsExcept(params string[] excludedPathIds) { - lock (_lockObject) + try { - var excludedSet = new HashSet(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(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); + // 即使发生异常也不抛出,避免影响主流程 } } diff --git a/src/Core/UIUpdate/Updates/ViewModelRefreshOperation.cs b/src/Core/UIUpdate/Updates/ViewModelRefreshOperation.cs index 6474d96..f653e8b 100644 --- a/src/Core/UIUpdate/Updates/ViewModelRefreshOperation.cs +++ b/src/Core/UIUpdate/Updates/ViewModelRefreshOperation.cs @@ -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); diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs index cd7d272..2912486 100644 --- a/src/PathPlanning/GridMapGenerator.cs +++ b/src/PathPlanning/GridMapGenerator.cs @@ -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(); - - // 计算相对于通道地面的高度区间 - 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(); + + // 计算相对于通道地面的高度区间 + 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; diff --git a/src/UI/WPF/Interfaces/IPropertyChangeNotifier.cs b/src/UI/WPF/Interfaces/IPropertyChangeNotifier.cs new file mode 100644 index 0000000..3ca23ea --- /dev/null +++ b/src/UI/WPF/Interfaces/IPropertyChangeNotifier.cs @@ -0,0 +1,17 @@ +using System.ComponentModel; + +namespace NavisworksTransport.UI.WPF.Interfaces +{ + /// + /// 提供属性变更通知接口,允许外部组件安全地触发属性变更事件 + /// 用于替代反射调用,提供更安全的属性变更通知机制 + /// + public interface IPropertyChangeNotifier : INotifyPropertyChanged + { + /// + /// 触发指定属性的变更通知 + /// + /// 属性名称 + void NotifyPropertyChanged(string propertyName); + } +} \ No newline at end of file diff --git a/src/UI/WPF/Services/SmartDataBindingOptimizer.cs b/src/UI/WPF/Services/SmartDataBindingOptimizer.cs index 89fed65..b927665 100644 --- a/src/UI/WPF/Services/SmartDataBindingOptimizer.cs +++ b/src/UI/WPF/Services/SmartDataBindingOptimizer.cs @@ -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) diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index b6ba215..ebf8763 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -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)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 /// 初始化是否成功 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; } } diff --git a/src/UI/WPF/ViewModels/ViewModelBase.cs b/src/UI/WPF/ViewModels/ViewModelBase.cs index f5fc864..71c5bbf 100644 --- a/src/UI/WPF/ViewModels/ViewModelBase.cs +++ b/src/UI/WPF/ViewModels/ViewModelBase.cs @@ -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提供线程安全的属性更新和防重入机制 /// 集成智能数据绑定优化功能,支持延迟更新、批量更新和条件更新 /// - public abstract class ViewModelBase : INotifyPropertyChanged + public abstract class ViewModelBase : IPropertyChangeNotifier { #region 字段和属性 @@ -145,6 +146,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } + /// + /// 公共接口:触发指定属性的变更通知(供外部组件使用,替代反射调用) + /// + /// 属性名称 + public virtual void NotifyPropertyChanged(string propertyName) + { + OnPropertyChanged(propertyName); + } + /// /// 同步触发属性变更通知(确保在UI线程上执行完成) ///