Revit二次开发之DMU

  Revit是一款三维联动的设计软件,一处修改处处修改, 比如,在三维视图修改了墙的位置,二维视图上墙的位置也跟着变化了,同时,墙上的门窗也会跟着移动。 这种联动关系是Revit内部设计好的,如果我们需要一些自定义的联动关系, 比如我希望两面墙的总长度是固定的,增加一面墙长度之后,另外一面墙会跟着缩减。又或者,链接文档的某个构件移动了,希望主文件的某个不相关的构件也移动。这个时候,Revit本身的关系已经不足以满足我们的需求,我们就可以使用DMU(Dynamic Model Update)了。

一、相关类

1、UpdaterRegistry

  修改器注册工具类,Autodesk.Revit.DB下。

  用来向Revit注册、修改或销毁IUpdater实现类实例以及为Updater实例添加Trigger等。

public class UpdaterRegistry : IDisposable
{//给定ChangeType和一定的元素范围,当此范围中的元素发生ChangeType对应的改变时,给定的Updater执行//ElementFilter可以参考RevitApi,其所有的子类都可以使用,根据需求赋值即可public static void AddTrigger(UpdaterId id, ElementFilter filter, ChangeType change);public static void AddTrigger(UpdaterId id, Document document, ElementFilter filter, ChangeType change);public static void AddTrigger(UpdaterId id, Document document, ICollection<ElementId> elements, ChangeType change);//移除给定Updater相关的所有追踪器public static void RemoveAllTriggers(UpdaterId id);public static void RemoveDocumentTriggers(UpdaterId id, Document document);//使给定的Updater失效public static void DisableUpdater(UpdaterId id);//使给定的Updater生效public static void EnableUpdater(UpdaterId id);//给定的Updater是否有效public static bool IsUpdaterEnabled(UpdaterId id);//给定的Updater是否是必须的public static bool GetIsUpdaterOptional(UpdaterId id);//设置的Updater是否是必须的public static void SetIsUpdaterOptional(UpdaterId id, bool isOptional);//获取已经注册的所有Updater信息public static IList<UpdaterInfo> GetRegisteredUpdaterInfos();public static IList<UpdaterInfo> GetRegisteredUpdaterInfos(Document document);//给定的UpdaterId是否已经被注册public static bool IsUpdaterRegistered(UpdaterId id);public static bool IsUpdaterRegistered(UpdaterId id, Document document);//注册Updaterpublic static void RegisterUpdater(IUpdater updater);public static void RegisterUpdater(IUpdater updater, Document document);public static void RegisterUpdater(IUpdater updater, bool isOptional);public static void RegisterUpdater(IUpdater updater, Document document, bool isOptional);//注销已经注册的Updaterpublic static void UnregisterUpdater(UpdaterId id);public static void UnregisterUpdater(UpdaterId id, Document document);//设置两个Updater的执行顺序public static void SetExecutionOrder(UpdaterId first, UpdaterId second);
}

2、IUpdater

  接口,DMU的执行者,Autodesk.Revit.DB下。

public interface IUpdater
{//Revit的回调函数,当Updater追踪到相关元素发生变化时,自动调用此方法实现联动void Execute(UpdaterData data);//获取当前Updater的附加信息string GetAdditionalInformation();//获取执行的优先级,ChangePriority是一个枚举,定义了多个Updater实例共同被触发时执行的顺序ChangePriority GetChangePriority();//获取当前Updater实例的Id,UpdaterId类型UpdaterId GetUpdaterId();//获取当前Updater的名字string GetUpdaterName();
}

  当我们使用DMU时,第一步就是实现IUpdater接口,实现其中的方法,尤其是Execute方法。

3、UpdaterId

  类,用来唯一标识一个Updater,Autodesk.Revit.DB下。

public class UpdaterId : IDisposable
{//构造方法,需要一个AddInId、一个Guid//其中,addInId为UIApplication.ActiveAddInId,表示Revit当前的活动插件,不能随便给。//val为一个随机的Guid,表示Updater的唯一标识public UpdaterId(AddInId addInId, Guid val);//获取AddInIdpublic AddInId GetAddInId();//获取Guidpublic Guid GetGUID();
}

  一个AddInId和一个Guid确定了一个UpdaterId,一个UpdaterId唯一确定了一个Updater。而AddInId本质上也是一个Guid:

 public class AddInId : IDisposable{public AddInId(Guid val);public string GetAddInName();public string GetAddInNameFromDocument(Document aDoc);public Guid GetGUID();}

  所以从根本上讲,两个Guid确定了一个UpdaterId,也唯一确定了一个Updater。具有相同UpdaterId的Updater不能同时被注册。不同的Updater实例,只要构造时使用的两个Guid相同,它们就是相同的UpdaterId。

