NavisworksTransport/doc/guide/navisworks-2017-api-guide.md

12 KiB
Raw Blame History

Navisworks 2017 API 使用指南

概述

本指南基于实际项目开发经验汇总了Navisworks 2017 API的核心用法和最佳实践。重点覆盖COM API的属性操作功能为插件开发提供实用的参考。

目录

  1. 环境配置
  2. COM API 基础
  3. 属性操作 API
  4. 常见问题与解决方案
  5. 最佳实践
  6. 参考资源

环境配置

必需引用

<!-- 项目文件中的引用 -->
<Reference Include="Autodesk.Navisworks.Api" />
<Reference Include="Autodesk.Navisworks.ComApi" />
<Reference Include="Autodesk.Navisworks.Interop.ComApi" />
<Reference Include="Autodesk.Navisworks.Interop.ComApiAutomation" />

命名空间导入

using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;

COM API 基础

COM API 状态对象获取

// 获取COM API状态对象 - 这是所有COM操作的入口点
ComApi.InwOpState10 state = ComApiBridge.State;

.NET 对象与 COM 对象转换

// ModelItem 转换为 COM Path
ComApi.InwOaPath3 comPath = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(modelItem);

// ModelItemCollection 转换为 COM Selection
ComApi.InwOpSelection comSelection = ComApiBridge.ToInwOpSelection(modelItems);

属性节点获取

// 获取模型项的属性节点
ComApi.InwGUIPropertyNode2 propertyNode = 
    (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(comPath, false);

属性操作 API

1. 创建用户定义属性

基本创建流程

public static void CreateUserDefinedProperty(ModelItem item, string categoryName, 
    Dictionary<string, string> properties)
{
    // 1. 获取COM API状态对象
    ComApi.InwOpState10 state = ComApiBridge.State;
    
    // 2. 转换ModelItem为COM路径
    ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item);
    
    // 3. 获取属性节点
    ComApi.InwGUIPropertyNode2 propertyNode = 
        (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false);
    
    // 4. 创建属性类别容器
    ComApi.InwOaPropertyVec propertyCategory = (ComApi.InwOaPropertyVec)state.ObjectFactory(
        ComApi.nwEObjectType.eObjectType_nwOaPropertyVec, null, null);
    
    // 5. 添加具体属性
    foreach (var kvp in properties)
    {
        AddProperty(state, propertyCategory, kvp.Key, kvp.Value, kvp.Key + "_Internal");
    }
    
    // 6. 设置用户定义属性 (index=0表示创建新类别)
    propertyNode.SetUserDefined(0, categoryName, categoryName + "_Internal", propertyCategory);
}

// 辅助方法:添加单个属性到类别
private static void AddProperty(ComApi.InwOpState10 state, 
    ComApi.InwOaPropertyVec propertyCategory, 
    string displayName, string value, string internalName)
{
    ComApi.InwOaProperty property = (ComApi.InwOaProperty)state.ObjectFactory(
        ComApi.nwEObjectType.eObjectType_nwOaProperty, null, null);
    
    property.name = internalName;      // 内部名称
    property.UserName = displayName;   // 显示名称
    property.value = value;            // 属性值
    
    propertyCategory.Properties().Add(property);
}

关键参数说明

  • index=0: 创建新的用户定义属性类别
  • categoryName: 属性类别的显示名称
  • internalName: 属性类别的内部名称(建议添加"_Internal"后缀)

2. 修改用户定义属性

查找现有属性索引

private static int FindUserDefinedPropertyIndex(ComApi.InwGUIPropertyNode2 propertyNode, 
    string categoryName)
{
    int index = 0;
    foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes())
    {
        if (attribute.UserDefined && attribute.ClassUserName == categoryName)
        {
            return index;
        }
        index++;
    }
    return -1; // 未找到
}

修改现有属性

public static void UpdateUserDefinedProperty(ModelItem item, string categoryName, 
    Dictionary<string, string> newProperties)
{
    ComApi.InwOpState10 state = ComApiBridge.State;
    ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item);
    ComApi.InwGUIPropertyNode2 propertyNode = 
        (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false);
    
    // 查找现有属性类别的索引
    int existingIndex = FindUserDefinedPropertyIndex(propertyNode, categoryName);
    
    if (existingIndex >= 0)
    {
        // 创建新的属性集合
        ComApi.InwOaPropertyVec newPropertyCategory = (ComApi.InwOaPropertyVec)state.ObjectFactory(
            ComApi.nwEObjectType.eObjectType_nwOaPropertyVec, null, null);
        
        // 添加新的属性值
        foreach (var kvp in newProperties)
        {
            AddProperty(state, newPropertyCategory, kvp.Key, kvp.Value, kvp.Key + "_Internal");
        }
        
        // 使用现有索引覆盖属性类别
        propertyNode.SetUserDefined(existingIndex, categoryName, 
                                  categoryName + "_Internal", newPropertyCategory);
    }
    else
    {
        // 如果不存在,则创建新的
        CreateUserDefinedProperty(item, categoryName, newProperties);
    }
}

关键要点

  • 使用现有索引: 修改时必须使用找到的现有索引而不是0
  • 完全覆盖: SetUserDefined会完全替换现有属性,而不是增量更新

3. 删除用户定义属性

计算相对索引

private static int GetUserDefinedPropertyRelativeIndex(ComApi.InwGUIPropertyNode2 propertyNode, 
    string categoryName)
{
    int relativeIndex = 0;
    foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes())
    {
        if (attribute.UserDefined)
        {
            if (attribute.ClassUserName == categoryName)
            {
                return relativeIndex;
            }
            relativeIndex++;
        }
    }
    return -1; // 未找到
}

