From 3b801c6bd42e8dfa6c0f2870059e73d29bad5f11 Mon Sep 17 00:00:00 2001
From: tian <11429339@qq.com>
Date: Wed, 11 Mar 2026 19:03:43 +0800
Subject: [PATCH] =?UTF-8?q?=E7=BB=99=E2=80=9C=E5=AF=BC=E5=87=BA=E5=89=96?=
=?UTF-8?q?=E9=9D=A2=E7=9B=92=E2=80=9D=E5=A2=9E=E5=8A=A0=E5=AF=BC=E5=87=BA?=
=?UTF-8?q?nwd=E6=96=87=E4=BB=B6=EF=BC=9B=E9=87=8D=E6=9E=84=E4=BA=86?=
=?UTF-8?q?=E5=8F=AF=E8=A7=81=E6=80=A7=E7=9A=84=E4=BF=9D=E5=AD=98=E5=92=8C?=
=?UTF-8?q?=E6=81=A2=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
doc/requirement/todo_features.md | 4 +
src/Core/SectionBoxExporter.cs | 76 ++++--
.../ViewModels/LayerManagementViewModel.cs | 123 +---------
.../WPF/ViewModels/ModelSettingsViewModel.cs | 49 ++--
src/Utils/VisibilityHelper.cs | 225 ++++++++++++++++++
5 files changed, 314 insertions(+), 163 deletions(-)
diff --git a/doc/requirement/todo_features.md b/doc/requirement/todo_features.md
index 9eb638c..e7a8cce 100644
--- a/doc/requirement/todo_features.md
+++ b/doc/requirement/todo_features.md
@@ -2,6 +2,10 @@
## 功能点
+### [2026/3/11]
+
+1. [x] (功能)给“导出剖面盒”增加导出nwd文件的选项
+
### [2026/3/9]
1. [x] (功能)支持记录并查看路径文件操作的历史记录
diff --git a/src/Core/SectionBoxExporter.cs b/src/Core/SectionBoxExporter.cs
index 6c1e322..d9554d6 100644
--- a/src/Core/SectionBoxExporter.cs
+++ b/src/Core/SectionBoxExporter.cs
@@ -6,6 +6,7 @@ using System.Windows.Forms;
using Autodesk.Navisworks.Api;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport.Core
{
@@ -15,7 +16,7 @@ namespace NavisworksTransport.Core
public class SectionBoxExporter
{
///
- /// 导出剖面盒信息到JSON文件
+ /// 导出剖面盒信息
///
/// Navisworks文档
/// 导出的文件路径,失败返回null
@@ -72,8 +73,8 @@ namespace NavisworksTransport.Core
return null;
}
- // 3. 选择保存路径
- string filePath = ShowSaveFileDialog();
+ // 3. 选择保存路径和格式
+ string filePath = ShowSaveFileDialog(out bool exportAsNwd);
if (string.IsNullOrEmpty(filePath))
return null;
@@ -82,15 +83,20 @@ namespace NavisworksTransport.Core
// 4. 获取剖面盒内的对象
var objectsInSectionBox = GetObjectsInSectionBox(document, sectionBoxBounds);
- // 5. 构建导出数据
- var exportData = BuildExportData(sectionBoxBounds, objectsInSectionBox, document);
-
- // 6. 序列化为JSON并保存
- string json = JsonConvert.SerializeObject(exportData, Formatting.Indented);
- File.WriteAllText(filePath, json);
-
- LogManager.Info($"剖面盒导出完成: {objectsInSectionBox.Count} 个对象 -> {filePath}");
- return filePath;
+ if (exportAsNwd)
+ {
+ // 导出为NWD格式
+ return ExportToNwd(document, objectsInSectionBox, filePath);
+ }
+ else
+ {
+ // 导出为JSON格式(原有逻辑)
+ var exportData = BuildExportData(sectionBoxBounds, objectsInSectionBox, document);
+ string json = JsonConvert.SerializeObject(exportData, Formatting.Indented);
+ File.WriteAllText(filePath, json);
+ LogManager.Info($"剖面盒导出完成: {objectsInSectionBox.Count} 个对象 -> {filePath}");
+ return filePath;
+ }
}
catch (Exception ex)
{
@@ -331,20 +337,58 @@ namespace NavisworksTransport.Core
return string.Join("/", pathParts);
}
+ ///
+ /// 导出为NWD文件
+ ///
+ private string ExportToNwd(Document document, List objects, string filePath)
+ {
+ // 必须在主线程执行Navisworks API操作
+ string result = null;
+ System.Windows.Application.Current.Dispatcher.Invoke(() =>
+ {
+ // 准备要显示的项目
+ var itemsToShow = new ModelItemCollection();
+ foreach (var item in objects)
+ itemsToShow.Add(item);
+
+ // 在隔离模式下执行导出,自动保存和恢复可见性
+ result = VisibilityHelper.ExecuteInIsolation(itemsToShow, () =>
+ {
+ // 创建导出选项
+ var exportOptions = new NwdExportOptions
+ {
+ ExcludeHiddenItems = true
+ };
+
+ // 执行导出
+ document.ExportToNwd(filePath, exportOptions);
+ LogManager.Info($"[SectionBoxExporter] NWD导出完成: {objects.Count} 个对象 -> {filePath}");
+ return filePath;
+ });
+ });
+
+ return result;
+ }
+
///
/// 显示保存文件对话框
///
- private string ShowSaveFileDialog()
+ private string ShowSaveFileDialog(out bool exportAsNwd)
{
+ exportAsNwd = true; // 默认导出为NWD
using (var dialog = new SaveFileDialog())
{
- dialog.Filter = "JSON files (*.json)|*.json|All files (*.*)|*.*";
- dialog.DefaultExt = "json";
- dialog.FileName = $"SectionBox_Export_{DateTime.Now:yyyyMMdd_HHmmss}.json";
+ // NWD 作为默认格式,JSON 作为备选
+ dialog.Filter = "Navisworks files (*.nwd)|*.nwd|JSON files (*.json)|*.json";
+ dialog.DefaultExt = "nwd";
+ dialog.FileName = $"SectionBox_Export_{DateTime.Now:yyyyMMdd_HHmmss}";
dialog.Title = "导出剖面盒信息";
if (dialog.ShowDialog() == DialogResult.OK)
{
+ // 如果用户选择了JSON(FilterIndex为2或文件扩展名为json)
+ exportAsNwd = !(dialog.FilterIndex == 2 ||
+ dialog.FileName.EndsWith(".json", StringComparison.OrdinalIgnoreCase));
return dialog.FileName;
}
}
diff --git a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs
index e0de785..58ca869 100644
--- a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs
+++ b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs
@@ -1853,15 +1853,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info($"[LayerManagementViewModel] 准备导出 {itemsToExport.Count} 个节点");
- // 保存当前可见性状态
- var originalVisibilityState = SaveCurrentVisibilityState(document);
-
- try
+ // 在隔离模式下执行导出,自动恢复可见性
+ VisibilityHelper.ExecuteInIsolation(itemsToExport, () =>
{
- // 隔离显示
- bool isolateSuccess = VisibilityHelper.IsolateSpecificItems(itemsToExport);
- if (!isolateSuccess) throw new InvalidOperationException("隔离显示失败");
-
// 导出选项
var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions
{
@@ -1873,12 +1867,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
document.ExportToNwd(saveFilePath, exportOptions);
exportResult = true;
LogManager.Info("[LayerManagementViewModel] ExportToNwd API调用完成");
- }
- finally
- {
- // 恢复可见性
- RestoreVisibilityState(document, originalVisibilityState);
- }
+ });
}
catch (Exception ex)
{
@@ -1930,112 +1919,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
- ///
- /// 保存当前可见性状态
- ///
- private Dictionary SaveCurrentVisibilityState(Document document)
- {
- var visibilityState = new Dictionary();
-
- try
- {
- // 获取所有顶级项目并记录它们的可见性状态
- foreach (Model model in document.Models)
- {
- foreach (ModelItem topLevelItem in model.RootItem.Children)
- {
- try
- {
- // 记录是否隐藏(IsHidden为true表示隐藏,我们存储可见性所以取反)
- visibilityState[topLevelItem] = !topLevelItem.IsHidden;
- }
- catch
- {
- // 如果无法获取状态,默认为可见
- visibilityState[topLevelItem] = true;
- }
- }
- }
-
- LogManager.Info($"[LayerManagementViewModel] 保存可见性状态完成,记录了 {visibilityState.Count} 个顶级项目");
- }
- catch (Exception ex)
- {
- LogManager.Error($"[LayerManagementViewModel] 保存可见性状态失败: {ex.Message}");
- }
-
- return visibilityState;
- }
-
- ///
- /// 恢复可见性状态
- ///
- private void RestoreVisibilityState(Document document, Dictionary visibilityState)
- {
- try
- {
- LogManager.Info("[LayerManagementViewModel] 恢复可见性状态");
-
- if (visibilityState == null || visibilityState.Count == 0)
- {
- LogManager.Warning("[LayerManagementViewModel] 没有可见性状态需要恢复,重置为全部可见");
- document.Models.ResetAllHidden();
- return;
- }
-
- // 使用成熟的可见性控制模式 - 分别收集要显示和隐藏的项目
- var itemsToShow = new ModelItemCollection();
- var itemsToHide = new ModelItemCollection();
-
- foreach (var kvp in visibilityState)
- {
- try
- {
- if (kvp.Value) // 原来是可见的
- {
- itemsToShow.Add(kvp.Key);
- }
- else // 原来是隐藏的
- {
- itemsToHide.Add(kvp.Key);
- }
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[LayerManagementViewModel] 处理项目可见性状态时出错: {ex.Message}");
- }
- }
-
- // 首先重置所有项目为可见状态
- document.Models.ResetAllHidden();
-
- // 然后隐藏原来应该隐藏的项目
- if (itemsToHide.Count > 0)
- {
- document.Models.SetHidden(itemsToHide, true);
- LogManager.Info($"[LayerManagementViewModel] 恢复隐藏 {itemsToHide.Count} 个项目");
- }
-
- LogManager.Info("[LayerManagementViewModel] 可见性状态恢复完成");
- }
- catch (Exception ex)
- {
- LogManager.Error($"[LayerManagementViewModel] 恢复可见性状态失败: {ex.Message}");
- // 失败时至少确保模型处于可见状态
- try
- {
- document.Models.ResetAllHidden();
- LogManager.Info("[LayerManagementViewModel] 已重置为全部可见状态");
- }
- catch (Exception resetEx)
- {
- LogManager.Error($"[LayerManagementViewModel] 重置可见性也失败: {resetEx.Message}");
- }
- }
- }
-
-
-
///
/// 测试ExportToNwd API - 专门的导出API
///
diff --git a/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs b/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs
index f9a3f35..1e0093e 100644
--- a/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs
+++ b/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs
@@ -1435,35 +1435,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else
{
- ShowAllInternal();
+ // 取消"仅显示物流元素",退出隔离模式
+ if (VisibilityHelper.ExitIsolation())
+ {
+ // 更新物流模型列表中的可见性状态
+ foreach (var model in LogisticsModels)
+ {
+ model.IsVisible = true;
+ }
+
+ UpdateMainStatus("已恢复可见性状态");
+ LogManager.Info("[UI-ModelSettings] 退出隔离模式,恢复可见性状态");
+ }
+ else
+ {
+ // 没有隔离状态,显示全部
+ VisibilityHelper.ShowAllItems();
+ UpdateMainStatus("已显示全部元素");
+ LogManager.Info("[UI-ModelSettings] 没有隔离状态,显示全部");
+ }
}
}, "应用可见性模式", runOnUIThread: true);
}
- ///
- /// 内部方法:显示所有元素
- ///
- private void ShowAllInternal()
- {
- bool success = VisibilityHelper.ShowAllItems();
-
- if (success)
- {
- foreach (var model in LogisticsModels)
- {
- model.IsVisible = true;
- }
-
- UpdateMainStatus("显示所有元素");
- LogManager.Info("切换到显示全部模式");
- }
- else
- {
- UpdateMainStatus("显示全部失败");
- LogManager.Error("显示全部失败");
- }
- }
-
///
/// 内部方法:仅显示物流元素
///
@@ -1492,7 +1486,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info($"[UI-ModelSettings] 找到 {logisticsItems.Count} 个物流元素");
- bool success = VisibilityHelper.IsolateSpecificItems(logisticsItems);
+ // 进入隔离模式(自动保存当前状态)
+ bool success = VisibilityHelper.EnterIsolation(logisticsItems);
if (success)
{
@@ -1507,7 +1502,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else
{
- LogManager.Error("[UI-ModelSettings] 隔离显示失败");
+ LogManager.Error("[UI-ModelSettings] 进入隔离模式失败");
UpdateMainStatus("操作失败");
}
}
diff --git a/src/Utils/VisibilityHelper.cs b/src/Utils/VisibilityHelper.cs
index c5b9cf8..1dd9e26 100644
--- a/src/Utils/VisibilityHelper.cs
+++ b/src/Utils/VisibilityHelper.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
using NavisApplication = Autodesk.Navisworks.Api.Application;
@@ -141,5 +142,229 @@ namespace NavisworksTransport
}
}
+ // 可见性状态栈(支持嵌套隔离)
+ private static readonly Stack> _visibilityStateStack = new Stack>();
+
+ ///
+ /// 进入隔离模式:保存当前可见性状态,并隔离显示指定项目
+ ///
+ /// 要显示的项目集合
+ /// 操作是否成功
+ public static bool EnterIsolation(ModelItemCollection itemsToShow)
+ {
+ try
+ {
+ var document = NavisApplication.ActiveDocument;
+ if (document == null)
+ {
+ LogManager.Warning("[VisibilityHelper] 进入隔离模式失败:没有活动文档");
+ return false;
+ }
+
+ // 保存当前状态到栈
+ var state = SaveVisibilityStateInternal(document);
+ _visibilityStateStack.Push(state);
+
+ // 执行隔离显示
+ bool success = IsolateSpecificItems(itemsToShow);
+ if (!success)
+ {
+ // 隔离失败,弹出状态
+ _visibilityStateStack.Pop();
+ return false;
+ }
+
+ LogManager.Info($"[VisibilityHelper] 进入隔离模式,当前栈深度: {_visibilityStateStack.Count}");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[VisibilityHelper] 进入隔离模式失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 退出隔离模式:恢复之前保存的可见性状态
+ ///
+ /// 操作是否成功
+ public static bool ExitIsolation()
+ {
+ try
+ {
+ if (_visibilityStateStack.Count == 0)
+ {
+ LogManager.Warning("[VisibilityHelper] 退出隔离模式失败:没有保存的状态");
+ return false;
+ }
+
+ var document = NavisApplication.ActiveDocument;
+ if (document == null)
+ {
+ LogManager.Warning("[VisibilityHelper] 退出隔离模式失败:没有活动文档");
+ return false;
+ }
+
+ // 弹出并恢复状态
+ var state = _visibilityStateStack.Pop();
+ RestoreVisibilityStateInternal(document, state);
+
+ LogManager.Info($"[VisibilityHelper] 退出隔离模式,当前栈深度: {_visibilityStateStack.Count}");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[VisibilityHelper] 退出隔离模式失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 在隔离模式下执行操作,自动恢复可见性
+ ///
+ /// 要显示的项目集合
+ /// 要执行的操作
+ public static void ExecuteInIsolation(ModelItemCollection itemsToShow, Action action)
+ {
+ bool entered = EnterIsolation(itemsToShow);
+ try
+ {
+ action?.Invoke();
+ }
+ finally
+ {
+ if (entered)
+ {
+ ExitIsolation();
+ }
+ }
+ }
+
+ ///
+ /// 在隔离模式下执行操作(带返回值),自动恢复可见性
+ ///
+ /// 返回值类型
+ /// 要显示的项目集合
+ /// 要执行的函数
+ /// 函数执行结果
+ public static T ExecuteInIsolation(ModelItemCollection itemsToShow, Func func)
+ {
+ bool entered = EnterIsolation(itemsToShow);
+ try
+ {
+ return func.Invoke();
+ }
+ finally
+ {
+ if (entered)
+ {
+ ExitIsolation();
+ }
+ }
+ }
+
+ ///
+ /// 获取当前隔离栈深度(用于调试)
+ ///
+ public static int IsolationDepth => _visibilityStateStack.Count;
+
+ ///
+ /// 清空所有保存的可见性状态(谨慎使用)
+ ///
+ public static void ClearIsolationStack()
+ {
+ _visibilityStateStack.Clear();
+ LogManager.Warning("[VisibilityHelper] 已清空所有隔离状态");
+ }
+
+ #region 内部实现
+
+ ///
+ /// 保存当前可见性状态(内部实现)
+ ///
+ private static Dictionary SaveVisibilityStateInternal(Document document)
+ {
+ var visibilityState = new Dictionary();
+
+ try
+ {
+ if (document?.Models == null)
+ return visibilityState;
+
+ foreach (Model model in document.Models)
+ {
+ if (model.RootItem?.Children == null)
+ continue;
+
+ foreach (ModelItem topLevelItem in model.RootItem.Children)
+ {
+ try
+ {
+ visibilityState[topLevelItem] = !topLevelItem.IsHidden;
+ }
+ catch
+ {
+ visibilityState[topLevelItem] = true;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[VisibilityHelper] 保存可见性状态失败: {ex.Message}");
+ }
+
+ return visibilityState;
+ }
+
+ ///
+ /// 恢复可见性状态(内部实现)
+ ///
+ private static void RestoreVisibilityStateInternal(Document document, Dictionary visibilityState)
+ {
+ try
+ {
+ if (document?.Models == null)
+ return;
+
+ if (visibilityState == null || visibilityState.Count == 0)
+ {
+ document.Models.ResetAllHidden();
+ return;
+ }
+
+ var itemsToHide = new ModelItemCollection();
+
+ foreach (var kvp in visibilityState)
+ {
+ try
+ {
+ if (!kvp.Value) // 原来是隐藏的
+ {
+ itemsToHide.Add(kvp.Key);
+ }
+ }
+ catch { }
+ }
+
+ document.Models.ResetAllHidden();
+
+ if (itemsToHide.Count > 0)
+ {
+ document.Models.SetHidden(itemsToHide, true);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[VisibilityHelper] 恢复可见性状态失败: {ex.Message}");
+ try
+ {
+ document.Models.ResetAllHidden();
+ }
+ catch { }
+ }
+ }
+
+ #endregion
}
}
\ No newline at end of file