我們在討論 Revit 2012 的拖放動作(drag and drop in Revit 2012) 時向大家展示瞭如何從外部進程觸發標準的Revit拖放動作。但是在那時我們無法改變 Revit 接收拖放文件的默認行爲。好消息是在 Revit 2013 裏我們可以這麼做了。
Revit 2013 SDK 的例程 UIAPI 向我們展示了最新的拖放API以及插件集成特性:應用程序可用性、預覽控件、定製WPF選項對話框。需要注意的是:
1. UIAPI 是一個 External Application,所以無法使用 RvtSamples 來加載
2. UIAPI 沒有在 Revit 2013 SDK 文檔中出現
UIAPI 拖放命令會顯示如下的無模式對話框
UIApplication DoDragDrop 方法
1. DoDragDrop ( ICollection<string> )
觸發一次包含多個文件的拖放操作
2. DoDragDrop ( object, IDropHandler )
觸發一次可定製Revit接收行爲的拖放操作
ApplicationEntryPoint 類也提供同樣的方法,但只是針對 Revit 宏。
拖放多個文件
DoDragDrop方法的第一個重載操作的對象是一組文件,並導致確定的Revit接收行爲(注意:至少在當前版本這些默認行爲是無法改變)。
- 當只有一種CAD格式或者圖片格式的文件被拖放時,Revit會彈出一個新的導入拜訪編輯器來處理文件導入
- 當多種CAD格式或者圖片格式的文件被拖放時,Revit僅會針對第一種格式彈出一個新的導入拜訪編輯器來處理文件導入
- 當只有一個族文件被拖放時,Revit會加載該族,並彈出一個編輯器來拜訪族實例
- 當多個族文件被拖放時,Revit會加載所有的族
- 當多個族文件加上其它格式的文件被拖放時,Revit會嘗試打開所有文件(注意:此時不會加載族了)
- 如果一個有效文件或者一組有效文件被拖放,Revit會嘗試合理地使用它們。如果有文件無法使用,Revit會彈出一個失敗消息,但是不會拋出異常,也不會通知插件
private void listBox1_MouseMove( object sender, MouseEventArgs e )
{
if( System.Windows.Forms.Control.MouseButtons == MouseButtons.Left )
{
FamilyListBoxMember member = (FamilyListBoxMember) listBox1.SelectedItem;
// 觸發標準的 Revit 拖放行爲
List<String> data = new List<String>();
data.Add( member.FullPath );
UIApplication.DoDragDrop( data );
}
}
從無模式對話框調用 Revit API 的缺點
值得注意的是,從無模式對話框調用 UIApplication.DoDragDrop 意味着在非 Revit API 上下文中調用 Revit API。絕大多數情況下這是非法的,可能導致無法預測的結果。所以通常我們應該從 Idling 事件或者 External 事件的回調函數中調用 Revit API。但是針對拖放操作,這種方式卻可能導致死鎖。
原因是 Revit 觸發的 Idling 事件和 External 事件都是假設沒有用戶界面操作的。如果在它們的事件回調中調用觸發UI操作,可能會導致如下情況:
1. UI請求被髮送到相同的消息隊列。但是由於消息隊列還在等待 Idling/External 事件的返回,所以這個新的UI請求將永遠無法被處理
2. UI請求被髮送到另外的消息隊列,這個不同的消息隊列最終會觸發一個新的 Idling/External 事件,然後導致用戶界面死鎖
所以結論令人失望,沒有安全的方式從無模式對話框中調用拖放操作。
定製Revit處理方式的拖放
DoDragDrap的第二個重載是個令人激動的新特性。它允許我們定義Revit接收拖放文件的行爲。我們需要做的就是實現一個 IDropHandler 接口,並且在它的 Execute() 方法中定義我們期望的行爲。下面這個例子裏,IDropHanlder 接收一個族類型的 ElementId,然後調用 PromptForFamilyInstancePlacement() 方法要求用戶拜訪它。顯然,你可以向 IDropHanlder 傳遞任何定製的數據,來實現你的需求。
public class LoadedFamilyDropHandler : IDropHandler
{
public void Execute( UIDocument doc, object data )
{
ElementId familySymbolId = (ElementId) data;
FamilySymbol symbol = doc.Document.GetElement( familySymbolId ) as FamilySymbol;
if( symbol != null )
{
doc.PromptForFamilyInstancePlacement( symbol );
}
}
}
private void listView_MouseMove( object sender, MouseEventArgs e )
{
if( System.Windows.Forms.Control.MouseButtons == MouseButtons.Left )
{
ListViewItem selectedItem = this.listView1.SelectedItems.Cast<ListViewItem>().FirstOrDefault<ListViewItem>();
if( selectedItem != null )
{
LoadedFamilyDropHandler myhandler = new LoadedFamilyDropHandler();
UIApplication.DoDragDrop( selectedItem.Tag, myhandler );
}
}
}