删除属性类别

public static void RemoveUserDefinedProperty(ModelItem item, string categoryName)
{
    ComApi.InwOpState10 state = ComApiBridge.State;
    ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item);
    ComApi.InwGUIPropertyNode2 propertyNode = 
        (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false);
    
    // 计算用户定义属性的相对索引
    int relativeIndex = GetUserDefinedPropertyRelativeIndex(propertyNode, categoryName);
    
    if (relativeIndex >= 0)
    {
        // 用户定义属性索引从1开始所以需要+1
        int userDefinedIndex = relativeIndex + 1;
        
        // 使用RemoveUserDefined方法完全删除属性类别
        propertyNode.RemoveUserDefined(userDefinedIndex);
    }
}

关键要点

  • 索引从1开始: 用户定义属性的索引从1开始不是0
  • 相对索引: 需要计算在用户定义属性中的相对位置
  • 完全删除: RemoveUserDefined会完全移除属性类别,不留痕迹

4. 读取属性值

使用COM API读取

public static string GetPropertyValueViaCom(ModelItem item, string categoryName, 
    string propertyName)
{
    try
    {
        ComApi.InwOpState10 state = ComApiBridge.State;
        ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item);
        ComApi.InwGUIPropertyNode2 propertyNode = 
            (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false);
        
        // 遍历属性类别
        foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes())
        {
            if (attribute.UserDefined && attribute.ClassUserName == categoryName)
            {
                // 遍历类别中的属性
                foreach (ComApi.InwOaProperty property in attribute.Properties())
                {
                    if (property.UserName == propertyName)
                    {
                        return property.value?.ToString() ?? "";
                    }
                }
            }
        }
        
        return ""; // 未找到
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine($"读取属性失败: {ex.Message}");
        return "";
    }
}

常见问题与解决方案

1. 属性缓存同步问题

问题: COM API写入属性后.NET API读取到的仍是旧值。

解决方案:

// 强制刷新文档状态
private static void ForceDocumentRefresh()
{
    try
    {
        var doc = Application.ActiveDocument;
        if (doc != null)
        {
            // 强制刷新文档状态
            doc.CurrentSelection.Clear();
            Application.DoEvents();
        }
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine($"文档刷新失败: {ex.Message}");
    }
}

2. 索引计算错误

问题: 使用错误的索引导致操作失败。

解决方案:

  • 创建新属性:使用 index = 0
  • 修改现有属性使用绝对索引从0开始计数所有属性
  • 删除属性:使用相对索引+1在用户定义属性中的位置+1

3. 重复属性类别

问题: 每次操作都创建新的属性类别。

解决方案:

// 始终先检查是否存在现有属性
int existingIndex = FindUserDefinedPropertyIndex(propertyNode, categoryName);
if (existingIndex >= 0)
{
    // 修改现有属性
    propertyNode.SetUserDefined(existingIndex, categoryName, internalName, newProperties);
}
else
{
    // 创建新属性
    propertyNode.SetUserDefined(0, categoryName, internalName, newProperties);
}

最佳实践

1. 错误处理

try
{
    // COM API 操作
}
catch (System.Runtime.InteropServices.COMException comEx)
{
    // 处理COM特定错误
    LogManager.WriteLog($"COM API错误: {comEx.Message}");
}
catch (Exception ex)
{
    // 处理一般错误
    LogManager.WriteLog($"操作失败: {ex.Message}");
}

2. 日志记录

// 详细的操作日志
LogManager.WriteLog($"[属性操作] 开始为 {items.Count} 个模型设置属性");
LogManager.WriteLog($"[属性操作] 类别名称: {categoryName}");
LogManager.WriteLog($"[属性操作] 操作完成,成功: {successCount}, 失败: {failCount}");

3. 批量操作

public static int BatchUpdateProperties(ModelItemCollection items, 
    string categoryName, Dictionary<string, string> properties)
{
    int successCount = 0;
    
    foreach (ModelItem item in items)
    {
        try
        {
            UpdateUserDefinedProperty(item, categoryName, properties);
            successCount++;
        }
        catch (Exception ex)
        {
            LogManager.WriteLog($"处理模型 {item.DisplayName} 时失败: {ex.Message}");
        }
    }
    
    return successCount;
}

4. 内存管理

// COM对象使用完毕后释放引用
ComApi.InwOaPropertyVec propertyCategory = null;
ComApi.InwOaProperty property = null;

try
{
    // 使用COM对象
}
finally
{
    // 释放COM对象引用
    if (propertyCategory != null)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(propertyCategory);
    }
    if (property != null)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(property);
    }
}

API 索引机制总结

操作类型 索引类型 索引值 说明
创建新属性 固定值 0 始终使用0创建新的用户定义属性
修改现有属性 绝对索引 0-n 在所有属性中的位置(包括系统属性)
删除属性 相对索引+1 1-n 在用户定义属性中的位置+1

参考资源

官方文档

  • Navisworks 2017 SDK 文档安装目录下的api/documentation文件夹
  • Autodesk Developer Network (ADN)

社区资源

实用工具

  • Navisworks Object Model Browser查看对象结构
  • Visual Studio 调试工具COM对象检查

更新日志

v1.0 (2025-06-22)

  • 初始版本,基于物流属性管理功能开发经验
  • 涵盖COM API属性操作的核心方法
  • 包含实际项目中遇到的问题和解决方案

注意: 本指南将持续更新补充更多API用法和最佳实践。欢迎贡献新的内容和改进建议。