.netautodesk-forgeautodeskbimautodesk-navisworks

Navisworks API selection null reference exception


I'm trying to create an action to check whether current selection in Autodesk Navisworks is a part of specific clash test results, and on top of that, to select all clash results where any of the selected elements is contained.

My code for getting clash results and model items intersection:

internal List<ModelItem> GroupIntersectingClashResults(Guid clashTestId, NW.ModelItemCollection clashResultSelectionToCheck)
{
    DocumentClash clashData = doc.GetClash();
    DocumentClashTests clashTests = clashData.TestsData;
    var targetTest = clashTests.Tests.FirstOrDefault(t => t.Guid == clashTestId) as ClashTest;

    if (targetTest == null)
    {
        Log.Warning("Clash test not found for id: {Id}", clashTestId);
        return [];
    }

    List<IClashResult> intersectingResults = FindIntersectingClashResults(targetTest, clashResultSelectionToCheck);
    if (!intersectingResults.Any())
    {
        Log.Warning("No intersecting items found for clash test id: {Id}", clashTestId);
        return [];
    }

    var mic = new List<ModelItem>();
    
    foreach (IClashResult clashResult in intersectingResults)
    {
        mic.AddRange(clashResult.Selection1);
        mic.AddRange(clashResult.Selection2);
    }

    return mic;
}

private static List<IClashResult> FindIntersectingClashResults(ClashTest clashTest, NW.ModelItemCollection selectionToCheck)
{
    var intersectionResults = new List<IClashResult>();
    foreach (SavedItem item in clashTest.Children)
    {
        if (item is not IClashResult clashResult)
            continue;

        if (selectionToCheck.IsAnyContained(clashResult.CompositeItemSelection1) || selectionToCheck.IsAnyContained(clashResult.CompositeItemSelection2))
            intersectionResults.Add(clashResult);
    }

    return intersectionResults;
}

This code works fine, and I'm really getting model items based on currently selected items. However, when I try to select them with Navisworks API, I'm getting null reference exception with this code:

public ClashResultsNavigatorControl()
{
    InitializeComponent();
    InitializeContextMenu();
    _doc = NW.Application.ActiveDocument;
    _clashTestNames = new Dictionary<Guid, string>();
    NW.Application.ActiveDocument.Models.CollectionChanged += OnDocumentChange;
    _doc.CurrentSelection.Changed += OnCurrentSelectionChange;
    Disposed += OnDispose;
    _doc.GetClash().TestsData.Changed += OnClashTestsDataChange;
}

private void OnCurrentSelectionChange(object sender, EventArgs e)
{
    try
    {
        var clashResultUtils = new ClashResultUtils(_doc);

        if (_doc.CurrentSelection.SelectedItems.Count == 0)
            return;

        List<ModelItem> selection = clashResultUtils
            .GroupIntersectingClashResults(_selectedClashTest.Guid, _doc.CurrentSelection.SelectedItems);

        foreach (ModelItem modelItem in selection)
        {
            
            var a = _doc.Models.FindIdForModelItem(modelItem);
        }
        if (selection.Count == 0)
            return;

        _doc.CurrentSelection.Changed -= OnCurrentSelectionChange;
        _doc.CurrentSelection.CopyFrom(selection);
    }
    catch (Exception ex)
    {
        ExceptionHandler.Handle(ex);
    }
    finally
    {
        _doc.CurrentSelection.Changed -= OnCurrentSelectionChange;
        _doc.CurrentSelection.Changed += OnCurrentSelectionChange;
    }
}

on line: _doc.CurrentSelection.CopyFrom(selection); but none of the elements from this line is null. Exception happens in NativeHandle.cs, on RuntimeChecks method.


Solution

  • Basically, need to get to geometry level and then back to first object. Model item collection created in this way can be used as a current selectiion. Everything else I tried didn't work.

    private ModelItemCollection GetModelItemsFromClashResults(List<ClashResult> clashResults)
            {
                var modelItems = new List<ModelItem>();
    
                foreach (ClashResult clashResult in clashResults)
                {
                    ModelItem firstObject1 = clashResult.CompositeItem1.FindFirstGeometry().Item.FindFirstObjectAncestor();
                    ModelItem firstObject2 = clashResult.CompositeItem2.FindFirstGeometry().Item.FindFirstObjectAncestor();
    
                    modelItems.Add(firstObject1);
                    modelItems.Add(firstObject2);
                }
    
                var collectionToFocus = new ModelItemCollection();
                collectionToFocus.AddRange(modelItems);
    
                return collectionToFocus;
            }