4、UpdaterInfo

  类,封装了Updater的信息,Autodesk.Revit.DB下。

public class UpdaterInfo : IDisposable
{public string AdditionalInformation { get; }//构建Updater时使用的AddInId对应的Application名,可能是一个外部应用名,也可能是一个外部命令名public string ApplicationName { get; }public bool IsOptional { get; }public bool IsValidObject { get; }//Updater的名字public string UpdaterName { get; }
}

5、UpdaterData

  类,封装了文档信息、文档中增删改的元素信息等,Autodesk.Revit.DB下。

public class UpdaterData : IDisposable
{//获取文档public Document GetDocument();//获取增加的元素Idpublic ICollection<ElementId> GetAddedElementIds();//获取删除的元素Idpublic ICollection<ElementId> GetDeletedElementIds();//获取修改的元素Idpublic ICollection<ElementId> GetModifiedElementIds();//判断某一个给定元素的某种改变是否是被追踪的public bool IsChangeTriggered(ElementId id, ChangeType type);
}

​  此类用在IUpdater的Execute的形参处,在Updater被触发时,Revit会把文档中的元素信息封装起来传递给回调函数,开发者可以在回调函数中据此实现联动的逻辑代码。

6、ChangeType

  类,表示元素的变化类型,Autodesk.Revit.DB下。

  这里的Change指的是被追踪的元素做出的改变。

public class ChangeType : IDisposable
{//融合给定的两种变化类型public static ChangeType ConcatenateChangeTypes(ChangeType changeType1, ChangeType changeType2);//当前类型是否包含给定的类型public bool Contains(ChangeType changeType);//当前类型是否和给定的类型完全相同public bool IsIdentical(ChangeType changeType);
}

  此类没有提供构造方法,想要获取它的实例,需要调用Element类中的静态方法:

//任何变化
public static ChangeType GetChangeTypeAny();
//元素添加
public static ChangeType GetChangeTypeElementAddition();
//元素删除
public static ChangeType GetChangeTypeElementDeletion();
//元素集合变化
public static ChangeType GetChangeTypeGeometry();
//元素参数变化
public static ChangeType GetChangeTypeParameter(Parameter param);
//元素参数变化
public static ChangeType GetChangeTypeParameter(ElementId parameterId);

  在DMU中,此类被用在UpdaterRegistry.AddTrigger方法中。

  例如:UpdaterRegistry.AddTrigger(UpdaterId id, ElementFilter filter, ChangeType change);

  它表示为给定的Updater添加一个追踪器,这个追踪器追踪的元素范围由ElementFilter 确定,只有此范围内的元素发生了ChangeType 指定的变化时,updater才会被触发。

7、ChangePriority

  枚举,表示Updater执行的优先级,如下表,自上而下,依次降低。

  这里的Change指的是Updater回调函数要做的修改。

GridsLevelsReferencePlanes = 0,
Masses = 1,
FloorsRoofsStructuralWalls = 2,
Structure = 3,
InteriorWalls = 4,
DoorsOpeningsWindows = 5,
MEPFixtures = 6,
RoomsSpacesZones = 7,
MEPCalculations = 8,
FreeStandingComponents = 9,
Connections = 10,
Rebar = 11,
MEPAccessoriesFittingsSegmentsWires = 12,
MEPSystems = 13,
Views = 14,
DetailComponents = 15,
Annotations = 16

  当多个Updater被同时触发时,Revit在调用回调函数的时候会有优先级排列,优先级高的先执行,优先级低的后执行。Updater优先级的设置是在其待实现的方法ChangePriority GetChangePriority()中进行的,返回你想要的设置的值即可。

  注意:回调函数要修改的内容和Updater优先级ChangePriority值之间并不是必须对应的,比如我要修改门窗洞,我也可以使用GridsLevelsReferencePlanes,并不一定是DoorsOpeningsWindows。只不过建议还是使用对应的优先级,因为这样不会在多个IUpdater之间产生修改的冲突。

二、注意事项

