Autodesk官方最新的.NET教程(七)(C#版)
2024-07-10 13:00:09
供稿:網友
本文來源于網頁設計愛好者web開發社區http://www.html.org.cn收集整理,歡迎訪問。第7章 事件
本章將討論autocad中的事件。我們將介紹事件處理函數的使用,特別是監視autocad命令的事件處理函數和監視被autocad命令修改的對象的事件處理函數。在解釋怎樣在c#中實現autocad的事件處理之前,我們將首先簡要地討論一下.net中的事件。
第一部分 c#中的事件
事件只是用來通知一個行為已經發生的信息。在objectarx中,我們使用反應器(reactor)來處理autocad的事件。而在autocad .net api中,objectarx反應器被換成了事件。
事件處理函數(或者叫回調函數)是用來監視和反饋程序中出現的事件。事件可以以不同的形式出現。
在介紹autocad .net api中的事件之前,讓我們先來簡單地了解一下代理。
第1a部分 代理
代理是一個存儲方法索引的類(概念與函數指針類似)。代理對方法是類型安全的(與c中的函數指針類似)。代理有特定的形式和返回類型。代理可以封裝符合這種特定形式的任何方法。
代理的一個用途就是作為產生事件的類的分發器。事件是.net環境中第一級別的對象。雖然c#把事件處理的許多細節給隱藏掉了,但事件總是由代理來實現的。事件代理可以多次調用(就是它們可以存儲多于1個的事件處理方法的索引)。它們保存了用于事件的一個注冊事件處理的列表。一個典型的代理有以下的形式:
public delegate event (object sender, eventargs e)
第一個參數sender表示引發事件的對象。第二個參數e是一個eventargs參數(或者是一個派生的類),這個對象通常包含用于事件處理函數的數據。
第1b部分 +=和-=語句
要使用事件處理函數,我們必須把它與事件聯系起來。這要通過使用+=語句。+=和-=允許你在運行時連接、斷開或修改與事件聯系的處理函數。
當我們使用+=語句時,我們要確定事件引發者的名字,并要使用new語句來確定事件處理函數,例如:
myclass1.anevent += new handlerdelegate(ehandler)
前面我們說過要使用-=語句從事件處理函數中斷開事件(移除聯系)。語法如下所示:
myclass1.anevent -= new handlerdelegate(ehandler)
第2部分 處理.net中的autocad事件
在objectarx中,我們使用反應器來封裝autocad事件。在autocad .net api中,我們可以使用事件來代替objectarx反應器。
通常,處理autocad事件的步驟如下:
1. 創建事件處理函數
當一個事件發生時,事件處理函數(或稱為回調函數)被調用。任何我們想要處理的回應autocad事件的動作都在事件處理函數中進行。
例如,假定我們只想通知用戶一個autocad對象已被加入。我們可以使用autocad數據庫事件”objectappended”來完成。我們可以編寫回調函數(事件處理函數)如下:
public void objappended(object o, objecteventargs e)
{
// 在這里加入處理代碼
}
函數中的第一個參數代表autocad數據庫。第二個參數代表objecteventargs類,它可能包含對處理函數有用的數據。
2. 把事件處理函數與事件聯系起來
為了開始監視動作,我們必須把事件處理函數與事件聯系起來。在這里,當一個對象加入到數據庫時,objectappended事件將會發生。但是,事件處理函數不會響應這個事件,除非我們把它與這個事件聯系起來,例如:
database db;
db = hostapplicationservices.workingdatabase;
db. objectappended += new objecteventhandler(objappended);
3. 斷開事件處理函數
要終止監視一個動作,我們必須斷開事件處理函數與事件的聯系。當對象被加入時,我們想要停止通知用戶這個事件,我們要斷開事件處理函數與事件objectappended的聯系。
db. objectappended -= new objecteventhandler(objappended);
第3部分 使用事件處理函數來控制autocad的行為
本章的目的是解釋autocad事件怎樣才能被用于控制autocad圖形中的行為。現在,讓我們使用前一章(第六章)的內容在autocad圖形中創建幾個employee塊索引。我們不想讓用戶能改變employee塊索引的位置,而對于其它的非employee塊索引的位置則沒有這個限制。我們將混合使用數據庫與文檔事件來做到這一點。
首先,我們想要監視將要被執行的autocad命令(使用commandwillstart事件)。特別地,我們要監視move命令。另外,當一個對象要被修改時,我們應該被通知(使用objectopenedformodify事件),這樣我們可以確定它是否為一個employee塊索引。如果這時就修改對象可能是無效的,因為我們的修改可能會再次觸發事件,從而引起不穩定的行為。所以,我們要等待move命令的執行結束(使用commandended事件),這時就可以安全地修改對象了。當然,任何對塊索引的修改將會觸發objectopenedformodify事件。我們還需要設置一些全局變量來表明一個move命令在運行和被修改的對象是一個employee塊索引。
注意:因為本章需要比較多的代碼來獲得想要的結果,所以我們不會解釋任何與事件處理無關的代碼,而只是將它們粘貼到事件處理函數中。這里的重點是成功創建和注冊事件處理函數。
第一步:創建新工程
我們以第六章的工程開始。請新加入一個類asdkclass2。我們還要加入四個全局變量。前兩個是boolean型的:一個用來表示我們監視的命令是否是活動的,另外一個用來表示objectopenedformodify事件處理函數是否該被忽略。
//全局變量
bool beditcommand;
bool bdorepositioning;
接下來,我們要聲明一個全局變量來表示一個objectidcollection,它用來存儲我們所選擇的要修改的對象的objectid。
objectidcollection changedobjects = new objectidcollection();
最后,我們要聲明一個全局變量來表示一個point3dcollection,它用來包含我們所選對象的位置(三維點)。
point3dcollection employeepositions = new point3dcollection();
第2步:創建第一個文檔事件處理函數(回調函數)
現在我們要創建一個事件處理函數。當autocad命令開始執行的時候它會通知我們。我們要檢查globalcommandname的值是否為move。
if ( e.globalcommandname == "move" )
{
}
如果move命令開始執行的話,我們要相應地設置boolean變量beditcommand的值,這樣我們可以知道我們所監視的命令是活動的。同樣地,我們應該把另外一個boolean變量bdorepositioning設置為false來忽略objectopenedformodify事件處理函數。兩個變量設置好以后,在命令活動期間,我們必須要獲得所選塊索引的信息。
我們還應該把兩個集合對象的內容清空。我們只關心當前選擇的對象。
第3步: 創建數據庫事件處理函數(回調函數)
無論什么時候一個對象被打開并要被修改時,數據庫事件處理函數會被調用。當然,如果這時我們監視的命令不是活動的,我們就應該跳過任何被這個回調函數調用的內容。
if ( beditcommand == false )
{
return;
}
同樣地,如果我們監視的命令已經結束,而objectopenedformodify事件被另一個回調函數再次觸發的話,而這時有對象被修改時,我們要阻止所有由這個回調函數執行的動作。
if ( bdorepositioning == true )
{
return;
}
這個回調函數剩余部分的代碼用來驗證我們是否正在處理employee塊索引。如果是的話,我們就獲取它的objectid和位置(三維點)。下面的代碼可以被粘貼到這個事件處理函數函數。
public void objopenedformod(object o, objecteventargs e)
{
if ( beditcommand == false )
{
return;
}
if ( bdorepositioning == true )
{
return;
}
objectid objid;
objid = e.dbobject.objectid;
transaction trans;
database db;
db = hostapplicationservices.workingdatabase;
trans = db.transactionmanager.starttransaction();
using(entity ent = (entity)trans.getobject(objid, openmode.forread, false))
{
if ( ent.gettype().fullname.equals( "autodesk.autocad.databaseservices.blockreference" ) )
{ //we use .net//s rtti to establish type.
blockreference br = (blockreference)ent;
//test whether it is an employee block
//open its extension dictionary
if ( br.extensiondictionary.isvalid )
{
using(dbdictionary brextdict = (dbdictionary)trans.getobject(br.extensiondictionary, openmode.forread))
{
if ( brextdict.getat("employeedata").isvalid )
{
//successfully got "employeedata" so br is employee block ref
//store the objectid and the position
changedobjects.add(objid);
employeepositions.add(br.position);
//get the attribute references,if any
attributecollection atts;
atts = br.attributecollection;
if ( atts.count > 0 )
{
foreach(objectid attid in atts )
{
attributereference att;
using(att = (attributereference)trans.getobject(attid, openmode.forread, false))
{
changedobjects.add(attid);
employeepositions.add(att.position);
}
}
}
}
}
}
}
}
trans.commit();
}
第4步 創建第二個文檔事件處理函數(回調函數)
當一個命令結束時,第三個事件處理函數被調用。同樣地,我們要檢查全局變量來驗證這個將要結束的命令是我們監視的命令。如果是我們監視的,那么我們要重置這個變量:
if ( beditcommand == false )
{
return;
}
beditcommand = false;
這個回調函數執行的動作將會再次觸發objectopenedformodify事件。我們必須確定在這個回調函數中跳過了所有與此事件有關的動作。
//設置標志來跳過openedformodify處理函數
bdorepositioning = true;
這個回調函數的剩余代碼用來把employee塊索引和它的關聯屬性引用的當前(修改過的)位置與它們的初始位置作比較。如果位置改變了,我們在這個回調函數中把它們重置這初始的位置。下面的代碼可以被粘貼到這個事件處理函數中。
public void cmdended(object o , commandeventargs e)
{
//was our monitored command active?
if ( beditcommand == false )
{
return;
}
beditcommand = false;
//set flag to bypass openedformodify handler
bdorepositioning = true;
database db = hostapplicationservices.workingdatabase;
transaction trans ;
blocktable bt;
point3d oldpos;
point3d newpos;
int i ;
for ( i = 0; i< changedobjects.count; i++)
{
trans = db.transactionmanager.starttransaction();
using( bt = (blocktable)trans.getobject(db.blocktableid, openmode.forread) )
{
using(entity ent = (entity)trans.getobject(changedobjects[i], openmode.forwrite))
{
if ( ent.gettype().fullname.equals("autodesk.autocad.databaseservices.blockreference") )
{ //we use .net//s rtti to establish type.
blockreference br = (blockreference)ent;
newpos = br.position;
oldpos = employeepositions[i];
//reset blockref position
if ( !oldpos.equals(newpos) )
{
using( trans.getobject(br.objectid, openmode.forwrite) )
{
br.position = oldpos;
}
}
}
else if ( ent.gettype().fullname.equals("autodesk.autocad.databaseservices.attributereference") )
{
attributereference att = (attributereference)ent;
newpos = att.position;
oldpos = employeepositions[i];
//reset attref position
if ( !oldpos.equals(newpos) )
{
using( trans.getobject(att.objectid, openmode.forwrite))
{
att.position = oldpos;
}
}
}
}
}
trans.commit();
}
}
第5步 創建命令來注冊/斷開事件處理函數
創建一個addevents命令,使用+=語句來把上面的3個事件處理函數連接到各自的事件。在這個命令中,我們還應該設置全局boolean變量:
beditcommand = false;
bdorepositioning = false;
創建另外一個命令removeevents,使用-=語句把事件處理函數與事件斷開。
第6步: 測試工程
要測試這個工程,請使用create命令創建一個或多個employee塊索引。如果你要作比較的話,你也可以插入一些非employee的塊索引。
在命令行中鍵入addevents命令來執行它。
在命令行中輸入move命令,然后選擇你想要的塊索引。注意,當move命令結束時,employee塊索引(包括屬性)還留在原處。
執行removeevents命令,然后在試一下move命令。注意,employee塊索引現在可以被移動了。
附加的問題:添加一個附加的回調函數,當用戶改變employee塊索引的”name”屬性時,這個回調函數被觸發。