前言:项目中需要一个需求,用户想调用出Revit中自带的绘制模型线方法,然后再绘制结束时,可以拿到绘制的模型线,然后实现后面的算法。这里记录一种方法,通过DocumentChange事件+修改Tag的PropertyChanged事件来实现对应的需求。
以下示例使用的都是Revit2019的环境进行开发,其他版本会涉及到对应的API的不同,可以自行修改
1.背景
用户需要运行命令后,点击选择墙面,然后在墙面上绘制模型线,在推出绘制模式的时候,可以提取线,并且删除绘制的线
 
 
2.代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB.Events;
using System.ComponentModel;
using Autodesk.Windows;
using System.Windows;
namespace RevitTest
{
    [Transaction(TransactionMode.Manual)]
    public class DUMDemo : IExternalCommand
    {
        /// <summary>
        /// 创建的构件
        /// </summary>
        private List<ElementId> _createIDList = new List<ElementId>();
        /// <summary>
        /// 判断是否已经载入
        /// </summary>
        private bool _subscribed;
        /// <summary>
        /// 外部事件
        /// </summary>
        private ExternalEvent _external;
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            var tReference = commandData.Application.ActiveUIDocument.Selection.PickObject(ObjectType.Face, "选择面");
            CreateLine(commandData.Application, tReference);
            return Result.Succeeded;
        }
        public void CreateLine(UIApplication uiapp, Reference selRef)
        {
            Document doc = uiapp.ActiveUIDocument.Document;
            using (Transaction ts = new Transaction(doc, "CreatModelCurve"))
            {
                ts.Start();
                Face face = doc.GetElement(selRef).GetGeometryObjectFromReference(selRef) as Face;
                Plane tPlan = Plane.CreateByNormalAndOrigin(face.ComputeNormal(new UV()), selRef.GlobalPoint);
                doc.ActiveView.SketchPlane = SketchPlane.Create(doc, tPlan);
                ts.Commit();
            }
            RevitCommandId revitCommand = RevitCommandId.LookupCommandId("ID_OBJECTS_PROJECT_CURVE");
            if (revitCommand != null && uiapp.CanPostCommand(revitCommand))
            {
                //加入document改变事件
                uiapp.Application.DocumentChanged += PostElement;
                //添加标签修改事件
                foreach (var tab in ComponentManager.Ribbon.Tabs)
                {
                    if (tab.Id == "Modify")
                    {
                        if (_subscribed)
                        {
                            tab.PropertyChanged -= TabPropertyChangedEvent;
                            _subscribed = false;
                        }
                        else
                        {
                            tab.PropertyChanged += TabPropertyChangedEvent;
                            _subscribed = true;
                        }
                        break;
                    }
                }
                //添加外部事件
                BaseEvent selectChangeEvent = new BaseEvent();
                selectChangeEvent.EventAction = app =>
                {
                    Transaction trans = new Transaction(app.ActiveUIDocument.Document, "删除线");
                    trans.Start();
                    try
                    {
                        app.Application.DocumentChanged -= PostElement;
                        app.ActiveUIDocument.Document.Delete(_createIDList);
                        _createIDList.Clear();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    trans.Commit();
                };
                _external = ExternalEvent.Create(selectChangeEvent);
                uiapp.PostCommand(revitCommand);
            }
        }
        /// <summary>
        /// 监控构件修改事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PostElement(object sender, DocumentChangedEventArgs e)
        {
            List<ElementId> collection = e.GetAddedElementIds().ToList();
            _createIDList.AddRange(collection);
        }
        /// <summary>
        /// 修改标签事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TabPropertyChangedEvent(object sender, PropertyChangedEventArgs e)
        {
            if (!(sender is RibbonTab))
            {
                return;
            }
            if (!string.Equals(e.PropertyName, "Title", StringComparison.CurrentCultureIgnoreCase))
            {
                return;
            }
            if (_subscribed == false)
            {
                (sender as RibbonTab).PropertyChanged -= TabPropertyChangedEvent;
                MessageBox.Show($"{_createIDList.Count}");
                _external.Raise();
            }
            else
            {
                _subscribed = false;
            }
        }
    }
    /// <summary>
    /// 外部事件
    /// </summary>
    public class BaseEvent : IExternalEventHandler
    {
        /// <summary>
        /// 实现方法的委托函数
        /// </summary>
        public Action<UIApplication> EventAction { get; set; }
        public void Execute(UIApplication app)
        {
            EventAction?.Invoke(app);
        }
        public string GetName()
        {
            return "事件";
        }
    }
}
3.代码分析
3.1 调用Revit中自带的模型线生成方法
通过直接调用内置方法的CommandId来实现放置模型线的方法
RevitCommandId revitCommand = RevitCommandId.LookupCommandId("ID_OBJECTS_PROJECT_CURVE");
uiapp.PostCommand(revitCommand);
3.2 注册DocumentChange事件
注册DocumentChange事件,通过这个事件去监控构建的添加,然后收集到集合中
		uiapp.Application.DocumentChanged += PostElement;
		/// <summary>
        /// 监控构件修改事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PostElement(object sender, DocumentChangedEventArgs e)
        {
            List<ElementId> collection = e.GetAddedElementIds().ToList();
            _createIDList.AddRange(collection);
        }