1、关于AddInId

  在构造Updater时,需要创建一个UpdaterId实例,而创建UpdaterId实例需要使用一个AddInId和一个Guid,虽然AddInId本质上也是一个Guid,但是它们的指向完全不一样。

  public UpdaterId(AddInId addInId, Guid val);

  addInId:必须要是当前插件的AddInId,也就是addin文件中的AddInId或者ClientId值,需要通过UIApplication.ActiveAddInId获取,而不能使用Guid随机生成。

  val:这就是一个随机的Guid,可以和addInId的Guid值一致,也可以不一致。

  AddInId值适合addin文件代码块一一对应的。例如:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<RevitAddIns><AddIn Type="Application"><Assembly>F:\LearnTest\RevitApiTest\bin\Debug\RevitApiTest.dll</Assembly><ClientId>356CDA5A-E6C5-4c2f-A9EF-B3222116B3C8</ClientId><FullClassName>RevitApiTest.DMU.DMUApplication</FullClassName><Name>DMU</Name><VendorId>Autodesk</VendorId><VendorDescription>Autodesk, www.autodesk.com</VendorDescription>      </AddIn><AddIn Type="Application"><Assembly>F:\LearnTest\RevitApiTest\bin\Debug\RevitApiTest.dll</Assembly><ClientId>753FDA5A-E6C5-4c2f-A9EF-B3222116B3C8</ClientId><FullClassName>RevitApiTest.DMU.DMUApplication1</FullClassName><Name>DMU</Name><VendorId>Autodesk</VendorId><VendorDescription>Autodesk, www.autodesk.com</VendorDescription>    </AddIn>
</RevitAddIns>

  上面的addIn文件就包含了两个addin文件代码块,一个addin文件代码块对应一个addInId,无论此代码块注册了多少按钮(命令),它们都公用同一个addInId。

  一个Application类型的文件代码块可以注册多个按钮,每一个按钮代表一个外部命令,这些外部命令公用同一个addInId。

  一个Command类型的文件代码块只能在“附加模块–外部–外部工具”下创建一个选项,代表一个外部命令,独享一个addInId。

2、关于Updater的注销

  当我们向Revit注册了Updater之后,如果不进行注销操作,Updater的生命周期会一直到Revit程序退出。如果我们需要在Revit运行时注销已经注册的Updater,就需要使用UpdaterRegistry 中的UnregisterUpdater方法,例如:UnregisterUpdater(UpdaterId id);

  此时就需要传入一个UpdaterId ,而往往注册Updater和注销Updater是不同的命令,这就导致注册Updater时使用的Guid无法获取到的,进而不能创建正确的UpdaterId。为了解决这种方式,我们可以在注册Updater时将注册信息保存到Xml文件中,在销毁时去读取此文件,进而获取正确的Guid值,构建正确的UpdaterId实例,执行注销操作。

三、代码实例

1、MyUpdater

public class MyUpdater : IUpdater
{public UpdaterId updaterId;public FreeStandingComponentsUpdater(AddInId addInId, Guid updaterGuid){updaterId = new UpdaterId(addInId, updaterGuid);}public void Execute(UpdaterData data){var modElems = data.GetModifiedElementIds().ToList();if (modElems.Count() != 0) {System.Windows.Forms.MessageBox.Show("DMU Execute");}}public string GetAdditionalInformation(){return "Info: Test DMU";}public ChangePriority GetChangePriority(){return ChangePriority.FreeStandingComponents;}public UpdaterId GetUpdaterId(){return updaterId;}public string GetUpdaterName(){return "MyUpdater";}
}

2、自定义UpdaterInfo

public class UpdaterInfo
{//名字public string Name { get; set; }//AddInIdpublic string UpdaterId_AddInId { get; set; }//随机的guidpublic string UpdaterId_Val { get; set; }//附加信息public string AdditionalInformation { get; set; }//优先级public int ChangePriority { get; set; }
}

3、XmlUtils工具类

