diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj
index c397615..5b2209b 100644
--- a/NavisworksTransportPlugin.csproj
+++ b/NavisworksTransportPlugin.csproj
@@ -117,6 +117,9 @@
+
+
+
diff --git a/doc/design/2026/NavisworksAPI使用方法.md b/doc/design/2026/NavisworksAPI使用方法.md
index 7bbb3f5..5334278 100644
--- a/doc/design/2026/NavisworksAPI使用方法.md
+++ b/doc/design/2026/NavisworksAPI使用方法.md
@@ -1839,3 +1839,35 @@ using Autodesk.Navisworks.Api.Controls;
SetHidden(hidden, true);
}
```
+
+### 使用进度条
+
+When instantiated a progress bar in the Navisworks style is displayed to the user inside the Navisworks main application. This can then be updated by the program or plugin with textual information as well as the percentage completed of the process.
+
+```csharp
+///
+/// Shows the basic concept of using the Progress class
+///
+private static void SimpleProgressExample()
+{
+ //first get reference to an instance of the Progress class
+ Progress progress = Autodesk.Navisworks.Api.Application.BeginProgress();
+
+ double stage;
+
+ for (stage = 0.0; stage < 1.0; stage += 0.1)
+ {
+ //Update progress bar
+ progress.Update(stage);
+
+ //Do something for a period of time
+ System.Threading.Thread.Sleep(1000);
+ }
+
+ //Update progress bar to 100%
+ progress.Update(1.0);
+
+ //notify the API that the operation has finished
+ Autodesk.Navisworks.Api.Application.EndProgress();
+}
+```
diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs
index 40a2455..a79eaef 100644
--- a/src/Core/Animation/PathAnimationManager.cs
+++ b/src/Core/Animation/PathAnimationManager.cs
@@ -26,6 +26,7 @@ namespace NavisworksTransport.Core.Animation
///
public class PathAnimationManager
{
+ private static PathAnimationManager _instance;
private ModelItem _animatedObject;
private List _pathPoints;
@@ -97,6 +98,21 @@ namespace NavisworksTransport.Core.Animation
LogManager.Warning($"TimeLiner 集成初始化失败,将使用基础动画功能: {ex.Message}");
_timeLinerManager = null;
}
+
+ // 订阅文档状态事件
+ DocumentStateManager.Instance.DocumentInvalidated += OnDocumentInvalidated;
+ }
+
+ ///
+ /// 获取单例实例
+ ///
+ public static PathAnimationManager GetInstance()
+ {
+ if (_instance == null)
+ {
+ _instance = new PathAnimationManager();
+ }
+ return _instance;
}
///
@@ -941,6 +957,63 @@ namespace NavisworksTransport.Core.Animation
///
public TimeLinerIntegrationManager TimeLinerManager => _timeLinerManager;
+ ///
+ /// 清理对象引用
+ ///
+ public void ClearReferences()
+ {
+ try
+ {
+ LogManager.Info("[PathAnimationManager] 清理对象引用");
+
+ // 停止动画
+ if (_currentState == AnimationState.Playing || _currentState == AnimationState.Paused)
+ {
+ StopAnimation();
+ }
+
+ // 清空对象引用
+ _animatedObject = null;
+ _pathPoints?.Clear();
+ _currentPosition = new Point3D(0, 0, 0);
+ _originalCenter = new Point3D(0, 0, 0);
+
+ LogManager.Info("[PathAnimationManager] 对象引用已清理");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[PathAnimationManager] 清理对象引用失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 验证动画对象是否有效
+ ///
+ private bool IsAnimatedObjectValid()
+ {
+ if (_animatedObject == null)
+ return false;
+
+ // 使用DocumentStateManager验证
+ return DocumentStateManager.Instance.IsModelItemValid(_animatedObject);
+ }
+
+ ///
+ /// 文档失效事件处理
+ ///
+ private void OnDocumentInvalidated(object sender, EventArgs e)
+ {
+ try
+ {
+ LogManager.Info("[PathAnimationManager] 文档失效,停止动画并清理");
+ ClearReferences();
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[PathAnimationManager] 处理文档失效事件失败: {ex.Message}");
+ }
+ }
+
///
/// 资源清理
///
@@ -987,6 +1060,9 @@ namespace NavisworksTransport.Core.Animation
}
}
+ // 4. 取消订阅文档事件
+ DocumentStateManager.Instance.DocumentInvalidated -= OnDocumentInvalidated;
+
LogManager.Info("PathAnimationManager资源清理完成");
}
catch (Exception ex)
@@ -1207,6 +1283,14 @@ namespace NavisworksTransport.Core.Animation
{
return; // 非播放状态,跳过更新
}
+
+ // 验证动画对象是否仍然有效
+ if (!IsAnimatedObjectValid())
+ {
+ LogManager.Warning("[PathAnimationManager] 动画对象已失效,停止动画");
+ StopAnimation();
+ return;
+ }
// 帧率控制
var now = DateTime.Now;
diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs
index 46b9420..95a1034 100644
--- a/src/Core/Collision/ClashDetectiveIntegration.cs
+++ b/src/Core/Collision/ClashDetectiveIntegration.cs
@@ -1467,6 +1467,42 @@ namespace NavisworksTransport
}
}
+ ///
+ /// 清理碰撞缓存
+ ///
+ public void ClearCollisionCache()
+ {
+ try
+ {
+ LogManager.Info("[ClashDetectiveIntegration] 清理碰撞缓存");
+
+ // 清空当前碰撞列表
+ _currentCollisions?.Clear();
+
+ // 清空缓存的结果
+ lock (_highlightLock)
+ {
+ _cachedResults?.Clear();
+ }
+
+ // 清除高亮显示
+ ClearHighlights();
+
+ // 清除对象缓存
+ ClearAllCaches();
+
+ // 重置计数器
+ _animationCollisionCount = 0;
+ _clashDetectiveCollisionCount = 0;
+
+ LogManager.Info("[ClashDetectiveIntegration] 碰撞缓存已清理");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[ClashDetectiveIntegration] 清理碰撞缓存失败: {ex.Message}");
+ }
+ }
+
///
/// 增强的碰撞对象信息,包含容器映射
///
diff --git a/src/Core/DocumentStateManager.cs b/src/Core/DocumentStateManager.cs
new file mode 100644
index 0000000..70ec82d
--- /dev/null
+++ b/src/Core/DocumentStateManager.cs
@@ -0,0 +1,240 @@
+using System;
+using Autodesk.Navisworks.Api;
+using NavisApplication = Autodesk.Navisworks.Api.Application;
+
+namespace NavisworksTransport.Core
+{
+ ///
+ /// 文档状态管理器,统一管理文档生命周期和状态变化
+ ///
+ public class DocumentStateManager
+ {
+ private static DocumentStateManager _instance;
+ private static readonly object _lock = new object();
+
+ private Document _currentDocument;
+ private bool _isDocumentValid;
+ private string _currentDocumentPath;
+
+ ///
+ /// 文档已失效事件
+ ///
+ public event EventHandler DocumentInvalidated;
+
+ ///
+ /// 文档已就绪事件
+ ///
+ public event EventHandler DocumentReady;
+
+ ///
+ /// 获取单例实例
+ ///
+ public static DocumentStateManager Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ lock (_lock)
+ {
+ if (_instance == null)
+ {
+ _instance = new DocumentStateManager();
+ }
+ }
+ }
+ return _instance;
+ }
+ }
+
+ private DocumentStateManager()
+ {
+ // 私有构造函数
+ _isDocumentValid = false;
+ }
+
+ ///
+ /// 获取当前文档是否有效
+ ///
+ public bool IsDocumentValid => _isDocumentValid;
+
+ ///
+ /// 获取当前文档
+ ///
+ public Document CurrentDocument => _currentDocument;
+
+ ///
+ /// 获取当前文档路径
+ ///
+ public string CurrentDocumentPath => _currentDocumentPath;
+
+ ///
+ /// 文档失效时调用
+ ///
+ public void OnDocumentInvalidated()
+ {
+ try
+ {
+ LogManager.Info("[DocumentStateManager] 文档失效通知");
+
+ _isDocumentValid = false;
+ _currentDocument = null;
+ _currentDocumentPath = null;
+
+ // 触发文档失效事件
+ DocumentInvalidated?.Invoke(this, EventArgs.Empty);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[DocumentStateManager] 处理文档失效失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 文档就绪时调用
+ ///
+ public void OnDocumentReady()
+ {
+ try
+ {
+ // 验证新文档
+ _currentDocument = NavisApplication.ActiveDocument;
+
+ if (_currentDocument != null)
+ {
+ _isDocumentValid = true;
+ _currentDocumentPath = _currentDocument.FileName;
+
+ LogManager.Info($"[DocumentStateManager] 文档就绪: {_currentDocumentPath ?? "未保存的文档"}");
+
+ // 触发文档就绪事件
+ DocumentReady?.Invoke(this, EventArgs.Empty);
+ }
+ else
+ {
+ LogManager.Warning("[DocumentStateManager] 没有活动文档");
+ _isDocumentValid = false;
+ _currentDocumentPath = null;
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[DocumentStateManager] 处理文档就绪失败: {ex.Message}");
+ _isDocumentValid = false;
+ }
+ }
+
+ ///
+ /// 验证当前文档是否仍然有效
+ ///
+ public bool ValidateCurrentDocument()
+ {
+ try
+ {
+ if (!_isDocumentValid || _currentDocument == null)
+ return false;
+
+ // 检查文档是否仍然是活动文档
+ var activeDoc = NavisApplication.ActiveDocument;
+ if (activeDoc == null || activeDoc != _currentDocument)
+ {
+ LogManager.Warning("[DocumentStateManager] 当前文档已不是活动文档");
+ _isDocumentValid = false;
+ return false;
+ }
+
+ // 尝试访问文档属性以验证其有效性
+ var fileName = activeDoc.FileName;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[DocumentStateManager] 文档验证失败: {ex.Message}");
+ _isDocumentValid = false;
+ return false;
+ }
+ }
+
+ ///
+ /// 验证ModelItem是否属于当前有效文档
+ ///
+ public bool IsModelItemValid(ModelItem item)
+ {
+ if (item == null)
+ return false;
+
+ if (!_isDocumentValid)
+ return false;
+
+ try
+ {
+ // 尝试访问ModelItem的属性
+ var name = item.DisplayName;
+ var hasGeometry = item.HasGeometry;
+ return true;
+ }
+ catch (Exception)
+ {
+ // 如果访问属性时出错,说明对象已失效
+ return false;
+ }
+ }
+
+ ///
+ /// 安全执行需要有效文档的操作
+ ///
+ public T SafeExecuteWithDocument(Func action, T defaultValue = default)
+ {
+ try
+ {
+ if (!ValidateCurrentDocument())
+ {
+ LogManager.Warning("[DocumentStateManager] 文档无效,操作被取消");
+ return defaultValue;
+ }
+
+ return action(_currentDocument);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[DocumentStateManager] 执行文档操作失败: {ex.Message}");
+ return defaultValue;
+ }
+ }
+
+ ///
+ /// 安全执行需要有效文档的操作(无返回值)
+ ///
+ public void SafeExecuteWithDocument(Action action)
+ {
+ SafeExecuteWithDocument(doc =>
+ {
+ action(doc);
+ return true;
+ }, false);
+ }
+
+ ///
+ /// 清理资源
+ ///
+ public void Dispose()
+ {
+ try
+ {
+ // 清空事件订阅者
+ DocumentInvalidated = null;
+ DocumentReady = null;
+
+ _currentDocument = null;
+ _isDocumentValid = false;
+ _currentDocumentPath = null;
+
+ LogManager.Info("[DocumentStateManager] 资源已清理");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[DocumentStateManager] 清理资源失败: {ex.Message}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Core/MainPlugin.cs b/src/Core/MainPlugin.cs
index 9ef9c9b..5981ffb 100644
--- a/src/Core/MainPlugin.cs
+++ b/src/Core/MainPlugin.cs
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using NavisworksTransport.Core;
+using NavisworksTransport.Core.Animation;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport
@@ -316,6 +317,208 @@ namespace NavisworksTransport
// 在任何控件创建之前初始化全局异常处理器
GlobalExceptionHandler.Initialize();
}
+
+ ///
+ /// 插件加载后调用(Navisworks插件生命周期)
+ ///
+ protected override void OnLoaded()
+ {
+ base.OnLoaded();
+
+ LogManager.Info("[文档管理] 插件OnLoaded - 开始初始化");
+
+ // 订阅文档事件
+ SubscribeToDocumentEvents();
+
+ // 检查是否已有活动文档并初始化
+ if (NavisApplication.ActiveDocument != null)
+ {
+ LogManager.Info("[文档管理] 插件加载时发现活动文档,立即初始化管理器");
+ InitializeManagers();
+ }
+ else
+ {
+ LogManager.Info("[文档管理] 插件加载时无活动文档");
+ }
+ }
+
+ ///
+ /// 插件卸载前调用(Navisworks插件生命周期)
+ ///
+ protected override void OnUnloading()
+ {
+ LogManager.Info("[文档管理] 插件OnUnloading - 开始清理");
+
+ // 取消订阅文档事件
+ UnsubscribeFromDocumentEvents();
+
+ // 清理管理器
+ CleanupManagers();
+
+ base.OnUnloading();
+ }
+
+ ///
+ /// 订阅文档相关事件
+ ///
+ private void SubscribeToDocumentEvents()
+ {
+ try
+ {
+ // 只订阅活动文档切换事件
+ NavisApplication.ActiveDocumentChanged += OnActiveDocumentChanged;
+
+ LogManager.Info("[文档管理] 已订阅活动文档切换事件");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[文档管理] 订阅文档事件失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 活动文档切换事件处理
+ ///
+ private void OnActiveDocumentChanged(object sender, EventArgs e)
+ {
+ GlobalExceptionHandler.SafeExecute(() =>
+ {
+ var activeDoc = NavisApplication.ActiveDocument;
+ var oldDocName = DocumentStateManager.Instance.CurrentDocumentPath ?? "无";
+ var newDocName = activeDoc?.FileName ?? "无活动文档";
+
+ LogManager.Info($"[文档管理] 活动文档切换: {oldDocName} → {newDocName}");
+
+ // 先清理旧文档的状态
+ CleanupManagers();
+
+ // 如果有新的活动文档,重新初始化
+ if (activeDoc != null)
+ {
+ InitializeManagers();
+ }
+ else
+ {
+ LogManager.Info("[文档管理] 当前无活动文档");
+ }
+
+ }, "处理活动文档切换事件");
+ }
+
+ ///
+ /// 初始化各个管理器
+ ///
+ private void InitializeManagers()
+ {
+ try
+ {
+ var activeDoc = NavisApplication.ActiveDocument;
+ if (activeDoc == null)
+ {
+ LogManager.Warning("[文档管理] 无活动文档,跳过初始化");
+ return;
+ }
+
+ LogManager.Info($"[文档管理] 开始为文档 '{activeDoc.FileName ?? "未保存"}' 初始化管理器...");
+
+ // 通知DocumentStateManager文档已就绪
+ DocumentStateManager.Instance.OnDocumentReady();
+
+ // 清理并重新初始化各种缓存
+ ClashDetectiveIntegration.ClearAllCaches();
+
+ LogManager.Info("[文档管理] 管理器初始化完成");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[文档管理] 初始化管理器失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 清理所有管理器状态
+ ///
+ private void CleanupManagers()
+ {
+ try
+ {
+ LogManager.Info("[文档管理] 开始清理管理器...");
+
+ // 通知DocumentStateManager文档已失效
+ DocumentStateManager.Instance.OnDocumentInvalidated();
+
+ // 停止所有动画
+ try
+ {
+ var animationManager = PathAnimationManager.GetInstance();
+ if (animationManager != null)
+ {
+ animationManager.StopAnimation();
+ animationManager.ClearReferences();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[文档管理] 清理动画管理器时出现警告: {ex.Message}");
+ }
+
+ // 清理路径规划管理器
+ try
+ {
+ var pathManager = PathPlanningManager.GetActivePathManager();
+ if (pathManager != null)
+ {
+ pathManager.ResetPathEditState();
+ pathManager.ClearInvalidReferences();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[文档管理] 清理路径管理器时出现警告: {ex.Message}");
+ }
+
+ // 清理碰撞检测缓存
+ try
+ {
+ ClashDetectiveIntegration.Instance?.ClearCollisionCache();
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[文档管理] 清理碰撞检测缓存时出现警告: {ex.Message}");
+ }
+
+ // 清理IdleEventManager任务
+ try
+ {
+ IdleEventManager.Instance?.Dispose();
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[文档管理] 清理Idle事件管理器时出现警告: {ex.Message}");
+ }
+
+ // 清除临时材质
+ try
+ {
+ var activeDoc = NavisApplication.ActiveDocument;
+ if (activeDoc?.Models != null)
+ {
+ activeDoc.Models.ResetAllTemporaryMaterials();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[文档管理] 清除临时材质时出现警告: {ex.Message}");
+ }
+
+ LogManager.Info("[文档管理] 管理器清理完成");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[文档管理] 清理管理器失败: {ex.Message}");
+ }
+ }
+
public override Control CreateControlPane()
{
@@ -363,6 +566,9 @@ namespace NavisworksTransport
wpfControl.Cleanup();
elementHost.Child = null;
}
+
+ // 取消订阅文档事件
+ UnsubscribeFromDocumentEvents();
// 释放控件资源
pane?.Dispose();
@@ -371,6 +577,24 @@ namespace NavisworksTransport
}, "销毁控制面板");
}
+
+ ///
+ /// 取消订阅文档相关事件
+ ///
+ private void UnsubscribeFromDocumentEvents()
+ {
+ try
+ {
+ // 只取消订阅活动文档切换事件
+ NavisApplication.ActiveDocumentChanged -= OnActiveDocumentChanged;
+
+ LogManager.Info("[文档管理] 已取消订阅文档事件");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[文档管理] 取消订阅文档事件失败: {ex.Message}");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs
index 053073b..2415c74 100644
--- a/src/Core/PathPlanningManager.cs
+++ b/src/Core/PathPlanningManager.cs
@@ -559,6 +559,32 @@ namespace NavisworksTransport
RaiseErrorOccurred(ex.Message, ex);
}
}
+
+ ///
+ /// 清理无效的对象引用
+ ///
+ public void ClearInvalidReferences()
+ {
+ try
+ {
+ LogManager.Info("[PathPlanningManager] 清理无效对象引用");
+
+ // 重置编辑状态
+ PathEditState = PathEditState.None;
+
+ // 清空当前路径
+ if (_currentRoute != null)
+ {
+ _currentRoute.Points.Clear();
+ }
+
+ LogManager.Info("[PathPlanningManager] 对象引用清理完成");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[PathPlanningManager] 清理对象引用失败: {ex.Message}");
+ }
+ }
///
/// 保存当前路线到历史记录
diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
index 55b0382..f16b3b7 100644
--- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
+++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
@@ -449,6 +449,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 初始化防抖定时器
InitializeParameterUpdateTimer();
+
+ // 订阅文档状态事件
+ DocumentStateManager.Instance.DocumentInvalidated += OnDocumentInvalidated;
+ DocumentStateManager.Instance.DocumentReady += OnDocumentReady;
LogManager.Info("AnimationControlViewModel初始化完成 - 支持统一状态栏");
}
@@ -1624,6 +1628,100 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#endregion
+
+ #region 文档管理
+
+ ///
+ /// 获取单例实例
+ ///
+ private static AnimationControlViewModel _instance;
+ public static AnimationControlViewModel Instance
+ {
+ get { return _instance; }
+ set { _instance = value; }
+ }
+
+ ///
+ /// 文档失效时的处理
+ ///
+ private void OnDocumentInvalidated(object sender, EventArgs e)
+ {
+ try
+ {
+ LogManager.Info("[AnimationControlViewModel] 文档失效,重置状态");
+ Reset();
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[AnimationControlViewModel] 处理文档失效失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 文档就绪时的处理
+ ///
+ private void OnDocumentReady(object sender, EventArgs e)
+ {
+ try
+ {
+ LogManager.Info("[AnimationControlViewModel] 文档就绪,启用功能");
+
+ // 重新启用UI
+ CanStartAnimation = true;
+ CanGenerateAnimation = true;
+
+ UpdateMainStatus("文档已加载,就绪");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[AnimationControlViewModel] 处理文档就绪失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 重置视图模型状态
+ ///
+ public void Reset()
+ {
+ try
+ {
+ LogManager.Info("[AnimationControlViewModel] 重置状态");
+
+ // 停止动画
+ if (_pathAnimationManager != null && _pathAnimationManager.IsAnimating)
+ {
+ _pathAnimationManager.StopAnimation();
+ }
+
+ // 清空选中对象
+ SelectedAnimatedObject = null;
+ SelectedAnimatedObjectName = string.Empty;
+ CurrentPathRoute = null;
+
+ // 重置动画参数
+ AnimationProgress = 0;
+ CurrentAnimationTime = 0;
+ StartAnimationButtonText = "开始";
+
+ // 禁用按钮
+ CanStartAnimation = false;
+ CanPauseAnimation = false;
+ CanStopAnimation = false;
+ CanGenerateAnimation = false;
+ HasSelectedAnimatedObject = false;
+
+ // 清空碰撞结果
+ HasCollisionResults = false;
+
+ UpdateMainStatus("已重置");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[AnimationControlViewModel] 重置失败: {ex.Message}");
+ }
+ }
+
+ #endregion
#region 资源清理