更新了版本,删掉2个测试按钮

This commit is contained in:
tian 2025-08-19 01:00:32 +08:00
parent cddb7de71e
commit 531e07f25d
6 changed files with 343 additions and 355 deletions

View File

@ -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 APICOM组件必须在主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架构重构重大突破 - 线程安全与稳定性全面提升

View File

@ -1 +1 @@
0.6.0
0.7.0

View File

@ -275,7 +275,176 @@ public void ExportSelectedNodes(List<ModelItem> 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<bool> 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<ModelItem> 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<ModelItem> 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` - 搜索性能对比

View File

@ -13,6 +13,10 @@
<!-- 主内容区域 -->
<TabControl Grid.Row="0" Name="MainTabControl" Margin="5">
<TabItem Header="分层管理" Name="LayerManagementTab">
<ContentControl x:Name="LayerManagementContent"/>
</TabItem>
<TabItem Header="类别设置" Name="ModelSettingsTab">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
@ -213,10 +217,6 @@
</ScrollViewer>
</TabItem>
<TabItem Header="分层管理" Name="LayerManagementTab">
<ContentControl x:Name="LayerManagementContent"/>
</TabItem>
<TabItem Header="系统管理" Name="SystemManagementTab">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">

View File

@ -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
}
}
/// <summary>
/// 测试导出功能 - 使用简化模式,避免崩溃
/// </summary>
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;
}
}
/// <summary>
/// 显示测试导出结果消息框
/// </summary>
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}");
}
}
/// <summary>
/// 运行环境诊断 - 检查Navisworks API环境
@ -1616,207 +1492,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 最简单的保存方法 - 直接保存当前模型
/// </summary>
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;
}
}
/// <summary>
/// 执行最简单的导出测试 - 只选择前几个模型项
/// </summary>
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<ModelItem>();
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;
}
}
/// <summary>
/// 测试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");

View File

@ -177,6 +177,11 @@ NavisworksTransport 分层管理页签视图 - 重构优化版本
IsChecked="{Binding EmbedXrefs}"
Margin="0,0,0,5"
ToolTip="将外部引用和纹理数据嵌入到导出的NWD文件中"/>
<TextBlock Text="⚠️ 注意:嵌入纹理数据可能导致导出失败或程序崩溃,建议在小模型上测试"
Style="{StaticResource StatusTextStyle}"
Foreground="#FFFF6600"
FontStyle="Italic"
Margin="0,0,0,8"/>
<CheckBox Content="阻止导出对象特性"
IsChecked="{Binding PreventObjectPropertyExport}"
Margin="0,0,0,5"
@ -302,15 +307,6 @@ NavisworksTransport 分层管理页签视图 - 重构优化版本
Command="{Binding DiagnosticCommand}"
ToolTip="检查Navisworks API环境和线程状态"
Background="#FFFFD700"/>
<Button Content="简单保存"
Style="{StaticResource SecondaryButtonStyle}"
Command="{Binding SimpleSaveCommand}"
ToolTip="直接保存当前模型为NWD"
Background="#FFAAFFAA"/>
<Button Content="测试导出"
Style="{StaticResource SecondaryButtonStyle}"
Command="{Binding TestExportCommand}"
ToolTip="导出5个模型项验证基础功能"/>
<Button Content="复杂导出测试"
Style="{StaticResource SecondaryButtonStyle}"
Command="{Binding TestExportToNwdCommand}"