public class XmlUtils
{public string xmlPath;public XmlUtils() {xmlPath = @"F:\LearnTest\RevitApiTest\DMU\UpdaterInfos.xml";if (!File.Exists(xmlPath)){Init();}}private void Init() {XmlDocument xDoc = new XmlDocument();XmlDeclaration xmldecl = xDoc.CreateXmlDeclaration("1.0", "utf-8", null);XmlElement root = xDoc.DocumentElement;xDoc.InsertBefore(xmldecl, root);XmlElement updaterInfosElem = xDoc.CreateElement("UpdaterInfos");XmlNode updaterInfosNode = xDoc.AppendChild(updaterInfosElem);xDoc.Save(xmlPath);}//添加public void AddUpdaterInfo(UpdaterInfo info) {XmlDocument xDoc = new XmlDocument();xDoc.Load(xmlPath);var updaterInfos = xDoc.GetElementsByTagName("UpdaterInfos")[0];var updaterInfoList = updaterInfos.ChildNodes.Cast<XmlNode>().ToList();XmlNode targetNode = updaterInfoList.FirstOrDefault(o => o.Attributes["Name"].Value == name);UpdaterInfo res = new UpdaterInfo();res.Name = targetNode.Attributes["Name"].Value;res.UpdaterId_AddInId = targetNode.Attributes["UpdaterId_AddInId"].Value;res.UpdaterId_Val = targetNode.Attributes["UpdaterId_Val"].Value;res.AdditionalInformation = targetNode.Attributes["AdditionalInformation"].Value;res.ChangePriority = Convert.ToInt32(targetNode.Attributes["ChangePriority"].Value);return res;}//获取public UpdaterInfo GetUpdaterInfo(string name) {XmlDocument xDoc = new XmlDocument();xDoc.Load(xmlPath);var updaterInfos = xDoc.GetElementsByTagName("UpdaterInfos")[0];var updaterInfoList = updaterInfos.ChildNodes.Cast<XmlNode>().ToList();XmlNode targetNode = updaterInfoList.FirstOrDefault(o => o.Attributes["Name"].Value == name);UpdaterInfo res = new UpdaterInfo();res.Name = targetNode.Attributes["Name"].Value;res.UpdaterId_AddInId = targetNode.Attributes["UpdaterId_AddInId"].Value;res.UpdaterId_Val = targetNode.Attributes["UpdaterId_Val"].Value;res.AdditionalInformation = targetNode.Attributes["AdditionalInformation"].Value;res.ChangePriority = Convert.ToInt32(targetNode.Attributes["ChangePriority"].Value);return res;}
}

4、DMURegistryCommand

[Transaction(TransactionMode.Manual)]
class DMURegistryCommand : IExternalCommand
{/// 注册IUpdaterpublic Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements){var uiApp = commandData.Application;var app = uiApp.Application;var uiDoc = uiApp.ActiveUIDocument;var doc = uiDoc.Document;var addInId = uiApp.ActiveAddInId;var guid = addInId.GetGUID();//这里省去判断UpdaterId是否已经被注册操作FreeStandingComponentsUpdater updater1 = new FreeStandingComponentsUpdater(addInId, guid);UpdaterRegistry.RegisterUpdater(updater1, false);//构造UpdaterInfo并记录到xml文档XmlUtils xmlUtils = new XmlUtils();var updaterInfo = new UpdaterInfo();updaterInfo.Name = updater1.GetUpdaterName();updaterInfo.UpdaterId_AddInId = updater1.GetUpdaterId().GetAddInId().GetGUID().ToString();updaterInfo.UpdaterId_Val = updater1.GetUpdaterId().GetGUID().ToString();updaterInfo.AdditionalInformation = updater1.GetAdditionalInformation();updaterInfo.ChangePriority = (int)updater1.GetChangePriority();xmlUtils.AddUpdaterInfo(updaterInfo);ElementClassFilter elementClassFilter = new ElementClassFilter(typeof(FamilyInstance), false);UpdaterRegistry.AddTrigger(updater1.GetUpdaterId(), elementClassFilter, Element.GetChangeTypeAny());return Result.Succeeded;}
}

5、DMUUnRegistryCommand

[Transaction(TransactionMode.Manual)]
//注销Register
class DMUUnRegistryCommand : IExternalCommand
{public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements){XmlUtils xmlUtils = new XmlUtils();var updaterInfo = xmlUtils.GetUpdaterInfo("FreeStandingComponentsUpdater");var updaterID = new UpdaterId(new AddInId(new Guid(updaterInfo.UpdaterId_AddInId)), new Guid(updaterInfo.UpdaterId_Val));UpdaterRegistry.UnregisterUpdater(updaterID);//这里省去删除Xml节点操作return Result.Succeeded;}
}

