RevitAPI: 使用DMU(Dynamic Model Update)实现联动


我们知道Revit的一大特色功能就是关联修改,即,一处修改处处修改,比如,在三维视图修改了墙的位置,二维视图上墙的位置也跟着变化了,同时,墙上的门窗也会跟着移动。

但有时候我们有自己的特殊需求,也想做到自定义的联动关系,比如我希望两面墙的总长度是固定的,增加一面墙长度之后,另外一面墙会跟着缩减。又或者,链接文档的某个构件移动了,希望主文件的某个不相关的构件也移动。这个时候,Revit本身的关系已经不足以满足我们的需求,我们就可以使用DMU(Dynamic Model Update)了。


什么是DMU呢?简单的说,是一种事件,或者叫发布-订阅(Sub-Pub)模式,即我们在Revit里面注册一个回调函数,告诉Revit当某种类型的变动发生的时候,就调用该回调函数。


注册

先来看怎么注册。是通过UpdaterRegistry.RegisterUpdater函数,它的签名如下:

public class UpdaterRegistry : IDisposable
{
    public static void RegisterUpdater(IUpdater updater);
    public static void RegisterUpdater(IUpdater updater, bool isOptional);
    public static void RegisterUpdater(IUpdater updater, Document document);
    public static void RegisterUpdater(IUpdater updater, Document document, bool isOptional);
}

它的几个参数的含义见下表:

参数 含义
IUpdater updater 处理事件的对象,需要实现一系列的函数
bool isOptional 是否为可选,如果设为true则表示对Revit来讲可有可无,false则表示很重要,一定需要它,如果Revit启动的时候,或者打开某个文档的时候,没有找到需要的updater,Revit会弹出警告框
Document document 对应的文档,如果不指定,则表示该updater在应用级别起作用,和文档无关。如果指定了,则表示只在该文档起作用,其他文档都和它无关

实现IUpdater接口

从上面的参数中可以看到,我们需要创建一个IUpdater接口的对象,那么再来看怎么实现IUpdater接口,下面是一个例子程序:

public class ParameterUpdater : IUpdater
{
    UpdaterId _uid;
    public ParameterUpdater(Guid guid)
    {
        _uid = new UpdaterId(new AddInId(
            new Guid("c1f5f009-8ba9-4f1d-b0fb-ba41a0f69942")), // addin id
            guid); // updater id
    }

    public void Execute(UpdaterData data)
    {
        Func<ICollection<ElementId>, string> toString = ids => ids.Aggregate("", (ss, id) => ss + "," + id).TrimStart(',');
        var sb = new StringBuilder();
        sb.AppendLine("added:" + toString(data.GetAddedElementIds()));
        sb.AppendLine("modified:" + toString(data.GetModifiedElementIds()));
        sb.AppendLine("deleted:" + toString(data.GetDeletedElementIds()));
        TaskDialog.Show("Changes", sb.ToString());
    }

    public string GetAdditionalInformation()
    {
        return "N/A";
    }

    public ChangePriority GetChangePriority()
    {
        return ChangePriority.FreeStandingComponents;
    }

    public UpdaterId GetUpdaterId()
    {
        return _uid;
    }

    public string GetUpdaterName()
    {
        return "ParameterUpdater";
    }
}


其中比较重要的有以下几点:

  • UpdaterId的创建,第一个参数是一个Guid,该Guid是注册updater的插件的guid,必须和.addin文件里面的ClientId或AddinId一致,可能是Command也可能是Application,如下面就是该Updater的Command入口,所以第一个参数应该是c1f5f009-8ba9-4f1d-b0fb-ba41a0f69942,第二个参数是该Updater的Guid,可以使用Visual Stuido自带的工具创建一个新的。

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <RevitAddIns>
      <AddIn Type="Command">
        <Name>CommandB</Name>
        <ClientId><span style="font-family: Arial, Helvetica, sans-serif;">c1f5f009-8ba9-4f1d-b0fb-ba41a0f69942</span><span style="font-family: Arial, Helvetica, sans-serif;"></ClientId></span>
        <Assembly>D:\ADN\Test\bin\Debug\CommandB.dll</Assembly>
        <FullClassName>ApplicationB.CommandB</FullClassName>
        <VendorId>ADSK</VendorId>
      </AddIn>
    </RevitAddIns>


  • Execute函数是主要的回调函数,它会在DMU触发的时候被调用,通过该函数的参数UpdaterData,我们可以获取很多内容,比如UpdaterData..GetDocument()可以获取Document, GetAddedElementIds()可以获取被添加的元素id。我们可以在此函数里面做很多事情,例如,修改其他元素实现关联修改。这完全可以取决于我们自己。
  • 在Execute函数里面不能使用Transaction,因为它本身就在Transaction里面
定义好IUpdater的实现类之后,我们就可以注册一个Updater对象了:
ParameterUpdater _updater = new ParameterUpdater(new Guid("{E305C880-2918-4FB0-8062-EE1FA70FABD6}"));
UpdaterRegistry.RegisterUpdater(_updater, true);
这里的Guid是使用VS自带的工具创建的

指定触发条件

最后还有一件事就是告诉Revit在什么情况下触发,需要使用AddTrigger函数,下面是它的签名:
public class UpdaterRegistry : IDisposable
{
    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);
}

下表是它各个参数的意义:
参数 含义
UpdaterId id Updater的id
ElementFilter filter 针对特定的元素,这些元素满足该filter的条件
ICollection<ElementId> elements 只针对这些特定的元素
Document document 只针对某个文档
ChangeType change 某种特定的修改才会触发,例如,当参数变化时(Element.GetChangeTypeParameter),或者当几何变化时(Element.GetChangeTypeGeometry),或任何变化(Element.GetChangeTypeAny)等等

如果我们想要某个Area的面积改变时触发,可以这样来写:

var parameter = element.get_Parameter(BuiltInParameter.ROOM_AREA);
UpdaterRegistry.AddTrigger(_updater.GetUpdaterId(), doc, new List<ElementId>() { new ElementId(197280)}, Element.GetChangeTypeParameter(parameter));

至此,整个DMU的流程代码都做好了,当我们改变id为197280的Area的面积的时候,Area的参数“Area"(面积)就会改变,Execute函数就会被触发。我们就可以在触发函数里面做任何我们想做的事情了。当然,还要注意,不要造成死循环了 :)

注:如果希望卸载Trigger,可以调用UpdaterRegistry.RemoveAllTriggers或者UpdaterRegistry.RemoveDocumentTriggers,卸载updater可以调用UpdaterRegistry.UnregisterUpdater



阅读更多
版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/lushibi/article/details/50629022
文章标签: RevitAPI DMU
个人分类: RevitAPI DMU
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

RevitAPI: 使用DMU(Dynamic Model Update)实现联动

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