ModelessForm_ExternalEvent,这个例子主要介绍的是 Revit External Event,用来生成非模态对话框。可以理解为,在不打断 Revit 正常操作的情况下,响应用户的请求。
在开始之前,先了解一下 External Event,可以参考官网:External Events
使用 External Event 的基本逻辑:
IExternalEventHandler
这个接口来实现一个事件处理的 handler
类;ExternalEvent.Create()
创建一个 ExternalEvent
;ExternalEvent.Raise()
;用户实现一个自己的 IExternalEventHandler,这个用户的处理函数,Revit 最终会在空闲的时候调用这个类的 Execute
方法。
public class ExternalEventExample : IExternalEventHandler
{
public void Execute(UIApplication app)
{
TaskDialog.Show("External Event", "Click Close to close.");
}
public string GetName()
{
return "External Event Example";
}
}
用静态方法 ExternalEvent.Create()
创建一个 ExternalEvent
。
public class ExternalEventExampleApp : IExternalApplication
{
// ExternalEventExample 是 IExternalEventHandler 的一个实现
public static ExternalEventExampleApp thisApp = null;
// 非模态对话框的实例
private ExternalEventExampleDialog m_MyForm;
public Result OnShutdown(UIControlledApplication application)
{
if (m_MyForm != null && m_MyForm.Visible)
{
m_MyForm.Close();
}
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
m_MyForm = null; // 会在用户触发 command 的时候被初始化
thisApp = this; // 记录 revit application instance
return Result.Succeeded;
}
// External command 会调用这个方法
public void ShowForm(UIApplication uiapp)
{
// 如果对话框还没有被创建,就创建一个
if (m_MyForm == null || m_MyForm.IsDisposed)
{
// 事件处理的类
ExternalEventExample handler = new ExternalEventExample();
// 用静态方法 `ExternalEvent.Create()` 创建一个 `ExternalEvent`
ExternalEvent exEvent = ExternalEvent.Create(handler);
// 把 handler 和 exEvent 都传给这个非模态对话框,最终非模态对话框通过它们来处理用户的请求
m_MyForm = new ExternalEventExampleDialog(exEvent, handler);
}
m_MyForm.Show();
}
}
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class Command : IExternalCommand
{
public virtual Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
try
{
ExternalEventExampleApp.thisApp.ShowForm(commandData.Application);
return Result.Succeeded;
}
catch (Exception ex)
{
message = ex.Message;
return Result.Failed;
}
}
}
通过 m_ExEvent.Raise()
,让 Revit 来处理用户的请求。
public partial class ExternalEventExampleDialog : Form
{
private ExternalEvent m_ExEvent;
private ExternalEventExample m_Handler;
public ExternalEventExampleDialog(ExternalEvent exEvent, ExternalEventExample handler)
{
InitializeComponent();
m_ExEvent = exEvent;
m_Handler = handler;
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
// we own both the event and the handler
// we should dispose it before we are closed
m_ExEvent.Dispose();
m_ExEvent = null;
m_Handler = null;
// do not forget to call the base class
base.OnFormClosed(e);
}
private void closeButton_Click(object sender, EventArgs e)
{
Close();
}
private void showMessageButton_Click(object sender, EventArgs e)
{
m_ExEvent.Raise();
}
}
最终效果如下:
对应的消息处理的类 RequestHandler
实现了 IExternalEventHandler
:
public void Execute(UIApplication uiapp)
{
try
{
switch (Request.Take())
{
case RequestId.None:
{
return; // no request at this time -> we can leave immediately
}
case RequestId.Delete:
{
ModifySelectedDoors(uiapp, "Delete doors", e => e.Document.Delete(e.Id));
break;
}
case RequestId.FlipLeftRight:
{
ModifySelectedDoors(uiapp, "Flip door Hand", e => e.flipHand());
break;
}
case RequestId.FlipInOut:
{
ModifySelectedDoors(uiapp, "Flip door Facing", e => e.flipFacing());
break;
}
case RequestId.MakeLeft:
{
ModifySelectedDoors(uiapp, "Make door Left", MakeLeft);
break;
}
case RequestId.MakeRight:
{
ModifySelectedDoors(uiapp, "Make door Right", MakeRight);
break;
}
case RequestId.TurnOut:
{
ModifySelectedDoors(uiapp, "Place door Out", TurnOut);
break;
}
case RequestId.TurnIn:
{
ModifySelectedDoors(uiapp, "Place door In", TurnIn);
break;
}
case RequestId.Rotate:
{
ModifySelectedDoors(uiapp, "Rotate door", FlipHandAndFace);
break;
}
default:
{
// some kind of a warning here should
// notify us about an unexpected request
break;
}
}
}
finally
{
Application.thisApp.WakeFormUp();
}
return;
}
每一个动作最终都用调用 ModifySelectedDoors
,这里用了策略设计模式,把各种类型的改变都包装成一个函数。ModifySelectedDoors
本身的逻辑很简单,就是把门过滤出来,然后对每个门调用改变的函数。
private void ModifySelectedDoors(UIApplication uiapp, String text, DoorOperation operation)
{
UIDocument uidoc = uiapp.ActiveUIDocument;
// 判断是否有活动的文档和选择集
if ((uidoc != null) && (uidoc.Selection != null))
{
ICollection<ElementId> selElements = uidoc.Selection.GetElementIds();
if (selElements.Count > 0)
{
// 从当前的选择集中过滤出所有的门
FilteredElementCollector collector = new FilteredElementCollector(uidoc.Document, selElements);
ICollection<Element> doorset = collector.OfCategory(BuiltInCategory.OST_Doors).ToElements();
if (doorset != null)
{
using (Transaction trans = new Transaction(uidoc.Document))
{
// The name of the transaction was given as an argument
if (trans.Start(text) == TransactionStatus.Started)
{
// 对每一个门进行修改
foreach (FamilyInstance door in doorset)
{
operation(door);
}
trans.Commit();
}
}
}
}
}
}
效果和 ModelessForm_ExternalEvent 一样,差别是处理函数不同。ModelessForm_IdlingEvent 是在 Revit 空闲的时候被调用。
具体逻辑:
通过 uiapp 来注册空闲事件:
public void ShowForm(UIApplication uiapp)
{
// If we do not have a dialog yet, create and show it
if (m_MyForm == null || m_MyForm.IsDisposed)
{
m_MyForm = new ModelessForm();
m_MyForm.Show();
// if we have a dialog, we need Idling too
uiapp.Idling += IdlingHandler;
}
}
public void IdlingHandler(object sender, IdlingEventArgs args)
{
UIApplication uiapp = sender as UIApplication;
if (m_MyForm.IsDisposed)
{
uiapp.Idling -= IdlingHandler;
return;
}
else // 对话框还打开着
{
// 从对话框查询用户的请求及内容
RequestId request = m_MyForm.Request.Take();
if (request != RequestId.None)
{
try
{
// 调用具体的处理函数,和ModelessForm_ExternalEvent的处理一致
RequestHandler.Execute(uiapp, request);
}
finally
{
// 激活无模态对话框
m_MyForm.WakeUp();
}
}
}
return;
}