前言:项目中需要一个需求,用户想调用出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事件去监控选择了什么构件的话,如果构件选择过多的时候,也是会出现错误的。大家如果遇到类似的问题,可以通过调试的方式去查看对应的问题。