fix: ExpandAndSelect uses ModelItem.Equals for precise sibling matching

- 新增 AliasNodeViewModel.ModelItem 属性(持有 Navisworks 原生引用)
- BuildNode 中存储 ModelItem
- ExpandAndSelect 重写为 DFS 递归,用 ModelItem.Equals 精确匹配
  (同 ModelItemAnalysisHelper.ModelItemEquals 逻辑,底层原生对象比较)
- 不再依赖 IndexPath 或 _nodeMap 做选中同步
This commit is contained in:
tian 2026-05-31 13:17:52 +08:00
parent 4feb12f6b9
commit c002feee51
2 changed files with 55 additions and 31 deletions

View File

@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport.UI.WPF.ViewModels
{
@ -17,9 +18,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private bool _hasAlias;
private int _childCount;
/// <summary>节点的序列化标识 key(对应 AliasNodeIdentity.ToKey()</summary>
/// <summary>节点的序列化标识 key</summary>
public string NodeKey { get; set; }
/// <summary>Navisworks ModelItem 引用(用于选中同步的比较)</summary>
public Autodesk.Navisworks.Api.ModelItem ModelItem { get; set; }
/// <summary>当前显示文本(别名优先,无别名回退到原名)</summary>
public string DisplayName
{

View File

@ -41,10 +41,13 @@ namespace NavisworksTransport.UI.WPF.Views
AliasTreeView.ItemsSource = rootNodes;
// 文档切换事件
NavisApplication.ActiveDocumentChanged += (s, e) => RebuildTree();
NavisApplication.ActiveDocumentChanged += (s, e) => { _built = false; RebuildTree(); };
NavisApplication.ActiveDocumentChanging += (s, e) => { _built = false; };
HookSelectionEvents();
// 立即构建:面板打开时如果文档已存在,不走事件也要构建
RebuildTree();
}
/// <summary>
@ -246,6 +249,7 @@ namespace NavisworksTransport.UI.WPF.Views
var node = new AliasNodeViewModel
{
NodeKey = key,
ModelItem = modelItem,
OriginalName = modelItem.DisplayName ?? "?",
Alias = alias,
ChildCount = modelItem.Children.Count()
@ -427,51 +431,67 @@ namespace NavisworksTransport.UI.WPF.Views
// ============================================================
// 展开并高亮指定 ModelItem
// 策略DFS 遍历别名树,用 ModelItem.Equals 精确匹配。
// 遇到未展开节点时先触发 LazyLoadChildren然后递归搜索。
// ============================================================
private void ExpandAndSelect(Autodesk.Navisworks.Api.ModelItem item)
{
var identity = AliasNodeIdentity.FromModelItem(item);
string key = identity.ToKey();
var rootNodes = AliasTreeView.ItemsSource
as System.Collections.ObjectModel.ObservableCollection<AliasNodeViewModel>;
if (rootNodes == null || rootNodes.Count == 0) return;
if (!_nodeMap.TryGetValue(key, out var targetNode))
return;
// 收集从根到目标的路径
var path = new List<AliasNodeViewModel>();
var current = targetNode;
while (current != null)
foreach (var rootNode in rootNodes)
{
path.Insert(0, current);
current = current.Parent;
if (ExpandAndSelectRecursive(rootNode, item, AliasTreeView))
return;
}
}
// 逐层展开
ItemsControl currentContainer = AliasTreeView;
foreach (var node in path)
private bool ExpandAndSelectRecursive(
AliasNodeViewModel node,
Autodesk.Navisworks.Api.ModelItem target,
ItemsControl currentContainer)
{
// 1. 展开容器 + 触发懒加载
if (currentContainer is TreeViewItem tvi)
{
// 触发懒加载
var aliasMap = _dataStore?.LoadAll();
LazyLoadChildren(node, aliasMap);
tvi.IsExpanded = true;
tvi.UpdateLayout();
}
var aliasMap = _dataStore?.LoadAll();
LazyLoadChildren(node, aliasMap);
if (currentContainer is TreeViewItem tvi)
// 2. 检查自身ModelItem.Equals 是底层原生对象比较,精确区分同名兄弟)
if (node.ModelItem != null && node.ModelItem.Equals(target))
{
if (currentContainer is TreeViewItem selfTvi)
{
tvi.IsExpanded = true;
tvi.UpdateLayout();
selfTvi.IsSelected = true;
selfTvi.BringIntoView();
}
var container = currentContainer.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem;
if (container == null) break;
currentContainer = container;
return true;
}
// 高亮目标
if (currentContainer is TreeViewItem targetTvi && targetTvi.DataContext is AliasNodeViewModel target
&& target.NodeKey == key)
// 3. DFS 子节点
for (int i = 0; i < node.Children.Count; i++)
{
targetTvi.IsSelected = true;
targetTvi.BringIntoView();
var child = node.Children[i];
var childContainer = currentContainer.ItemContainerGenerator
.ContainerFromItem(child) as TreeViewItem;
if (childContainer == null)
{
currentContainer.UpdateLayout();
childContainer = currentContainer.ItemContainerGenerator
.ContainerFromItem(child) as TreeViewItem;
}
if (childContainer == null) continue;
if (ExpandAndSelectRecursive(child, target, childContainer))
return true;
}
return false;
}
// ============================================================