Revit二次开发之DMU相关推荐

  1. Revit二次开发之CAD线的翻模--梁的翻模

    最近开始翻模旅程,根据跟群友讨论和网上的搜寻.初步方法为先拾取CAD梁的线段,根据线段所在图层,取得所有梁的最长的那2段线.这样情况下,一条梁仅有2段相等的且平行的直线(不在是一个矩形),具体如图1- ...

  2. Revit二次开发之“选择某一楼层的墙”

    其实就是过滤器的用法.这里想要找到同一楼层中的风管,不可行. 要用:duct.ReferenceLevel //选择某一楼层上的墙 [Transaction(TransactionMode.Manua ...

  3. Revit二次开发之“取得所选元素的族名称”

    start //根据族实例FamilyInstance找到Family的方法:FamilyInstance.Symbol.Family [Transaction(TransactionMode.Man ...

  4. Revit二次开发之“PromptForFamilyInstancePlacement()函数动态拖动/鼠标跟随”效果

    布置设备的时候,希望有个拖动的效果,这个函数可以实现. //跟随鼠标拖动效果 [Transaction(TransactionMode.Manual)] [Regeneration(Regenerat ...

  5. Revit二次开发之“遍历材质判断材质类别的新方法”BuiltInParameter.PHY_MATERIAL_PARAM_CLASS...

    老版本API的方法: if (material is MaterialSteel) 新版本API的方法: Parameter curPara = material.get_Parameter(Buil ...

  6. Revit二次开发之“使用ElementTransformUtils.MoveElement()移动元素”

    使用ElementTransformUtils.MoveElement()移动元素,改变元素的Location属性 [Transaction(TransactionMode.Manual)] [Reg ...

  7. Revit二次开发之“创建尺寸标注”

    运行该命令,选中一个可标注的对象,即实现标注. [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.Transaction ...

  8. Revit二次开发之“为Ribbon设置快捷键”

    目前不支持使用代码为Ribbon设置快捷键. 以下是帮助文档: 单击"视图"选项卡 "窗口"面板 "用户界面"下拉列表 "快捷键& ...

  9. Revit二次开发之“让对象处于被选择状态”

    要想让对象被选择,需要知道对象的ID比如506025 uidoc.Selection.Elements.Add(element); [Transaction(TransactionMode.Manua ...

最新文章

  1. docker 数据卷 mysql_Docker容器数据卷原理及使用方法解析
  2. 无线循环里面 string = “i”会内存溢出吗?_记一次公司JVM堆溢出抽茧剥丝定位的过程...
  3. oracle 分段函数,清华大学出版社-图书详情-《新高考数学题型全归纳(基础版)》...
  4. java 写文件 属性吗_使用JAVA读写Properties属性文件
  5. 毕业生当头一棒?忆本科四年,高校毕业生与就业单位基本要求差多少?工作还是考研?
  6. C语言课后习题(27)
  7. 微软想证明Windows比Chrome好 主要源自恐惧?
  8. Linux 配置本地yum源步骤
  9. 备课好帮手,免费分享 下载
  10. 国产数据库 OceanBase 二次刷榜 TPC-C,7 亿 tpmC
  11. 开发一款APP需要多少钱?
  12. mongoDb一对多之springboot demo
  13. 方便微信公众号征集图片视频的小程序
  14. 【笔记:Spring】
  15. 海康威视监控云存储服务器,监控设备,云存储服务器,安防监控—年轻人安防官网...
  16. 千万级巨型汉语词库分享
  17. 想学linux需要的电脑配置相关
  18. 北漂生活-租房那些事
  19. MetaMask轻钱包教程 修心手把手带你玩小狐狸 钱包小白必备非原创
  20. 2022百度收录批量自动推送助手

热门文章

  1. Python制作一系列倒计时器,倒计时结束播放音乐
  2. Android 车载应用开发与分析 - CarLauncher
  3. 利用python进行数据分析 笔记_利用python进行数据分析(O#039;Relly)学习笔记-一团网...
  4. C语言 蓝桥杯 1221是一个非常特殊的数,它从左边读和从右边读是一样的,编程求所有这样的四位十进制数。
  5. PicoScenes安装踩坑记录
  6. 加拿大访学/博后的子女选择公立学校及办理入学手续详解
  7. Ubuntu上搭建git服务器
  8. SUMO仿真教程(7)—— 交通需求模型介绍
  9. 产品原型这么做,才叫真的爽!
  10. 观察者模式和委托实现 - 老鼠跑,猫叫,人醒