fix: 别名树深度超限节点导航 — 改用 ModelItem.Parent 链替代 IndexPath
- 移除 IndexPath 解析/偏移量等易错逻辑 - ExpandAndSelect 改为沿 ModelItem.Parent 链回溯,ModelItem.Equals 匹配 - 深层节点通过 BuildNode 直接创建,不依赖占位/懒加载通道 - 删除 LoadPathChild、EnsureChildLoaded 等废弃方法 - 清理约 10 行净代码
This commit is contained in:
parent
ea49774f39
commit
f7544a2095
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 只加载指定索引的子节点(不全量加载,用于 ExpandAndSelect 加速)
|
||||
/// </summary>
|
||||
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}"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 懒加载子节点(在 Expanded 事件中触发)
|
||||
/// </summary>
|
||||
@ -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<Autodesk.Navisworks.Api.ModelItem>();
|
||||
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<AliasNodeViewModel>;
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 展开树节点(程序化,不触发 Expanded 事件)
|
||||
/// </summary>
|
||||
private void ExpandTreeNode(AliasNodeViewModel node)
|
||||
{
|
||||
var tvi = GetTreeViewItemForNode(node);
|
||||
if (tvi != null)
|
||||
{
|
||||
_isProgrammaticExpand = true;
|
||||
tvi.IsExpanded = true;
|
||||
_isProgrammaticExpand = false;
|
||||
tvi.UpdateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选中并滚动到指定树节点
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user