diff --git a/CHANGELOG.md b/CHANGELOG.md index 040abec..b178b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,147 @@ # NavisworksTransport 变更日志 +## [0.7.0] - 2025-08-18 + +### 🎯 分层管理功能完整实现 - WPF架构重构与线程安全解决方案 + +#### 核心功能突破 + +**🔥 分层管理页签 - 全新WPF实现** + +- **WPF UI架构重构** + - 完全使用WPF替代WinForms,实现现代化用户界面 + - 采用MVVM架构模式,实现业务逻辑与UI分离 + - 集成ThreadSafeObservableCollection确保跨线程集合操作安全 + - 使用RelayCommand实现统一的命令模式 + +- **分层预览功能** + - 实现智能楼层分析,支持按楼层和按自定义属性分层 + - 支持1级、2级、3级、全部四种遍历深度选择 + - 提供实时预览结果,显示分层名称、对象数量、估算文件大小 + - 缓存机制优化,避免重复分析提升性能 + +- **分层保存功能** + - 批量导出功能,支持一键保存所有分层到指定目录 + - 智能文件命名:项目名_分层名_时间戳格式 + - 支持NWD导出选项配置(嵌入纹理、阻止对象属性导出等) + - 完整的进度显示和错误处理机制 + +- **保存当前选择项功能** + - 支持多选节点的智能导出 + - 包含子节点选项,完整保存节点层次结构 + - 自动生成默认文件名,支持多选节点描述 + - 智能可见性控制,只导出相关项目 + +#### 重大技术突破 + +**🔧 Navisworks API线程安全解决方案** + +- **根本问题解决**:修复了分层保存时程序崩溃的线程安全问题 + - 发现Navisworks API(COM组件)必须在主UI线程(STA线程)中执行 + - 对比分析"保存当前选择项"(成功)vs"分层保存"(崩溃)的线程差异 + - 使用`System.Windows.Application.Current.Dispatcher.Invoke()`确保API调用线程安全 + +- **API调用模式修复**: + ```csharp + // ❌ 问题代码:后台线程调用导致崩溃 + document.ExportToNwd(outputPath, exportOptions); + + // ✅ 修复代码:确保主线程执行 + Dispatcher.Invoke(() => { + document.ExportToNwd(outputPath, exportOptions); + }); + ``` + +- **线程状态诊断**: + - 添加线程状态检查(STA/MTA)和日志记录 + - 实现线程安全的异常处理机制 + - 建立完整的API调用最佳实践 + +#### 业务逻辑架构 + +**🔧 SimplifiedModelSplitterManager - 轻量级分层管理器** + +- **核心功能模块**: + - 楼层检测和分析:集成FloorDetector实现智能楼层识别 + - 属性分组:支持按自定义属性进行模型分组 + - 预览生成:提供分层预览结果,包含项目统计和元数据 + - 批量导出:支持多分层同时导出,完整的错误处理 + +- **性能优化**: + - 双重缓存策略:分层结果缓存和深度控制缓存 + - 智能可见性控制:只操作顶级节点,避免全模型遍历 + - 内存管理:大量模型项时自动垃圾回收 + +#### UI架构技术细节 + +**🔧 LayerManagementViewModel - MVVM架构实现** + +- **线程安全UI更新**: + - 集成UIStateManager实现安全的跨线程UI更新 + - 使用ThreadSafeObservableCollection避免集合操作异常 + - 实现SetPropertyThreadSafe方法确保属性更新安全 + +- **Command模式应用**: + - AnalyzeFloorsCommand:楼层分析命令 + - PreviewSplitCommand:分层预览命令 + - ExecuteSplitCommand:分层保存命令 + - SaveSelectedItemsCommand:选择项保存命令 + +- **异步操作管理**: + - 正确的业务逻辑与UI分离模式 + - 四步骤异步操作:初始UI更新→业务逻辑执行→结果UI更新→状态清理 + - 完善的异常处理和状态恢复机制 + +#### API使用方法文档更新 + +**📚 线程安全章节补充** + +- **实际案例记录**:将此次线程安全问题解决方案完整记录 +- **最佳实践模式**:提供线程安全的Navisworks API调用模式 +- **常见问题对照表**:线程问题症状、原因、解决方案 +- **代码示例**:完整的线程安全API调用示例 + +#### 技术架构成果 + +**架构质量提升** + +- **完整MVVM实现**:ViewModels、Views、Models分离,代码结构清晰 +- **线程安全保障**:解决COM API线程安全问题,确保稳定性 +- **异常处理完善**:多层次异常处理,优雅降级机制 +- **性能优化显著**:缓存机制和智能算法,处理大模型性能提升 + +**用户体验改进** + +- **现代化界面**:WPF界面美观且响应流畅 +- **操作简化**:一键预览、一键导出,操作门槛降低 +- **反馈完善**:实时进度显示、详细状态提示 +- **容错能力强**:异常情况自动恢复,不影响其他功能 + +### 验证结果 ✅ + +- ✅ 分层预览功能完全正常,支持多种分层策略 +- ✅ 分层保存功能稳定运行,无崩溃问题 +- ✅ 保存当前选择项功能完善,支持多选节点 +- ✅ 线程安全问题彻底解决,API调用稳定 +- ✅ WPF UI响应流畅,用户体验良好 +- ✅ 所有功能完整测试通过,系统稳定性达标 + +### 技术文档更新 + +- ✅ `NavisworksAPI使用方法.md` 补充线程安全章节 +- ✅ 记录实际问题解决过程和技术方案 +- ✅ 提供完整的最佳实践代码示例 +- ✅ 建立线程安全问题诊断和解决流程 + +### 开发里程碑 + +- **分层管理功能**:从概念到完整实现,涵盖预览、保存、选择项导出 +- **WPF架构重构**:完全现代化的UI架构和MVVM模式 +- **线程安全解决**:深入理解COM API特性,建立稳定的调用模式 +- **技术文档完善**:经验总结和最佳实践,为后续开发铺路 + +--- + ## [0.6.0] - 2025-08-17 ### 🎯 UI架构重构重大突破 - 线程安全与稳定性全面提升 diff --git a/VERSION.md b/VERSION.md index 09a3acf..bcaffe1 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -0.6.0 \ No newline at end of file +0.7.0 \ No newline at end of file diff --git a/doc/design/2026/NavisworksAPI使用方法.md b/doc/design/2026/NavisworksAPI使用方法.md index e45f6aa..2dafadb 100644 --- a/doc/design/2026/NavisworksAPI使用方法.md +++ b/doc/design/2026/NavisworksAPI使用方法.md @@ -275,7 +275,176 @@ public void ExportSelectedNodes(List selectedItems, string filePath) } ``` -## 8. 常用属性和方法速查 +## 8. 线程安全 - 关键重要 + +### 8.1 Navisworks API 线程安全要求 + +**核心原则:所有 Navisworks API 调用必须在主 UI 线程(STA 线程)中执行** + +```csharp +// ❌ 错误:在后台线程中调用 Navisworks API +await Task.Run(() => +{ + var document = Application.ActiveDocument; // 可能崩溃 + document.ExportToNwd(path, options); // 会崩溃 +}); + +// ✅ 正确:使用 Dispatcher.Invoke 确保主线程执行 +await Task.Run(() => +{ + System.Windows.Application.Current.Dispatcher.Invoke(() => + { + var document = Application.ActiveDocument; + document.ExportToNwd(path, options); // 安全执行 + }); +}); +``` + +### 8.2 实际案例:分层导出修复 + +**问题场景**:`SimplifiedModelSplitterManager.ExportLayerToNwd` 方法通过后台线程调用时崩溃 + +```csharp +// ❌ 问题代码:导致崩溃 +public bool ExportLayerToNwd(...) +{ + var document = NavisApplication.ActiveDocument; + document.ExportToNwd(outputPath, exportOptions); // 后台线程崩溃 +} +``` + +**修复方案**:使用 Dispatcher.Invoke 包装所有 API 调用 + +```csharp +// ✅ 修复代码:线程安全 +public bool ExportLayerToNwd(...) +{ + bool exportResult = false; + Exception exportException = null; + + // 确保在主线程中执行所有 Navisworks API 调用 + System.Windows.Application.Current.Dispatcher.Invoke(() => + { + try + { + var document = NavisApplication.ActiveDocument; + + // 保存可见性状态 + var originalVisibilityState = SaveCurrentVisibilityState(document); + + try + { + // 隐藏不需要的项目 + var itemsToHide = GetItemsToHide(...); + document.Models.SetHidden(itemsToHide, true); + + // 创建导出选项 + var exportOptions = new NwdExportOptions + { + ExcludeHiddenItems = true, + EmbedXrefs = false, + PreventObjectPropertyExport = false + }; + + // 在主线程中安全执行导出 + document.ExportToNwd(outputPath, exportOptions); + exportResult = true; + } + finally + { + // 恢复可见性状态 + RestoreVisibilityState(document, originalVisibilityState); + } + } + catch (Exception ex) + { + exportException = ex; + } + }); + + if (exportException != null) + throw exportException; + + return exportResult; +} +``` + +### 8.3 线程安全检查和诊断 + +```csharp +// ✅ 检查当前线程状态 +var apartmentState = System.Threading.Thread.CurrentThread.GetApartmentState(); +LogManager.Info($"当前线程状态: {apartmentState}"); // 应该是 STA + +if (apartmentState != System.Threading.ApartmentState.STA) +{ + LogManager.Warning("警告:不在STA线程中,API调用可能失败"); +} + +// ✅ 验证是否在主线程中 +bool isMainThread = System.Windows.Application.Current.Dispatcher.CheckAccess(); +if (!isMainThread) +{ + LogManager.Warning("警告:不在主线程中,需要使用Dispatcher.Invoke"); +} +``` + +### 8.4 常见线程安全问题和解决方案 + +| 问题场景 | 症状 | 解决方案 | +|---------|------|---------| +| 后台线程调用 API | 程序崩溃,无错误信息 | 使用 `Dispatcher.Invoke()` | +| Command.ExecuteAsync() | Task 中的 API 调用崩溃 | 在 Task 内部使用 Dispatcher | +| 异步方法调用 API | 间歇性崩溃 | 检查执行线程,确保主线程 | +| Timer 中调用 API | 定时器触发时崩溃 | Timer 回调使用 Dispatcher | + +### 8.5 最佳实践模式 + +```csharp +// ✅ 推荐模式:安全的异步 Navisworks API 调用 +public async Task SafeNavisworksOperationAsync() +{ + // 1. 后台准备数据 + var preparedData = await Task.Run(() => + { + // 在后台线程中进行数据准备(不涉及 Navisworks API) + return PrepareDataSafely(); + }); + + // 2. 主线程执行 API 调用 + bool result = false; + await System.Windows.Application.Current.Dispatcher.InvokeAsync(() => + { + // 所有 Navisworks API 调用都在主线程中 + var document = Application.ActiveDocument; + result = document.SomeNavisworksOperation(preparedData); + }); + + return result; +} + +// ✅ 推荐模式:批量 API 操作 +public void BatchNavisworksOperations(List items) +{ + System.Windows.Application.Current.Dispatcher.Invoke(() => + { + var document = Application.ActiveDocument; + + // 批量操作,避免多次线程切换 + var itemCollection = new ModelItemCollection(); + foreach (var item in items) + { + itemCollection.Add(item); + } + + // 一次性完成所有操作 + document.Models.SetHidden(itemCollection, true); + document.CurrentSelection.CopyFrom(itemCollection); + }); +} +``` + +## 9. 常用属性和方法速查 ### ModelItem 常用属性 - `HasGeometry` - 是否有几何体 @@ -301,16 +470,19 @@ public void ExportSelectedNodes(List selectedItems, string filePath) - `SetRequired(ModelItemCollection items, bool required)` - 设置必需状态 - `RootItemDescendantsAndSelf` - 所有根项目的后代 -## 9. 错误避免指南 +## 10. 错误避免指南 -1. **不要使用不存在的API**:如 `SearchCondition.HasAncestor` -2. **避免深度递归**:使用内置的 `DescendantsAndSelf` 代替手写递归 -3. **批量操作**:使用 `ModelItemCollection` 进行批量设置,而不是逐个操作 -4. **正确的命名空间**:确保引用 `using Autodesk.Navisworks.Api;` -5. **异常处理**:文件操作和API调用要适当处理异常 -6. **资源清理**:隐藏操作后要恢复原始状态 +1. **线程安全是第一要务**:所有 Navisworks API 调用必须在主 UI 线程中执行 +2. **不要使用不存在的API**:如 `SearchCondition.HasAncestor` +3. **避免深度递归**:使用内置的 `DescendantsAndSelf` 代替手写递归 +4. **批量操作**:使用 `ModelItemCollection` 进行批量设置,而不是逐个操作 +5. **正确的命名空间**:确保引用 `using Autodesk.Navisworks.Api;` +6. **异常处理**:文件操作和API调用要适当处理异常 +7. **资源清理**:隐藏操作后要恢复原始状态 +8. **线程状态检查**:在关键操作前验证线程状态(STA) +9. **Dispatcher 模式**:后台线程中需要调用 API 时,始终使用 Dispatcher.Invoke -## 10. 参考官方示例 +## 11. 参考官方示例 强烈建议查看以下官方示例了解更多用法: - `SearchComparisonPlugIn.cs` - 搜索性能对比 diff --git a/src/UI/WPF/LogisticsControlPanel.xaml b/src/UI/WPF/LogisticsControlPanel.xaml index 9c2bae4..b7703b3 100644 --- a/src/UI/WPF/LogisticsControlPanel.xaml +++ b/src/UI/WPF/LogisticsControlPanel.xaml @@ -13,6 +13,10 @@ + + + + @@ -213,10 +217,6 @@ - - - - diff --git a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs index bf0de3b..b6992fc 100644 --- a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs +++ b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs @@ -408,9 +408,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels public ICommand BrowseOutputDirectoryCommand { get; private set; } public ICommand SaveSelectedItemsCommand { get; private set; } public ICommand CancelOperationCommand { get; private set; } - public ICommand TestExportCommand { get; private set; } public ICommand DiagnosticCommand { get; private set; } - public ICommand SimpleSaveCommand { get; private set; } public ICommand TestExportToNwdCommand { get; private set; } #endregion @@ -503,18 +501,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels CancelOperationCommand = new RelayCommand(CancelOperation, () => IsProcessing); - TestExportCommand = new RelayCommand( - async () => await TestExportAsync(), - () => IsNotProcessing); - DiagnosticCommand = new RelayCommand( async () => await RunDiagnosticAsync(), () => IsNotProcessing); - SimpleSaveCommand = new RelayCommand( - async () => await SimpleSaveAsync(), - () => IsNotProcessing); - TestExportToNwdCommand = new RelayCommand( async () => await TestExportToNwdAsync(), () => IsNotProcessing); @@ -1231,11 +1221,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels } LogManager.Info($"[LayerManagementViewModel] 已重新选择 {originalSelection.Count} 个目标节点"); - // 创建导出选项 - 只导出可见项目 + // 创建导出选项 - 使用用户配置的参数 var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions(); exportOptions.ExcludeHiddenItems = true; // 只导出可见项目(选中节点及其子项) - exportOptions.EmbedXrefs = false; - exportOptions.PreventObjectPropertyExport = false; + exportOptions.EmbedXrefs = EmbedXrefs; + exportOptions.PreventObjectPropertyExport = PreventObjectPropertyExport; + + LogManager.Info($"[LayerManagementViewModel] SaveSelectedItems导出选项: EmbedXrefs={exportOptions.EmbedXrefs}, PreventObjectPropertyExport={exportOptions.PreventObjectPropertyExport}"); LogManager.Info("[LayerManagementViewModel] 开始调用ExportToNwd API"); @@ -1356,123 +1348,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } - /// - /// 测试导出功能 - 使用简化模式,避免崩溃 - /// - private async Task TestExportAsync() - { - try - { - LogManager.Info("[LayerManagementViewModel] 开始简化测试导出"); - // 1. 简单状态更新 - IsProcessing = true; - - // 2. 获取保存路径 - string saveFilePath = null; - System.Windows.Application.Current.Dispatcher.Invoke(() => - { - var saveDialog = new Microsoft.Win32.SaveFileDialog - { - Title = "测试导出 - 选择保存位置", - Filter = "Navisworks文件 (*.nwd)|*.nwd", - DefaultExt = "nwd", - FileName = $"测试导出_{DateTime.Now:yyyyMMdd_HHmmss}.nwd" - }; - - if (saveDialog.ShowDialog() == true) - { - saveFilePath = saveDialog.FileName; - } - }); - - if (string.IsNullOrEmpty(saveFilePath)) - { - LogManager.Info("[LayerManagementViewModel] 用户取消了测试导出"); - return; - } - - // 3. 使用SimplifiedModelSplitterManager进行基础测试导出 - bool exportResult = false; - string errorMessage = ""; - - try - { - LogManager.Info($"[LayerManagementViewModel] 开始测试导出到: {saveFilePath}"); - - // 使用最简单的导出测试,不使用复杂的分层逻辑 - exportResult = PerformSimpleExportTest(saveFilePath); - - LogManager.Info($"[LayerManagementViewModel] TestBasicExport调用完成,结果: {exportResult}"); - } - catch (Exception ex) - { - LogManager.Error($"[LayerManagementViewModel] 测试导出异常: {ex.Message}", ex); - errorMessage = ex.Message; - exportResult = false; - } - - // 4. 简单的结果显示 - if (exportResult) - { - if (File.Exists(saveFilePath)) - { - var fileInfo = new FileInfo(saveFilePath); - MessageBox.Show( - $"测试导出成功!\n\n文件路径: {saveFilePath}\n文件大小: {fileInfo.Length / 1024} KB\n创建时间: {fileInfo.CreationTime}\n\n这说明基础导出功能正常工作!", - "测试导出结果", MessageBoxButton.OK, MessageBoxImage.Information); - } - else - { - MessageBox.Show("测试导出操作完成,但文件不存在。", "测试导出结果", MessageBoxButton.OK, MessageBoxImage.Warning); - } - } - else - { - MessageBox.Show($"测试导出失败!\n\n错误信息: {errorMessage}\n\n请检查日志了解详细信息。", - "测试导出结果", MessageBoxButton.OK, MessageBoxImage.Error); - } - - LogManager.Info("[LayerManagementViewModel] 简化测试导出完成"); - } - catch (Exception ex) - { - LogManager.Error($"[LayerManagementViewModel] 测试导出过程异常: {ex.Message}", ex); - MessageBox.Show($"测试导出过程异常: {ex.Message}", "测试导出错误", MessageBoxButton.OK, MessageBoxImage.Error); - } - finally - { - // 5. 简单的状态清理 - IsProcessing = false; - } - } - - /// - /// 显示测试导出结果消息框 - /// - private void ShowTestExportResult(bool isSuccess, string message, string filePath) - { - try - { - System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => - { - if (isSuccess) - { - var successMessage = $"测试导出成功!\n\n{message}\n\n文件位置: {filePath}"; - MessageBox.Show(successMessage, "测试导出结果", MessageBoxButton.OK, MessageBoxImage.Information); - } - else - { - var errorMessage = $"测试导出失败!\n\n{message}\n\n请检查日志了解详细错误信息。"; - MessageBox.Show(errorMessage, "测试导出结果", MessageBoxButton.OK, MessageBoxImage.Warning); - } - })); - } - catch (Exception ex) - { - LogManager.Error($"[LayerManagementViewModel] 显示测试导出结果失败: {ex.Message}"); - } - } /// /// 运行环境诊断 - 检查Navisworks API环境 @@ -1616,207 +1492,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } - /// - /// 最简单的保存方法 - 直接保存当前模型 - /// - private async Task SimpleSaveAsync() - { - try - { - LogManager.Info("[LayerManagementViewModel] 开始简单保存"); - // 1. 简单状态更新 - IsProcessing = true; - - // 2. 获取保存路径 - string saveFilePath = null; - System.Windows.Application.Current.Dispatcher.Invoke(() => - { - var saveDialog = new Microsoft.Win32.SaveFileDialog - { - Title = "保存当前模型", - Filter = "Navisworks文件 (*.nwd)|*.nwd", - DefaultExt = "nwd", - FileName = $"当前模型_{DateTime.Now:yyyyMMdd_HHmmss}.nwd" - }; - - if (saveDialog.ShowDialog() == true) - { - saveFilePath = saveDialog.FileName; - } - }); - - if (string.IsNullOrEmpty(saveFilePath)) - { - LogManager.Info("[LayerManagementViewModel] 用户取消了保存操作"); - return; - } - - // 3. 最简单的保存操作 - 在主UI线程中直接执行,避免线程问题 - bool saveResult = false; - string errorMessage = ""; - - try - { - LogManager.Info($"[LayerManagementViewModel] 开始保存到: {saveFilePath}"); - - // 获取当前文档 - var document = Autodesk.Navisworks.Api.Application.ActiveDocument; - if (document == null) - { - errorMessage = "没有活动文档"; - saveResult = false; - } - else - { - LogManager.Info($"[LayerManagementViewModel] 当前文档: {document.FileName ?? "未命名"}"); - LogManager.Info($"[LayerManagementViewModel] 模型数量: {document.Models?.Count ?? 0}"); - - // 最基本的保存调用 - 在主线程中执行 - document.SaveFile(saveFilePath, Autodesk.Navisworks.Api.DocumentFileVersion.Current); - - saveResult = true; - LogManager.Info($"[LayerManagementViewModel] SaveFile调用完成"); - } - } - catch (Exception ex) - { - LogManager.Error($"[LayerManagementViewModel] 保存异常: {ex.Message}", ex); - errorMessage = ex.Message; - saveResult = false; - } - - // 4. 简单的结果显示 - 避免复杂的UI更新 - if (saveResult) - { - if (File.Exists(saveFilePath)) - { - var fileInfo = new FileInfo(saveFilePath); - MessageBox.Show( - $"保存成功!\n\n文件路径: {saveFilePath}\n文件大小: {fileInfo.Length / 1024} KB\n创建时间: {fileInfo.CreationTime}", - "保存结果", MessageBoxButton.OK, MessageBoxImage.Information); - } - else - { - MessageBox.Show("保存操作完成,但文件不存在。", "保存结果", MessageBoxButton.OK, MessageBoxImage.Warning); - } - } - else - { - MessageBox.Show($"保存失败!\n\n错误信息: {errorMessage}\n\n这可能说明基础的Navisworks API调用有问题。", - "保存结果", MessageBoxButton.OK, MessageBoxImage.Error); - } - - LogManager.Info("[LayerManagementViewModel] 简单保存完成"); - } - catch (Exception ex) - { - LogManager.Error($"[LayerManagementViewModel] 简单保存异常: {ex.Message}", ex); - MessageBox.Show($"保存过程异常: {ex.Message}", "保存错误", MessageBoxButton.OK, MessageBoxImage.Error); - } - finally - { - // 5. 简单的状态清理 - IsProcessing = false; - } - } - - /// - /// 执行最简单的导出测试 - 只选择前几个模型项 - /// - private bool PerformSimpleExportTest(string saveFilePath) - { - try - { - LogManager.Info("[LayerManagementViewModel] 开始执行简单导出测试"); - - // 获取当前文档 - var document = Autodesk.Navisworks.Api.Application.ActiveDocument; - if (document?.Models?.Count == 0) - { - LogManager.Error("[LayerManagementViewModel] 没有活动文档或模型"); - return false; - } - - // 保存当前选择状态 - var originalSelection = new List(); - foreach (ModelItem item in document.CurrentSelection.SelectedItems) - { - originalSelection.Add(item); - } - - try - { - // 清空当前选择 - document.CurrentSelection.Clear(); - - // 获取根节点的前5个子项进行测试 - int itemCount = 0; - - LogManager.Info($"[LayerManagementViewModel] 模型数量: {document.Models.Count}"); - - // 遍历所有模型的根项 - foreach (Model model in document.Models) - { - if (itemCount >= 5) break; - - var rootItem = model.RootItem; - LogManager.Info($"[LayerManagementViewModel] 处理模型: {model.FileName},根项: {rootItem.DisplayName}"); - - foreach (ModelItem child in rootItem.Children) - { - if (itemCount >= 5) break; - - try - { - document.CurrentSelection.Add(child); - LogManager.Info($"[LayerManagementViewModel] 已选择项目 #{itemCount + 1}: {child.DisplayName}"); - itemCount++; - } - catch (Exception ex) - { - LogManager.Warning($"[LayerManagementViewModel] 跳过无效项目: {ex.Message}"); - } - } - } - - if (itemCount == 0) - { - LogManager.Error("[LayerManagementViewModel] 没有找到可用的模型项"); - return false; - } - - LogManager.Info($"[LayerManagementViewModel] 已选择 {itemCount} 个模型项,开始导出"); - - // 直接导出选择的项目,使用最基本的API - document.SaveFile(saveFilePath, Autodesk.Navisworks.Api.DocumentFileVersion.Current); - - LogManager.Info("[LayerManagementViewModel] 简单导出测试完成"); - return true; - } - finally - { - // 恢复原始选择 - document.CurrentSelection.Clear(); - foreach (ModelItem item in originalSelection) - { - try - { - document.CurrentSelection.Add(item); - } - catch (Exception ex) - { - LogManager.Warning($"[LayerManagementViewModel] 恢复选择项时出错: {ex.Message}"); - } - } - } - } - catch (Exception ex) - { - LogManager.Error($"[LayerManagementViewModel] 简单导出测试异常: {ex.Message}", ex); - return false; - } - } /// /// 测试ExportToNwd API - 专门的导出API @@ -1992,11 +1668,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels document.CurrentSelection.Add(targetItem); LogManager.Info($"[LayerManagementViewModel] 已选择目标节点: {targetItem.DisplayName}"); - // 创建导出选项 - 只导出可见项目 + // 创建导出选项 - 使用用户配置的参数 var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions(); exportOptions.ExcludeHiddenItems = true; // 只导出可见项目(目标节点及其子项) - exportOptions.EmbedXrefs = false; - exportOptions.PreventObjectPropertyExport = false; + exportOptions.EmbedXrefs = EmbedXrefs; + exportOptions.PreventObjectPropertyExport = PreventObjectPropertyExport; + + LogManager.Info($"[LayerManagementViewModel] TestExportToNwd导出选项: EmbedXrefs={exportOptions.EmbedXrefs}, PreventObjectPropertyExport={exportOptions.PreventObjectPropertyExport}"); LogManager.Info("[LayerManagementViewModel] 开始调用ExportToNwd API"); diff --git a/src/UI/WPF/Views/LayerManagementView.xaml b/src/UI/WPF/Views/LayerManagementView.xaml index d590349..f575650 100644 --- a/src/UI/WPF/Views/LayerManagementView.xaml +++ b/src/UI/WPF/Views/LayerManagementView.xaml @@ -177,6 +177,11 @@ NavisworksTransport 分层管理页签视图 - 重构优化版本 IsChecked="{Binding EmbedXrefs}" Margin="0,0,0,5" ToolTip="将外部引用和纹理数据嵌入到导出的NWD文件中"/> + -