From f7544a2095642debd29a22958fd9e2d66bcf6097 Mon Sep 17 00:00:00 2001
From: tian <11429339@qq.com>
Date: Sun, 31 May 2026 22:30:54 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20=E5=88=AB=E5=90=8D=E6=A0=91=E6=B7=B1?=
=?UTF-8?q?=E5=BA=A6=E8=B6=85=E9=99=90=E8=8A=82=E7=82=B9=E5=AF=BC=E8=88=AA?=
=?UTF-8?q?=20=E2=80=94=20=E6=94=B9=E7=94=A8=20ModelItem.Parent=20?=
=?UTF-8?q?=E9=93=BE=E6=9B=BF=E4=BB=A3=20IndexPath?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除 IndexPath 解析/偏移量等易错逻辑
- ExpandAndSelect 改为沿 ModelItem.Parent 链回溯,ModelItem.Equals 匹配
- 深层节点通过 BuildNode 直接创建,不依赖占位/懒加载通道
- 删除 LoadPathChild、EnsureChildLoaded 等废弃方法
- 清理约 10 行净代码
---
src/UI/WPF/Views/AliasTreeControl.xaml.cs | 204 ++++++++++------------
1 file changed, 96 insertions(+), 108 deletions(-)
diff --git a/src/UI/WPF/Views/AliasTreeControl.xaml.cs b/src/UI/WPF/Views/AliasTreeControl.xaml.cs
index 54d7a29..31585cc 100644
--- a/src/UI/WPF/Views/AliasTreeControl.xaml.cs
+++ b/src/UI/WPF/Views/AliasTreeControl.xaml.cs
@@ -338,7 +338,7 @@ namespace NavisworksTransport.UI.WPF.Views
_nodeMap[key] = node;
- // 深度限制:超过阈值且无别名 → 不展开子节点
+ // 深度限制:超过阈值且无别名 → 不展开子节点,不加占位
bool hasChildren = node.ChildCount > 0;
if (hasChildren && depth >= MaxFullDisplayDepth && string.IsNullOrEmpty(alias))
hasChildren = false;
@@ -355,34 +355,6 @@ namespace NavisworksTransport.UI.WPF.Views
return node;
}
- ///
- /// 只加载指定索引的子节点(不全量加载,用于 ExpandAndSelect 加速)
- ///
- private void EnsureChildLoaded(AliasNodeViewModel parent, int index)
- {
- if (parent.Children.Count != 1 || parent.Children[0].OriginalName != "加载中...")
- return;
-
- parent.Children.Clear();
- try
- {
- var doc = NavisApplication.ActiveDocument;
- if (doc == null) return;
- var item = FindModelItemByNodeKey(parent.NodeKey);
- if (item == null) return;
-
- var aliasMap = _dataStore?.LoadAll();
- var childrenList = item.Children.ToList();
- int loadCount = Math.Min(index + 1, childrenList.Count);
- for (int i = 0; i < loadCount; i++)
- {
- var childNode = BuildNode(childrenList[i], aliasMap, parent.Depth + 1);
- if (childNode != null) { childNode.Parent = parent; parent.Children.Add(childNode); }
- }
- }
- catch (Exception ex) { LogManager.Error($"[别名树] EnsureChildLoaded: {ex.Message}"); }
- }
-
///
/// 懒加载子节点(在 Expanded 事件中触发)
///
@@ -544,109 +516,122 @@ namespace NavisworksTransport.UI.WPF.Views
// ============================================================
// 展开并高亮指定 ModelItem
- // 策略:用 FromModelItem 获取 IndexPath,沿路径逐层展开,不遍历整棵树。
+ // 策略:利用 ModelItem.Parent 链逐层匹配,不依赖 IndexPath。
// ============================================================
private void ExpandAndSelect(Autodesk.Navisworks.Api.ModelItem item)
{
- var identity = AliasNodeIdentity.FromModelItem(item);
- var indexPath = identity.IndexPath;
+ if (item == null) return;
- LogManager.Info($"[别名树] ExpandAndSelect: target={item.DisplayName}, IndexPath={indexPath}");
+ LogManager.Info($"[别名树] ExpandAndSelect: target={item.DisplayName}");
- if (string.IsNullOrEmpty(indexPath))
+ // 1. 沿 Parent 链构建祖先列表(根 → 目标)
+ var chain = new System.Collections.Generic.List();
+ var current = item;
+ while (current != null)
{
- LogManager.Warning($"[别名树] IndexPath 为空,无法导航: {item.DisplayName}");
- return;
+ chain.Insert(0, current);
+ current = current.Parent;
}
- string[] parts = indexPath.Split(',');
+ // 2. 在 rootNodes 中找匹配的根节点
var rootNodes = AliasTreeView.ItemsSource
as System.Collections.ObjectModel.ObservableCollection;
if (rootNodes == null || rootNodes.Count == 0) return;
- // IndexPath 第一个元素可能是模型索引。
- // 对单模型文档,IndexPath 只有一个元素(子节点索引)
- int startPart = 0;
- if (parts.Length > 1 && int.TryParse(parts[0], out int maybeModelIdx))
+ AliasNodeViewModel treeNode = null;
+ foreach (var rn in rootNodes)
{
- // 多模型:parts[0] 是模型索引,从 rootNodes[modelIdx] 开始
- if (maybeModelIdx < rootNodes.Count)
+ if (rn.ModelItem != null && rn.ModelItem.Equals(chain[0]))
{
- startPart = 1;
+ treeNode = rn;
+ break;
}
- // 单模型:parts[0] 直接是子节点索引
}
-
- // 确定起始根节点
- AliasNodeViewModel rootNode;
- ItemsControl currentContainer;
- if (startPart == 1 && int.TryParse(parts[0], out int modelIdx))
+ if (treeNode == null)
{
- rootNode = rootNodes[modelIdx];
- AliasTreeView.UpdateLayout();
- var rc = AliasTreeView.ItemContainerGenerator.ContainerFromItem(rootNode) as TreeViewItem;
- if (rc == null) return;
- rc.IsExpanded = true;
- rc.UpdateLayout();
- var aliasMap = _dataStore?.LoadAll();
- LazyLoadChildren(rootNode, aliasMap);
- currentContainer = rc;
- }
- else
- {
- rootNode = rootNodes[0];
- AliasTreeView.UpdateLayout();
- var rc = AliasTreeView.ItemContainerGenerator.ContainerFromItem(rootNode) as TreeViewItem;
- if (rc == null) return;
- currentContainer = rc;
- }
-
- // 先检查根节点
- if (rootNode.ModelItem != null && rootNode.ModelItem.Equals(item))
- {
- if (currentContainer is TreeViewItem t)
- {
- _isProgrammaticSelect = true;
- t.IsSelected = true;
- _isProgrammaticSelect = false;
- t.BringIntoView();
- }
+ LogManager.Warning($"[别名树] 根节点未找到: {chain[0].DisplayName}");
return;
}
- // 沿 IndexPath 逐层展开 — 只加载路径上的子节点
- AliasNodeViewModel current = rootNode;
-
- for (int i = startPart; i < parts.Length; i++)
+ // 3. 目标就是根节点
+ if (chain.Count == 1)
{
- if (!int.TryParse(parts[i], out int childIdx)) return;
- int adjIdx = childIdx - 1;
- if (adjIdx < 0) adjIdx = 0;
+ SelectTreeNode(treeNode);
+ return;
+ }
+
+ // 4. 沿链逐层向下,匹配/加载子节点
+ for (int i = 1; i < chain.Count; i++)
+ {
+ var targetModelItem = chain[i];
// 展开当前节点
- var tvi = GetTreeViewItemForNode(current);
- if (tvi != null)
- {
- _isProgrammaticExpand = true;
- tvi.IsExpanded = true;
- _isProgrammaticExpand = false;
- tvi.UpdateLayout();
- }
- var aliasMap = _dataStore?.LoadAll();
- LazyLoadChildren(current, aliasMap);
+ ExpandTreeNode(treeNode);
- if (adjIdx >= current.Children.Count)
+ // 加载子节点(有占位 → 全量懒加载;否则只找目标那一个)
+ var aliasMap = _dataStore?.LoadAll();
+ if (treeNode.Children.Count == 1 && treeNode.Children[0].OriginalName == "加载中...")
{
- LogManager.Warning($"[别名树] IndexPath 越界: parts[{i}]={childIdx}, adjIdx={adjIdx}, Children.Count={current.Children.Count}");
+ LazyLoadChildren(treeNode, aliasMap);
+ }
+
+ // 在已加载的子节点中查找匹配
+ AliasNodeViewModel child = null;
+ foreach (var c in treeNode.Children)
+ {
+ if (c.OriginalName != "加载中..." && c.ModelItem != null && c.ModelItem.Equals(targetModelItem))
+ {
+ child = c;
+ break;
+ }
+ }
+
+ // 没找到(深度超限节点)→ 直接用 ModelItem 创建
+ if (child == null)
+ {
+ child = BuildNode(targetModelItem, aliasMap, treeNode.Depth + 1);
+ if (child != null)
+ {
+ child.Parent = treeNode;
+ treeNode.Children.Add(child);
+ }
+ }
+
+ if (child == null)
+ {
+ LogManager.Warning($"[别名树] 无法定位子节点: {targetModelItem.DisplayName}");
return;
}
- current = current.Children[adjIdx];
+ treeNode = child;
}
- // 到达目标:强制父节点 BringIntoView,延迟后获取容器选中
- var parent = current.Parent;
+ // 5. 选中最终节点
+ SelectTreeNode(treeNode);
+ }
+
+ ///
+ /// 展开树节点(程序化,不触发 Expanded 事件)
+ ///
+ private void ExpandTreeNode(AliasNodeViewModel node)
+ {
+ var tvi = GetTreeViewItemForNode(node);
+ if (tvi != null)
+ {
+ _isProgrammaticExpand = true;
+ tvi.IsExpanded = true;
+ _isProgrammaticExpand = false;
+ tvi.UpdateLayout();
+ }
+ }
+
+ ///
+ /// 选中并滚动到指定树节点
+ ///
+ private void SelectTreeNode(AliasNodeViewModel node)
+ {
+ var parent = node.Parent;
if (parent != null)
{
var parentTvi = GetTreeViewItemForNode(parent);
@@ -655,11 +640,10 @@ namespace NavisworksTransport.UI.WPF.Views
parentTvi.BringIntoView();
parentTvi.UpdateLayout();
- // 延迟重试:BringIntoView 会触发滚动,创建更多容器
Dispatcher.BeginInvoke(new Action(() =>
{
AliasTreeView.UpdateLayout();
- var childTvi = parentTvi.ItemContainerGenerator.ContainerFromItem(current) as TreeViewItem;
+ var childTvi = parentTvi.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem;
if (childTvi != null)
{
_isProgrammaticSelect = true;
@@ -667,16 +651,20 @@ namespace NavisworksTransport.UI.WPF.Views
_isProgrammaticSelect = false;
childTvi.BringIntoView();
}
- else
- {
- LogManager.Warning($"[别名树] 延迟后仍未获取容器: {current.OriginalName}");
- }
}), System.Windows.Threading.DispatcherPriority.Background);
return;
}
}
- LogManager.Warning($"[别名树] 最终容器获取失败: {current.OriginalName}");
+ // 无父节点(根节点)直接选中
+ var tvi = GetTreeViewItemForNode(node);
+ if (tvi != null)
+ {
+ _isProgrammaticSelect = true;
+ tvi.IsSelected = true;
+ _isProgrammaticSelect = false;
+ tvi.BringIntoView();
+ }
}
///