3.3 注册修改Tab的PropertyChanged事件
通过tabs去遍历所有的tab,然后ID为"Modify"的Tab对应的是修改按钮。因为进入绘制模型线的时候,Tab会变成“修改 | 放置线”,结束绘制的时候,变回“修改”,因此给tab直接加上属性修改的事件来监控整个流程的开始和结束
                foreach (var tab in ComponentManager.Ribbon.Tabs)
                {
                    if (tab.Id == "Modify")
                    {
                        if (_subscribed)
                        {
                            tab.PropertyChanged -= TabPropertyChangedEvent;
                            _subscribed = false;
                        }
                        else
                        {
                            tab.PropertyChanged += TabPropertyChangedEvent;
                            _subscribed = true;
                        }
                        break;
                    }
                }
        
		/// <summary>
        /// 修改标签事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TabPropertyChangedEvent(object sender, PropertyChangedEventArgs e)
        {
            if (!(sender is RibbonTab))
            {
                return;
            }
            if (!string.Equals(e.PropertyName, "Title", StringComparison.CurrentCultureIgnoreCase))
            {
                return;
            }
            if (_subscribed == false)
            {
                (sender as RibbonTab).PropertyChanged -= TabPropertyChangedEvent;
                MessageBox.Show($"{_createIDList.Count}");
                _external.Raise();
            }
            else
            {
                _subscribed = false;
            }
        }
3.4 注册外部事件
外部事件的作用是在结束整体功能的时候,去除DocumentChange事件,以免照成事件加载多次的现象
//添加外部事件
                BaseEvent selectChangeEvent = new BaseEvent();
                selectChangeEvent.EventAction = app =>
                {
                    Transaction trans = new Transaction(app.ActiveUIDocument.Document, "删除线");
                    trans.Start();
                    try
                    {
                        app.Application.DocumentChanged -= PostElement;
                        app.ActiveUIDocument.Document.Delete(_createIDList);
                        _createIDList.Clear();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    trans.Commit();
                };
                _external = ExternalEvent.Create(selectChangeEvent);
BaseEvent是自己定义的外部事件接口,大家可以根据自己的想法来封装
4.写在最后
对得到的构件进行操作的话,逻辑应该写在外部事件里面。
这里提供了一个监控生成构建的解决方案,但是做很复杂的操作时,有可能会出现外部事件丢失的情况。如果是想通过PropertyChanged事件去监控选择了什么构件的话,如果构件选择过多的时候,也是会出现错误的。大家如果遇到类似的问题,可以通过调试的方式去查看对应的问题。


















