diff --git a/src/Core/AliasTree/AliasNodeIdentity.cs b/src/Core/AliasTree/AliasNodeIdentity.cs
index 38ef7e1..7442ecf 100644
--- a/src/Core/AliasTree/AliasNodeIdentity.cs
+++ b/src/Core/AliasTree/AliasNodeIdentity.cs
@@ -53,16 +53,16 @@ namespace NavisworksTransport.Core.AliasTree
if (System.Threading.Thread.CurrentThread.GetApartmentState() != System.Threading.ApartmentState.STA)
LogManager.Error($"[别名树] FromModelItem 不在 STA 线程!");
- // 1. IndexPath — 复用 ModelItemAnalysisHelper
+ // 1. IndexPath — 使用 Navisworks 官方 API CreatePathId
string indexPath;
try
{
- var indexPaths = ModelItemAnalysisHelper.GetModelItemIndexPaths(item);
- indexPath = indexPaths.FirstOrDefault() ?? string.Empty;
+ var pid = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CreatePathId(item);
+ indexPath = $"{pid.ModelIndex}:{pid.PathId}";
}
catch (Exception ex)
{
- LogManager.Error($"[别名树] GetModelItemIndexPaths 异常: {ex.Message}");
+ LogManager.Error($"[别名树] CreatePathId 异常: {ex.Message}");
indexPath = string.Empty;
}
diff --git a/src/UI/WPF/Views/AliasTreeControl.xaml.cs b/src/UI/WPF/Views/AliasTreeControl.xaml.cs
index 31585cc..a02383e 100644
--- a/src/UI/WPF/Views/AliasTreeControl.xaml.cs
+++ b/src/UI/WPF/Views/AliasTreeControl.xaml.cs
@@ -9,6 +9,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
+using Autodesk.Navisworks.Api.DocumentParts;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport.UI.WPF.Views
@@ -84,6 +85,11 @@ namespace NavisworksTransport.UI.WPF.Views
RebuildTree();
else
RefreshAliasesFromStore();
+
+ // 无论树是否已构建,确保已存别名的深层节点路径被加载
+ var aliasMap = _dataStore?.LoadAll();
+ if (aliasMap != null && aliasMap.Count > 0)
+ EnsureAliasedPathsVisible(aliasMap);
}
private void RefreshAliasesFromStore()
@@ -289,6 +295,10 @@ namespace NavisworksTransport.UI.WPF.Views
// 重新挂载选择事件(可能文档刚切换)
HookSelectionEvents();
+ // 对已有别名的深层节点,强制加载其路径使其可见
+ if (aliasMap != null && aliasMap.Count > 0)
+ EnsureAliasedPathsVisible(aliasMap);
+
LogManager.Info($"[别名树] 树构建完成,共 {_nodeMap.Count} 个节点");
}
catch (Exception ex)
@@ -392,128 +402,41 @@ namespace NavisworksTransport.UI.WPF.Views
}
// ============================================================
- // 从 NodeKey 回溯定位 ModelItem
- // 策略:IndexPath(COM 树索引路径)优先,DisplayPath 降级
+ // 从 NodeKey 回溯定位 ModelItem(仅用 IndexPath)
// ============================================================
private Autodesk.Navisworks.Api.ModelItem FindModelItemByNodeKey(string key)
{
if (!AliasNodeIdentity.TryParseKey(key, out var identity))
return null;
+ if (string.IsNullOrEmpty(identity.IndexPath))
+ return null;
try
{
var doc = NavisApplication.ActiveDocument;
if (doc == null || doc.Models == null) return null;
- // 策略 1:从 IndexPath 定位(最快、最精确)
- if (!string.IsNullOrEmpty(identity.IndexPath))
- {
- var item = LocateByIndexPath(doc, identity.IndexPath);
- if (item != null && ModelItemAnalysisHelper.IsModelItemValid(item))
- return item;
- }
+ // 解析 IndexPath: "modelIndex:PathId"
+ int colonIdx = identity.IndexPath.IndexOf(':');
+ if (colonIdx < 0) return null;
+ if (!int.TryParse(identity.IndexPath.Substring(0, colonIdx), out int modelIndex))
+ return null;
+ string pathId = identity.IndexPath.Substring(colonIdx + 1);
- // 策略 2:从 DisplayPath 降级定位
- return LocateByDisplayPath(doc, identity);
+ LogManager.Info($"[别名树] FindByKey: IndexPath={identity.IndexPath}, modelIndex={modelIndex}, pathId={pathId}");
+
+ var result = doc.Models.ResolvePathId(new ModelItemPathId { ModelIndex = modelIndex, PathId = pathId });
+ LogManager.Info($"[别名树] FindByKey: ResolvePathId -> {(result != null ? result.DisplayName : "null")}");
+ return result;
}
- catch
+ catch (Exception ex)
{
+ LogManager.Error($"[别名树] FindByKey 失败: {key}, {ex.Message}");
return null;
}
}
- ///
- /// 按 COM 索引路径定位 ModelItem
- /// 路径格式:"模型索引,一级子索引,二级子索引,..."
- ///
- private static Autodesk.Navisworks.Api.ModelItem LocateByIndexPath(
- Autodesk.Navisworks.Api.Document doc, string indexPath)
- {
- string[] parts = indexPath.Split(',');
- if (parts.Length == 0) return null;
-
- if (!int.TryParse(parts[0], out int modelIndex))
- return null;
-
- // 取模型
- if (modelIndex < 0 || modelIndex >= doc.Models.Count)
- return null;
-
- var model = doc.Models[modelIndex];
- if (model?.RootItem == null) return null;
-
- Autodesk.Navisworks.Api.ModelItem current = model.RootItem;
-
- for (int i = 1; i < parts.Length; i++)
- {
- if (!int.TryParse(parts[i], out int childIndex))
- return null;
-
- var childrenList = current.Children.ToList();
- if (childIndex < 0 || childIndex >= childrenList.Count)
- return null;
-
- current = childrenList[childIndex];
- }
-
- return current;
- }
-
- ///
- /// 按 DisplayPath 降级定位
- ///
- private static Autodesk.Navisworks.Api.ModelItem LocateByDisplayPath(
- Autodesk.Navisworks.Api.Document doc, AliasNodeIdentity identity)
- {
- string[] parts = identity.GetHierarchicalPath().Split('/');
- if (parts.Length == 0) return null;
-
- // parts[0] 是模型名/根节点名,匹配模型
- foreach (var model in doc.Models)
- {
- var current = model.RootItem;
- if (current == null) continue;
- if (current.DisplayName != parts[0]) continue;
-
- for (int i = 1; i < parts.Length; i++)
- {
- string targetName = parts[i];
- Autodesk.Navisworks.Api.ModelItem matched = null;
- foreach (var child in current.Children)
- {
- if (child.DisplayName == targetName)
- {
- matched = child;
- break;
- }
- }
- if (matched == null) return null;
- current = matched;
- }
-
- // 同级同名去重索引
- int siblingIndex = identity.GetSiblingIndex();
- if (siblingIndex > 0)
- {
- int idx = 0;
- foreach (var child in current.Children)
- {
- if (child.DisplayName == parts.Last())
- {
- if (idx == siblingIndex) return child;
- idx++;
- }
- }
- return null;
- }
-
- return current;
- }
-
- return null;
- }
-
// ============================================================
// 展开并高亮指定 ModelItem
// 策略:利用 ModelItem.Parent 链逐层匹配,不依赖 IndexPath。
@@ -667,6 +590,129 @@ namespace NavisworksTransport.UI.WPF.Views
}
}
+ ///
+ /// 确保所有已存别名的节点路径在树中可见。
+ /// 在 RebuildTree 后调用,对深层别名节点逐级加载其祖先。
+ ///
+ private void EnsureAliasedPathsVisible(Dictionary aliasMap)
+ {
+ LogManager.Info($"[别名树] EnsureAliasedPathsVisible: 共 {aliasMap.Count} 条别名");
+ foreach (var kvp in aliasMap)
+ {
+ LogManager.Info($"[别名树] alias: key={kvp.Key} -> \"{kvp.Value}\"");
+ }
+
+ int loaded = 0;
+ foreach (var kvp in aliasMap)
+ {
+ try
+ {
+ LogManager.Info($"[别名树] EnsureAliased: key={kvp.Key}, alias={kvp.Value}");
+ var item = FindModelItemByNodeKey(kvp.Key);
+ if (item == null)
+ {
+ LogManager.Warning($"[别名树] EnsureAliased: FindModelItemByNodeKey 返回 null, key={kvp.Key}");
+ continue;
+ }
+ LogManager.Info($"[别名树] EnsureAliased: 找到 ModelItem DisplayName={item.DisplayName}, ClassName={item.ClassName}");
+ if (EnsurePathLoaded(item))
+ loaded++;
+ else
+ LogManager.Warning($"[别名树] EnsureAliased: EnsurePathLoaded 返回 false, item={item.DisplayName}");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[别名树] 加载别名路径失败: {kvp.Key}, {ex.Message}");
+ }
+ }
+ if (loaded > 0)
+ LogManager.Info($"[别名树] 已确保 {loaded} 个别名节点路径可见");
+ }
+
+ ///
+ /// 沿 ModelItem 父链逐层加载节点到树中(不选中)。
+ /// 逻辑与 ExpandAndSelect 相同,只是最终不选中。
+ ///
+ private bool EnsurePathLoaded(Autodesk.Navisworks.Api.ModelItem item)
+ {
+ // 1. 构建祖先链
+ var chain = new System.Collections.Generic.List();
+ var current = item;
+ while (current != null)
+ {
+ chain.Insert(0, current);
+ current = current.Parent;
+ }
+
+ var chainStr = string.Join(" -> ", chain.ConvertAll(c => c.DisplayName ?? "?"));
+ LogManager.Info($"[别名树] EnsurePathLoaded: chain=[{chainStr}], depth={chain.Count - 1}");
+
+ // 2. 匹配根节点
+ var rootNodes = AliasTreeView.ItemsSource
+ as System.Collections.ObjectModel.ObservableCollection;
+ if (rootNodes == null) { LogManager.Warning("[别名树] EnsurePathLoaded: rootNodes null"); return false; }
+
+ AliasNodeViewModel treeNode = null;
+ foreach (var rn in rootNodes)
+ {
+ if (rn.ModelItem != null && rn.ModelItem.Equals(chain[0]))
+ {
+ treeNode = rn;
+ break;
+ }
+ }
+ if (treeNode == null)
+ {
+ LogManager.Warning($"[别名树] EnsurePathLoaded: 根节点未匹配 chain[0]={chain[0].DisplayName}");
+ return false;
+ }
+ if (chain.Count == 1) return true;
+
+ // 3. 逐层加载
+ for (int i = 1; i < chain.Count; i++)
+ {
+ var targetModelItem = chain[i];
+ LogManager.Info($"[别名树] EnsurePathLoaded: L{i} cur={treeNode.OriginalName} target={targetModelItem.DisplayName} childCnt={treeNode.Children.Count}");
+ ExpandTreeNode(treeNode);
+
+ if (treeNode.Children.Count == 1 && treeNode.Children[0].OriginalName == "加载中...")
+ {
+ LogManager.Info($"[别名树] EnsurePathLoaded: L{i} 懒加载子节点");
+ LazyLoadChildren(treeNode, _dataStore?.LoadAll());
+ }
+
+ AliasNodeViewModel child = null;
+ foreach (var c in treeNode.Children)
+ {
+ if (c.OriginalName != "加载中..." && c.ModelItem != null && c.ModelItem.Equals(targetModelItem))
+ {
+ child = c;
+ break;
+ }
+ }
+
+ if (child == null)
+ {
+ LogManager.Info($"[别名树] EnsurePathLoaded: L{i} 未匹配, BuildNode 创建");
+ child = BuildNode(targetModelItem, _dataStore?.LoadAll(), treeNode.Depth + 1);
+ if (child != null)
+ {
+ child.Parent = treeNode;
+ treeNode.Children.Add(child);
+ }
+ }
+
+ if (child == null)
+ {
+ LogManager.Warning($"[别名树] EnsurePathLoaded: L{i} child 为 null, 中断");
+ return false;
+ }
+ treeNode = child;
+ }
+
+ return true;
+ }
+
///
/// 从 TreeView 根开始,沿 parent 链找 ViewModel 对应的 TreeViewItem。
/// 虚拟化下可能返回 null。