在.NET中进行AutoCAD二次开发(C#+ObjectArx) (二)
?
第 5 章 用戶互操作:提示和選擇
背景
提示通常包含一個(gè)描述性信息,伴隨一個(gè)停止以讓用戶理解所給的信息并輸入數(shù)據(jù)。數(shù)據(jù)可以通過多種方式被輸入,如通過命令行、對(duì)話框或AutoCAD編輯窗口。給出的提示要遵循一定的格式,格式要與一般的AutoCAD提示相一致,這一點(diǎn)是非常重要的。例如,關(guān)鍵字要用“/”號(hào)分隔并放在方括號(hào)“[]”中,缺省值要放在“<>”內(nèi)。對(duì)于一個(gè)AutoCAD用戶來說,堅(jiān)持統(tǒng)一的格式將會(huì)減少信息理解錯(cuò)誤的產(chǎn)生。
當(dāng)用戶在AutoCAD命令行中選擇一個(gè)實(shí)體時(shí),實(shí)體是使用選擇機(jī)制被選擇的。這種機(jī)制包括一個(gè)提示,用來讓用戶知道選擇什么并怎樣選擇(如,窗口或單一實(shí)體),然后是一個(gè)停頓。
試一下諸如PINE這種命令來看一下提示的顯示,PEDIT來看一下使用單一實(shí)體或多線來進(jìn)行選擇。
練習(xí)
Prompts:
提示:
在本章中,我們將提示輸入雇員名字、職位、薪水和部門來創(chuàng)建一個(gè)雇員塊索引對(duì)象。如果輸入的部門不存在,我們將提示輸入部門經(jīng)理的名字來創(chuàng)建一個(gè)新的部門。在我們繼續(xù)之前,讓我們?cè)囍赜靡郧暗拇a。
為了進(jìn)行選擇,我們將提示用戶在一個(gè)窗口中進(jìn)行選擇或選擇一個(gè)實(shí)體,而我們只顯示選擇集中的雇員對(duì)象。
在前面的章節(jié)中,我們創(chuàng)建了一個(gè)名叫“Earnest Shackleton”的雇員,名字被存儲(chǔ)為“EmployeeBlock”塊定義(塊表記錄)中的MText。如果我們多次插入這個(gè)塊,那么我們看到的都是同一個(gè)雇員的名字。我們?cè)鯓硬拍茏远x這個(gè)塊以使每次插入這個(gè)塊的時(shí)候顯示不同雇員的名字?這就要使用塊屬性的功能了。屬性是存儲(chǔ)在每一個(gè)塊索引實(shí)例中的文本,并被作為實(shí)例的一部分來被顯示。屬性從存儲(chǔ)在塊表記錄中的屬性定義中繼承相關(guān)的屬性。
屬性:
讓我們來把MText實(shí)體類型改變?yōu)閷傩远x。在CreateEmployeeDefinition()函數(shù)中,把下面的代碼替換
?
//文本:
MText text = new MText();
text.Contents = "Earnest Shackleton";
text.Location = center;
?
為
?
//屬性定義
AttributeDefinition text = new AttributeDefinition(center, "NoName", "Name:", "Enter Name", db.Textstyle);
text.ColorIndex = 2;
?
試著使用TEST命令來測(cè)試一下CreateEmployeeDefinition()函數(shù):
?????? [CommandMethod("Test")]
?????? public? void Test()
?????? {
?????? ?????? CreateEmployeeDefinition();
?????? }
?
你現(xiàn)在應(yīng)該可以使用INSERT命令來插入EmployeeBlock塊并對(duì)每一個(gè)實(shí)例確定一個(gè)雇員名。
當(dāng)你插入Employee塊時(shí),請(qǐng)注意一下塊插入的位置。它是正好被放置在所選點(diǎn)還是有些偏移?試試怎樣修復(fù)它。(提示:檢查塊定義中的圓心)
修改CreateEmployee ()以重用
?
1)讓我們來修改CreateEmployee()函數(shù),以讓它可以接收名字、薪水、部門和職位并返回創(chuàng)建的雇員塊索引的ObjectId。函數(shù)的形式如下(你可以改變參數(shù)順序)
public ObjectId CreateEmployee(string name, string division, double salary, Point3d pos)
?
2) 移除上面函數(shù)中的CommandMethod屬性”CREATE”,這樣它就不再是用來創(chuàng)建雇員的命令。
3)????????????????? 修改函數(shù)的代碼,這樣就可以正確地設(shè)置塊索引的名字、職位、部門和薪水和它的擴(kuò)展字典。
·??????? 替換
?
BlockReference br = new BlockReference(new Point3d(10, 10, 0), CreateEmployeeDefinition());
?
為
?
BlockReference br = new BlockReference(pos, CreateEmployeeDefinition());
?
·??????? 替換
?
????? xRec.Data = new ResultBuffer(
????? ???? new TypedValue((int)DxfCode.Text, "Earnest Shackleton"),
?????? new TypedValue((int)DxfCode.Real, 72000),
?????? new TypedValue((int)DxfCode.Text, "Sales"));
?
為
?
??? xRec.Data = new ResultBuffer(
?????? new TypedValue((int)DxfCode.Text, name),
?????? new TypedValue((int)DxfCode.Real, salary),
?????? new TypedValue((int)DxfCode.Text, division));
?
4)????????????????? 因?yàn)槲覀儼压蛦T的名字從MText替換成塊的屬性定義,因此我們要?jiǎng)?chuàng)建一個(gè)相應(yīng)的屬性索引來顯示雇員的名字。屬性索引將使用屬性定義的屬性。
·???????? 替換:
?
?????? btr.AppendEntity(br); //加入索引到模型空間
?????? trans.AddNewlyCreatedDBObject(br, true); //讓事務(wù)處理知道
?
為
?
?????? AttributeReference attRef = new AttributeReference();
?????? //遍歷雇員塊來查找屬性定義
?????? BlockTableRecord empBtr = (BlockTableRecord)trans.GetObject(bt["EmployeeBlock"], OpenMode.ForRead);
?????? foreach (ObjectId id in empBtr)
?????? {
????????????? Entity ent = (Entity)trans.GetObject(id, OpenMode.ForRead, false);
????????????? //打開當(dāng)前的對(duì)象!
????????????? if (ent is AttributeDefinition)?
????????????? {
???????????????????? //設(shè)置屬性為屬性索引中的屬性定義
???????????????????? AttributeDefinition attDef = ((AttributeDefinition)(ent));
???????????????????? attRef.SetPropertiesFrom(attDef);
???????????????????? attRef.Position = new Point3d(attDef.Position.X + br.Position.X, attDef.Position.Y + br.Position.Y, attDef.Position.Z + br.Position.Z);
???????????????????? attRef.Height = attDef.Height;
???????????????????? attRef.Rotation = attDef.Rotation;
???????????????????? attRef.Tag = attDef.Tag;
???????????????????? attRef.TextString = name;
????????????? }
?????? }
?????? //把索引加入模型空間
?????? btr.AppendEntity(br);
?????? //把屬性索引加入到塊索引
?????? br.AttributeCollection.AppendAttribute(attRef);
?????? //讓事務(wù)處理知道
?????? trans.AddNewlyCreatedDBObject(attRef, true);
?????? trans.AddNewlyCreatedDBObject(br, true);
?
?
研究一下上面的代碼,看看是怎樣把屬性定義中除顯示用的文本字符串外的屬性復(fù)制到屬性索引的。屬性被加入到塊索引的屬性集合中。這就是你怎樣來為每一個(gè)實(shí)例自定義雇員名字。
5)不要忘記返回雇員塊索引的ObjectId,但要在提交事務(wù)處理之后才能返回:
trans.Commit();
return br.ObjectId;
?
6)?????? 測(cè)試CreateEmployee。
加入一個(gè)Test命令來測(cè)試CreateEmployee:
?
?????? [CommandMethod("Test")]
?????? public? void Test()
?????? {
????????????? CreateEmployee("Earnest Shackleton", "Sales", 10000, new Point3d(10, 10, 0));
?????? }
?
?
修改CreateDivision()以重用:
讓我們來修改CreateDivision ()函數(shù),以讓它可以接收部門名字、經(jīng)理名字并返回創(chuàng)建的部門經(jīng)理擴(kuò)展記錄的ObjectId。如果部門經(jīng)理已經(jīng)存在,則不改變經(jīng)理的名字。
1)????????????????? 如果你先前在CreateEmployeeDefinition()中調(diào)用了CreateDivision(),請(qǐng)把它注釋掉,因?yàn)槲覀冊(cè)谶@里不需要?jiǎng)?chuàng)建一個(gè)部門
2)???? 改變CreateDivision()的形式讓它接收部門和經(jīng)理的名字并返回一個(gè)ObjectId。
public ObjectId CreateDivision(string division, string manager)
3)???? 修改上面函數(shù)的代碼創(chuàng)建部門的名字和經(jīng)理:
·??????? 替換:
?
divDict = (DBDictionary)trans.GetObject(acmeDict.GetAt("Sales"), OpenMode.ForWrite);
?
為:
?
divDict = (DBDictionary)trans.GetObject(acmeDict.GetAt(division), OpenMode.ForWrite);
?
·???????? 替換:
?
acmeDict.SetAt("Sales", divDict);
?
為:
?
acmeDict.SetAt(division, divDict);
?
·???????? 替換:
?
mgrXRec.Data = new ResultBuffer(new TypedValue((int)DxfCode.Text, "Randolph P. Brokwell"));
?
為:
?
mgrXRec.Data = new ResultBuffer(new TypedValue((int)DxfCode.Text, manager));
?
?
不要忘了返回部門經(jīng)理這個(gè)擴(kuò)展記錄的ObjectId,但要在提交事務(wù)處理后才返回。
trans.Commit();
//返回部門經(jīng)理這個(gè)擴(kuò)展記錄的ObjectId
return mgrXRec.ObjectId;
?
現(xiàn)在把在中CreateEmployeeDefinition調(diào)用的CreateDivision函數(shù)給注釋掉。
4)??? 現(xiàn)在通過使用TEST命令來測(cè)試調(diào)用CreateDivision函數(shù)。使用ArxDbg工具來檢查條目是否已被加入到“ACME_DIVISION”下的命名對(duì)象字典。
CreateDivision("Sales", "Randolph P. Brokwell")
?
使用CREATE命令來創(chuàng)建雇員:
?
我們將加入一個(gè)名為CREATE的新命令,此命令用來提示輸入雇員的詳細(xì)資料來創(chuàng)建雇員塊索引。讓我們來看一下這個(gè)命令是怎樣使用的。
1)????? 讓我們加入一個(gè)名為CREATE的新命令,并聲明幾個(gè)常用的變量和一個(gè)try-finally塊。
?
[CommandMethod("CREATE")]
public void CreateEmployee()
{
?????? Database db = HostApplicationServices.WorkingDatabase;
?????? Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
?????? Transaction trans = db.TransactionManager.StartTransaction();
?????? try
?????? {
????????????? trans.Commit();
?????? }
?????? finally
?????? {
????????????? trans.Dispose();
?????? }
}
?
?
2)????? 讓我們來為雇員定義可以用作為提示缺省值的常數(shù)。注意,布爾值gotPosition是用來判斷用戶是否已輸入職位。
. 雇員名 ????????????? - 類型 :String? ??????? -缺省值 “Earnest Shackleton”
. 雇員所在部門名 - 類型:String ??????????? -缺省值“Sales”
. 薪水?????????????????? -類型:Double (non-negative and not zero)???? -缺省值10000
. 職位?????????????????? -類型:Point3d ?????????? -缺省值(0,0,0)
?
把這些常數(shù)加入到try語句后面:
string empName =? "Earnest Shackleton";
string divName =? "Sales";
double salary = new double();
salary = 10000;
Point3d position = new Point3d(0, 0, 0);
bool gotPosition = new bool();
//布爾值用來判斷用戶是否已輸入職位
gotPosition = false;
?
3)????? 現(xiàn)在讓我們提示用戶輸入值。我們先使用PromptXXXOptions類來初始化要顯示的提示字符串。
//提示輸入每個(gè)雇員的詳細(xì)資料
PromptStringOptions prName = new PromptStringOptions("Enter Employee Name <" + empName + ">");
PromptStringOptions prDiv = new PromptStringOptions("Enter Employee Division <" + divName + ">");
PromptDoubleOptions prSal = new PromptDoubleOptions("Enter Employee Salary <" + salary + ">");
PromptPointOptions prPos = new PromptPointOptions("Enter Employee Position or");
?
注意,提示字符串用尖括號(hào)來顯示變量的值。這是AutoCAD用來提示用戶這個(gè)值為缺省值。
4)? 當(dāng)提示用戶輸入職位時(shí),我們也提供了一個(gè)關(guān)鍵字列表選項(xiàng),如名字、部門和薪水。如果用戶想要在選擇一個(gè)點(diǎn)的時(shí)候改變?yōu)槠渌?#xff0c;他可以選擇那個(gè)關(guān)鍵字。
一個(gè)命令提示的例子如下:
Command: CREATE
Enter Employee Position or [Name/Division/Salary]:
?
要?jiǎng)?chuàng)建一個(gè)雇員,用戶會(huì)選擇一個(gè)點(diǎn)而其它的值被設(shè)置為缺省值。如果用戶要改變其它的值,如名字,他可以輸入”N”或全名”Name”,然后輸入名字:
Command: CREATE
Enter Employee Position or [Name/Division/Salary]:N
Enter Employee Name <Earnest Shackleton>:
?
如果用戶想要再次選擇缺省的名字,他可以按回車鍵。
讓我們創(chuàng)建用于職位提示的關(guān)鍵字列表:
?
//加入用于職位提示的關(guān)鍵字
prPos.Keywords.Add("Name");
prPos.Keywords.Add("Division");
prPos.Keywords.Add("Salary");
//設(shè)置提示的限制條件
prPos.AllowNone = false; //不允許沒有值
?
?
5)????? 現(xiàn)在讓我們聲明PromptXXXResult變量來獲取提示的結(jié)果:
//prompt results
PromptResult prNameRes;
PromptResult prDivRes;
PromptDoubleResult prSalRes;
PromptPointResult prPosRes;
?
6)????? 直到用戶成功輸入一個(gè)點(diǎn)后,循環(huán)才結(jié)束。如果輸入錯(cuò)誤的話,我們會(huì)提示用戶并退出函數(shù):
判斷用戶是否輸入了關(guān)鍵字,我們通過檢查promptresult的狀態(tài)來進(jìn)行:
//循環(huán)用來獲取雇員的詳細(xì)資料。當(dāng)職位被輸入后,循環(huán)終止。
while (!gotPosition)
{
?????? //提示輸入職位
?????? prPosRes = ed.GetPoint(prPos);
?????? //取得一個(gè)點(diǎn)
?????? if (prPosRes.Status == PromptStatus.OK)
?????? {
?????? ?????? gotPosition = true;
????????????? position = prPosRes.Value;
?????? }
?????? else if (prPosRes.Status == PromptStatus.Keyword) //獲取一個(gè)關(guān)鍵字
?????? {
????????????? //輸入了Name關(guān)鍵字
????????????? if (prPosRes.StringResult == "Name")
????????????? {
????????????? ?????? //獲取雇員名字
???????????????????? prName.AllowSpaces = true;
???????????????????? prNameRes = ed.GetString(prName);
???????????????????? if (prNameRes.Status != PromptStatus.OK)
???????????????????? {
???????????????????? ?????? return;
???????????????????? }
???????????????????? //如果獲取雇員名字成功
???????????????????? if (prNameRes.StringResult != "")
???????????????????? {
???????????????????? ?????? empName = prNameRes.StringResult;
???????????????????? }
????????????? }
?????? }
?????? else
?????? {
????????????? // 獲取職位時(shí)發(fā)生錯(cuò)誤
????????????? ed.WriteMessage("***Error in getting a point, exiting!!***" + "/r/n");
????????????? return;
?????? }? // 如果獲取一個(gè)點(diǎn)
?
}
?
7)????? 上面的代碼只提示輸入名字,請(qǐng)加入提示輸入薪水和部門的代碼。
8)????? 完成提示輸入后,我們將使用獲得的值來創(chuàng)建雇員。
//創(chuàng)建雇員
CreateEmployee(empName, divName, salary, position);
?
9)????? 現(xiàn)在來檢查部門經(jīng)理是否已存在。我們通過檢查NOD中部門的擴(kuò)展記錄中的經(jīng)理名字來進(jìn)行。如果檢查到的是一個(gè)空字符串,那么我們會(huì)提示用戶輸入經(jīng)理的名字。注意,通過修改CreateDivision()函數(shù),獲取經(jīng)理的名字變得簡(jiǎn)單了。
string manager = "";
//創(chuàng)建部門
//給經(jīng)理傳入一個(gè)空字符串來檢查它是否已存在
Xrecord depMgrXRec;
ObjectId xRecId;
xRecId = CreateDivision(divName, manager);
//打開部門經(jīng)理擴(kuò)展記錄
depMgrXRec = (Xrecord)trans.GetObject(xRecId, OpenMode.ForRead);
TypedValue[] typedVal = depMgrXRec.Data.AsArray();
foreach (TypedValue val in typedVal)
{
?????? string str;
?????? str = (string)val.Value;
?????? if (str == "")
?????? {
????????????? //經(jīng)理沒有被設(shè)置,現(xiàn)在設(shè)置它
????????????? // 先提示輸入經(jīng)理的名字
????????????? ed.WriteMessage("/r/n");
????????????? PromptStringOptions prManagerName = new PromptStringOptions("No manager set for the division! Enter Manager Name");
????????????? prManagerName.AllowSpaces = true;
????????????? PromptResult prManagerNameRes = ed.GetString(prManagerName);
????????????? if (prManagerNameRes.Status != PromptStatus.OK)
????????????? {
???????????????????? return;
?????? ?????????? }
????????????? //設(shè)置經(jīng)理的名字
????????????? depMgrXRec.Data = new ResultBuffer(new TypedValue((int)DxfCode.Text, prManagerNameRes.StringResult));
?????? }
}
?
?
?
10)? 測(cè)試CREATE命令
?
選擇集:
現(xiàn)在讓我們來創(chuàng)建一個(gè)命令,當(dāng)用戶在圖形中選擇一個(gè)雇員對(duì)象時(shí),它會(huì)顯示雇員的詳細(xì)資料。
我們會(huì)使用上一章中創(chuàng)建的ListEmployee()函數(shù)在命令行中輸出雇員的詳細(xì)資料。
下面是你必須遵循的步驟:
1.? 調(diào)用“LISTEMPLOYEES”命令
2.? 調(diào)用Editor的GetSelection()函數(shù)來選擇實(shí)體
?????? PromptSelectionResult res = ed.GetSelection(Opts, filter);
?
3.? 上面的filter用來過濾選擇集中的塊索引。你可以創(chuàng)建如下的過濾列表:
TypedValue[] filList = new TypedValue[1];
filList[0] = new TypedValue((int)DxfCode.Start, "INSERT");
SelectionFilter filter = new SelectionFilter(filList);
?
4.? 從選擇集中獲取ObjectId數(shù)組:
?
?????? //如果選擇失敗則什么也不做
?????? if (res.Status != PromptStatus.OK)
?????? ?????? return;
?????? Autodesk.AutoCAD.EditorInput.SelectionSet SS = res.Value;
?????? ObjectId[] idArray;
?????? idArray = SS.GetObjectIds();
?
5. 最后,把選擇集中的每個(gè)ObjectId輸入到ListEmployee()函數(shù)來獲取一個(gè)雇員詳細(xì)資料的字符串?dāng)?shù)組。把雇員的詳細(xì)資料輸出到命令行。例如:
?
//獲取saEmployeeList 數(shù)組中的所有雇員
foreach (ObjectId employeeId in idArray)
{
?????? ListEmployee(employeeId, ref saEmployeeList);
?????? //把雇員的詳細(xì)資料輸出到命令行
?????? foreach (string employeeDetail in saEmployeeList)
?????? {
?????? ?????? ed.WriteMessage(employeeDetail);
?????? }
??????
?????? ed.WriteMessage("----------------------" + "/r/n");
}
?
Autodesk官方最新的.NET教程(六)(C#版)
?
第6章 更多的用戶界面:添加自定義數(shù)據(jù)
在本章中,我們將介紹.NET API的用戶界面部分能做些什么。我們首先將介紹一個(gè)自定義上下文菜單(快捷菜單)。接下來我們將實(shí)現(xiàn)一個(gè)無模式可停靠的面板(一個(gè)真正的AutoCAD增強(qiáng)輔助窗口)來支持拖放操作。接著我們將介紹通過模式窗體選取實(shí)體。最后,我們將介紹使用AutoCAD的選項(xiàng)對(duì)話框來設(shè)置雇員的缺省值。
本章還會(huì)介紹和上面內(nèi)容有關(guān)的API。
?
第一部分 自定義上下文菜單
?
到目前為止,我們所寫的代碼只與CommandMethod屬性定義的命令行進(jìn)行相互操作。一個(gè)AutoCAD .NET程序能通過一個(gè)特殊的類來實(shí)現(xiàn)裝載時(shí)的初始化工作。這個(gè)類只要實(shí)現(xiàn)IExtensionApplication .NET接口并暴露一個(gè)組件級(jí)別的屬性(此屬性把類定義為ExtensionApplication),就可以響應(yīng)一次性的裝載和卸載事件。例子:
[assembly: ExtensionApplication(typeof(Lab6_CS.AsdkClass1))]
?
?? public classAsdkClass1 : IExtensionApplication
?? {
?
1) 現(xiàn)在修改AsdkClass1類來實(shí)現(xiàn)上面的接口。要實(shí)現(xiàn)這個(gè)接口,你必須實(shí)現(xiàn)Initialize() 和Terminate()函數(shù)。因?yàn)槲覀円獙?shí)現(xiàn)的是一個(gè)接口,而接口中的函數(shù)總是定義為純虛擬的。
?
????? public voidInitialize()
????? {
???????? AddContextMenu();
???????? EmployeeOptions.AddTabDialog();
????? }?
?????
????? public voidTerminate()
????? {
????? }
?
為了加入自定義上下文菜單,我們必須定義一個(gè)‘ContextMenuExtension’類的成員。這個(gè)類位于Autodesk.AutoCAD.Windows命名空間中。
要使用ContextMenuExtension,我們必須使用new關(guān)鍵字來進(jìn)行初始化,給必要的屬性賦值,并調(diào)用Application.AddDefaultContextMenuExtension()。上下文菜單的工作方式是:對(duì)于每個(gè)菜單條目,我們定義一個(gè)成員函數(shù)來處理菜單單擊事件。我們可能通過.NET的代理來實(shí)現(xiàn)。我們使用C#關(guān)鍵字+=和-=確定讓哪個(gè)函數(shù)來處理事件。請(qǐng)盡快熟悉這種設(shè)計(jì)模式,因?yàn)樵贑#中會(huì)使用很多。
2) 添加一個(gè)‘ContextMenuExtension’成員變量和下面兩個(gè)用來添加和移除自定義菜單的函數(shù)。請(qǐng)好好研究一下代碼來看看究竟發(fā)生了什么。
????? void AddContextMenu()
????? {
???????? try
???????? {
??????????? m_ContextMenu= new ContextMenuExtension();
??????????? m_ContextMenu.Title= "Acme Employee Menu";
??????????? Autodesk.AutoCAD.Windows.MenuItemmi;
??????????? mi= newAutodesk.AutoCAD.Windows.MenuItem("Create Employee");
??????????? mi.Click+=? newEventHandler(CallbackOnClick);
??????????? m_ContextMenu.MenuItems.Add(mi);
??????????
??????????? Autodesk.AutoCAD.ApplicationServices.Application.AddDefaultContextMenuExtension(m_ContextMenu);
???????? }
???????? catch
???????? {
???????? }
????? }
?
????? void RemoveContextMenu()
????? {
???????? try
???????? {
??????????? if( m_ContextMenu != null)
??????????? {
?????????????? Autodesk.AutoCAD.ApplicationServices.Application.RemoveDefaultContextMenuExtension(m_ContextMenu);
??????????????? m_ContextMenu= null;
??????????? }
???????? }
???????? catch
???????? {
???????? }
????? }
?
注意我們?cè)诖a中使用了‘CallbackOnClick’函數(shù)。我們希望這個(gè)函數(shù)(我們現(xiàn)在還沒有定義)響應(yīng)菜單項(xiàng)選擇事件。在我們的例子中,我們想要做的是調(diào)用我們的成員函數(shù)‘Create()’。請(qǐng)加入下面的代碼。
????? void CallbackOnClick(objectSender, EventArgs e)
????? {
???????? Create();
????? }
?
現(xiàn)在,從Initialize()中調(diào)用AddContextMenu()函數(shù),同樣地,請(qǐng)?jiān)赥erminate()中調(diào)用RemoveContextMenu()。
請(qǐng)運(yùn)行代碼。使用NETLOAD來裝載編譯好的組件,然后在AutoCAD的空白處右擊……你應(yīng)該可以看到’Acme‘快捷菜單了。如果失敗了,明明你做的都是正確的……為什么呢?
通常,AutoCAD的數(shù)據(jù)是存儲(chǔ)在文檔中的,而訪問實(shí)體的命令有權(quán)修改文檔。當(dāng)我們運(yùn)行代碼來響應(yīng)上下文菜單單擊事件,我們是從命令結(jié)構(gòu)的外部來訪問文檔。當(dāng)我們調(diào)用的代碼嘗試通過添加一個(gè)雇員來修改文檔時(shí),我們就碰到了錯(cuò)誤。正確的做法是必須鎖住文檔,這可以通過使用Document.LockDocument()命令來實(shí)現(xiàn)。
3) 修改CallbackOnClick來鎖住文檔:
????? void CallbackOnClick(objectSender, EventArgs e)
????? {
???????? DocumentLockdocLock =Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument();
???????? Create();
???????? docLock.Dispose();
????? }
?
注意,我們保留了一個(gè)‘DocumentLock’對(duì)象的拷貝。要把文檔解鎖,我們只要銷毀這個(gè)DocumentLock對(duì)象。
再次運(yùn)行代碼。現(xiàn)在應(yīng)該可以看到快捷菜單了。
?
第2部分 無模式對(duì)話框、可進(jìn)行拖放的可停靠面板
為了使我們的用戶界面和AutoCAD實(shí)現(xiàn)無縫鏈接,我們要盡可能在所有的地方使用同樣的用戶界面結(jié)構(gòu)。這會(huì)使應(yīng)用程序看起來與AutoCAD結(jié)合的很好,并有效地減少代碼的重復(fù)。一個(gè)很好的例子是AutoCAD中的可停靠面板。
使用.NET API,我們可以創(chuàng)建一個(gè)簡(jiǎn)單的窗體并把它放到面板中。我們可以實(shí)例化一個(gè)自定義的‘PaletteSet’對(duì)象來包含窗體,并可以把這個(gè)PaletteSet定義成我們喜歡的樣式。
4) 在解決方案瀏覽器中通過右擊工程來添加一個(gè)用戶控件。給它命名為ModelessForm。使用控件工具箱,加入如下所示的編輯框和標(biāo)簽控件。
?
使用屬性窗口設(shè)置三個(gè)編輯框的屬性。設(shè)置如下:
<首先是最上面的編輯框>
(Name)??? = tb_Name
Text = <請(qǐng)輸入一個(gè)名字>
?
<第二個(gè)編輯框>
(Name) = tb_Division
Text = Sales
?
<第三個(gè)編輯框>
(Name) = tb_Salary
Text = <請(qǐng)輸入薪水>
?
?
要使用.NET API實(shí)例化一個(gè)面板對(duì)象,你必須要實(shí)例化用戶控件對(duì)象(無模式窗體)和‘PaletteSet’對(duì)象。調(diào)用PaletteSet的成員函數(shù)Add來傳遞用戶控件對(duì)象。
5) 接下來,我們要加入一個(gè)命令來創(chuàng)建面板。在類中加入一個(gè)名為CreatePalette的函數(shù)和CommandMethod屬性來定義名為“PALETTE”的命令。
請(qǐng)看一下下面的代碼塊。這是實(shí)例化面板的代碼。
ps = new Autodesk.AutoCAD.Windows.PaletteSet("TestPalette Set");
ps.MinimumSize = newSystem.Drawing.Size(300, 300);
System.Windows.Forms.UserControl myCtrl = new ModelessForm();
ps.Add("test", myCtrl);
ps.Visible = true;
?
6) 把上面的代碼加入到CreatePalette()函數(shù)。‘ps’需要在函數(shù)的外部聲明:
????? private Autodesk.AutoCAD.Windows.PaletteSet ps;
?
在函數(shù)的實(shí)例化面板代碼之前加入檢查ps是否為null的代碼。
編譯并運(yùn)行工程。在AutoCAD中裝載組件,運(yùn)行‘PALETTE’命令來檢查面板是否被裝載。
使用PaletteSet.Style來看看PaletteSetStyles對(duì)象。例如:
ps.Style = PaletteSetStyles.ShowTabForSingle;
我們也可以試試諸如透明性的屬性,例如:
?? ps.Opacity =90;
?
注意:要使用PaletteSet 和PaletteSetStyles對(duì)象,你必須加入兩個(gè)命名空間Autodesk.AutoCAD.Windows和Autodesk.AutoCAD.Windows.Palette
?
在我們繼續(xù)之前,讓我們執(zhí)行一個(gè)快速的維護(hù)更新:請(qǐng)?jiān)贏sdkClass1類中加入下列成員:
????? public static string sDivisionDefault = "Sales";
????? public static string sDivisionManager = "Fiona Q.Farnsby";
?
這些值將被用作為部門和部門經(jīng)理的缺省值。由于它們被聲明為’static’,它們?cè)诿總€(gè)程序中只實(shí)例化一次,并在組件裝載的時(shí)候?qū)嵗?/p>
?
第2a部分 在無模式窗體中加入拖放支持
在這部分,我們將加入允許我們使用面板窗體中編輯框的值來創(chuàng)建一個(gè)雇員。當(dāng)用戶從面板中拖動(dòng)到AutoCAD中,將會(huì)提示輸入職位,一個(gè)新的雇員實(shí)體將使用這些值來進(jìn)行創(chuàng)建。
7) 為了支持拖放,我們首先需要一個(gè)對(duì)象來進(jìn)行拖動(dòng)。在編輯框的下面,另外加入一個(gè)名為L(zhǎng)abel4的標(biāo)簽控件,設(shè)置標(biāo)簽的文本為一些提示性的東西(‘Drag to Create Employee’)。通過這個(gè)標(biāo)簽,我們可以在AutoCAD中處理拖放。
要捕捉到什么時(shí)候拖動(dòng)事件發(fā)生,我們必須要知道什么時(shí)候鼠標(biāo)開始操作。
首先,我們要在類的構(gòu)造函數(shù)中注冊(cè)事件,代碼如下:
???????? Label4.MouseMove+= newSystem.Windows.Forms.MouseEventHandler(Label4_MouseMove);
?
8) 在ModelessForm類中加入下面的函數(shù)聲明:
????? private voidLabel4_MouseMove( object sender,System.Windows.Forms.MouseEventArgs e)
????? {
???????? if (System.Windows.Forms.Control.MouseButtons ==System.Windows.Forms.MouseButtons.Left)
???????? {
??????????? // start dragDrop operation, MyDropTarget will be calledwhen the cursor enters the AutoCAD view area.
??????????? Autodesk.AutoCAD.ApplicationServices.Application.DoDragDrop(this, this,System.Windows.Forms.DragDropEffects.All, newMyDropTarget());
???????? }
????? }
?
通常事件處理器有2個(gè)輸入?yún)?shù),一個(gè)object類的sender和與事件有關(guān)的參數(shù)。對(duì)于MouseMove,我們也要做同樣的事情。
運(yùn)行這個(gè)工程,檢查一下當(dāng)鼠標(biāo)經(jīng)過文本的時(shí)候,函數(shù)是否被調(diào)用的。
我們還可以進(jìn)一步知道是不是按了鼠標(biāo)左鍵:
???????? if (System.Windows.Forms.Control.MouseButtons ==System.Windows.Forms.MouseButtons.Left)
???????? {
???????? }
?
我們需要一個(gè)方法來檢測(cè)什么時(shí)候?qū)ο蟊煌先氲紸utoCAD。我們可以使用.NET的基類DropTarget來實(shí)現(xiàn)。要使用它,你只要?jiǎng)?chuàng)建從這個(gè)基類派生的類并實(shí)現(xiàn)你想要的函數(shù)。在我們這個(gè)例子中,我們需要的是OnDrop()。
9) 在工程中加入一個(gè)從Autodesk.AutoCAD.Windows.DropTarget派生的類‘MyDropTarget’。如果你把這個(gè)類加入到ModelessForm.cs文件中,請(qǐng)把這個(gè)類加入到ModelessForm類之后。
????? override public void OnDrop(System.Windows.Forms.DragEventArgs e)
??? {
}
?
在這個(gè)函數(shù)中,我們最后會(huì)調(diào)用AsdkClass1的成員CreateDivision() 和CreateEmployee,傳入ModelessForm類中的編輯框的值。要實(shí)現(xiàn)這個(gè)功能,我們需要一個(gè)方法來連接ModelessForm實(shí)例。最佳的方法是通過DragEventArgs。但首先我們要把鼠標(biāo)事件連接到MyDropTarget類。
10) 加入下面的代碼到鼠標(biāo)左鍵(MouseButtons.Left)處理函數(shù)中:
?? Autodesk.AutoCAD.ApplicationServices.Application.DoDragDrop(this, this,System.Windows.Forms.DragDropEffects.All, newMyDropTarget());
?
注意我們傳入’this’兩次。第一次是用于Control參數(shù),第二次是用于傳入用戶自定義數(shù)據(jù)。因?yàn)槲覀儌魅氲氖荕odelessForm 類的實(shí)例,所以我們可以在放下的時(shí)候使用它來獲取編輯框的值。
11) 回到OnDrop處理函數(shù),讓我們使用參數(shù)來調(diào)用創(chuàng)建雇員的函數(shù)。首先,添加職位提示的代碼。在AsdkClass1.Create()中已經(jīng)有相關(guān)的代碼了,位于‘Get EmployeesCoordinates…’.注釋下面。添加此代碼來提示輸入職位。
12) 接下來,獲取傳入到DragEventArgs 參數(shù)的ModelessForm對(duì)象:
???????? ModelessFormctrl = (ModelessForm)e.Data.GetData(typeof(ModelessForm));
?
請(qǐng)注意一下怎樣通過typeof關(guān)鍵字把參數(shù)強(qiáng)制轉(zhuǎn)化為ModelessForm的實(shí)例。
13) 使用上面的實(shí)例來調(diào)用AsdkClass1成員:
???????? AsdkClass1.CreateDivision(ctrl.tb_Division.Text,AsdkClass1.sDivisionManager);
???????? AsdkClass1.CreateEmployee(ctrl.tb_Name.Text,ctrl.tb_Division.Text, Convert.ToDouble(ctrl.tb_Salary.Text), prPosRes.Value);
?
注意:AsdkClass1的方法要不通過AsdkClass1的實(shí)例來調(diào)用,那么方法必須被聲明為’ public static’。因?yàn)閜ublic static 方法只能調(diào)用其它的public static 方法,你需要修改幾個(gè)AsdkClass1類中的方法為’ public static’。請(qǐng)你進(jìn)行相關(guān)的修改(應(yīng)該至少有4項(xiàng)要修改)。
14) 最后,因?yàn)槲覀兲幚淼氖录挥贏utoCAD命令之外,我們必須再次在會(huì)修改數(shù)據(jù)庫(kù)的代碼處鎖住文檔。請(qǐng)加入鎖住文檔的代碼,加入的方法與前面的上下文菜單是一樣的。
編譯、裝載并運(yùn)行組件,使用PALETTE命令。你應(yīng)該可以使用拖放操作來創(chuàng)建一個(gè)雇員了。
?
第三部分 從有模式窗體中選擇實(shí)體
本章的以下部分將演示獲取一個(gè)用戶在屏幕上選擇的雇員實(shí)例的詳細(xì)信息,并把信息顯示在一個(gè)有模式窗體的編輯框中。這部分的重點(diǎn)是創(chuàng)建一個(gè)有模式窗體,并在執(zhí)行選擇操作而窗體要失去焦點(diǎn)時(shí)隱藏它。為了獲取雇員的詳細(xì)信息,我們將使用第4章結(jié)束時(shí)給出的ListEmployee幫助函數(shù)。
首先,我們需要?jiǎng)?chuàng)建一個(gè)窗體類。這個(gè)類是一個(gè)真實(shí)的窗體而不是我們?cè)贛odelessForm中創(chuàng)建的用戶控件。
15) 在工程中創(chuàng)建一個(gè)Windows窗體類。調(diào)用‘ModalForm’類。在窗體中加入以下所示的三個(gè)編輯框控件和標(biāo)簽控件以及兩個(gè)按鈕。
?
?
?
使用屬性窗口來設(shè)置三個(gè)編輯框的屬性。設(shè)置如下:
<首先是最上面的編輯框>
(Name)??? = tb_Name
Text = <空白>
?
<第二個(gè)編輯框>
(Name) = tb_Division
Text = <空白>
?
<第三個(gè)編輯框>
(Name) = tb_Salary
Text = <空白>
?
<上部的按鈕>
(Name)? = SelectEmployeeButton
Text= Select Employee
?
<下部的按鈕>
(Name)? = Close
Text= Close
?
接下來創(chuàng)建按鈕的事件處理函數(shù)。‘Close’按鈕可以只簡(jiǎn)單地調(diào)用:
???????? this.Close();
?
要顯示對(duì)話框,讓我們?cè)陬愔袆?chuàng)建一個(gè)把窗體實(shí)例化為有模式對(duì)話框的命令函數(shù)。下面的實(shí)現(xiàn)的代碼:
????? [CommandMethod("MODALFORM")]
????? public voidShowModalForm()
????? {
???????? ModalFormmodalForm = new ModalForm();
???????? Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(modalForm);
????? }
?
編譯、裝載并在AutoCAD中運(yùn)行MODALFORM命令來看看對(duì)話框是否可以工作。試試在對(duì)話框的右下角調(diào)整對(duì)話框的大小,然后關(guān)閉它。注意,重新使用MODALFORM命令時(shí),對(duì)話框會(huì)出現(xiàn)在你上次離開的地方!這是ShowModalDialog方法的一個(gè)特征。大小和位置值被AutoCAD保存了。
?
?‘Select Employee’按鈕首先將執(zhí)行一個(gè)簡(jiǎn)單的實(shí)體選擇。這我們可以通過使用Editor.GetEntity()方法來實(shí)現(xiàn),選擇單一的實(shí)體比使用選擇集來得方便的多。下面是怎樣使用這個(gè)方法的代碼:
??????????? PromptEntityOptionsprEnt = new PromptEntityOptions("Select anEmployee");
??????????? PromptEntityResultprEntRes = ed.GetEntity(prEnt);
?
16) 把上面的代碼加入到SelectEmployeeButton_Click處理函數(shù)中,還要加入必需的數(shù)據(jù)庫(kù)、命令行、事務(wù)處理設(shè)置變量和一個(gè)try catch塊。不要忘了在finally塊中銷毀它們。
使用PromptStatus.OK來測(cè)試GetEntity的返回值,如果返回不等于,就調(diào)用this.Show并退出處理函數(shù)。
一旦我們獲得的返回值是OK,那么我們就可以使用PromptEntityResult.ObjectId()方法來獲取所選實(shí)體的object Id。這個(gè)id可以和一個(gè)固定的字符串?dāng)?shù)組被傳入到AsdkClass1.ListEmployee函數(shù)中來獲取雇員的詳細(xì)信息。可以通過以下的代碼說明:
???????? ArrayListsaEmployeeList = new ArrayList();
?
??????????? AsdkClass1.ListEmployee(prEntRes.ObjectId,saEmployeeList);
??????????? if (saEmployeeList.Count == 4)
??????????? {
??????????????? tb_Name.Text= saEmployeeList[0].ToString();
??????????????? tb_Salary.Text= saEmployeeList[1].ToString();
??????????????? tb_Division.Text= saEmployeeList[2].ToString();
??????????? }
?
17) 加入上面的代碼,它會(huì)在窗體的編輯框中顯示雇員的詳細(xì)信息。
?
在開始測(cè)試代碼之前,我們還要記住的是代碼是在有模式對(duì)話框中運(yùn)行的,也就意味著當(dāng)對(duì)話框可見的時(shí)候用戶與AutoCAD的互操作是被禁止的。在用戶能夠進(jìn)行選擇雇員對(duì)象之前,我們必須隱藏窗體。當(dāng)選擇結(jié)束后,我們可以再次站窗體顯示(例如,可以在finally塊的函數(shù)中)
18) 在選擇之前加入隱藏窗體的代碼(例如在try塊之前) ‘this.Hide’ 和選擇結(jié)束后顯示窗體的代碼(例如,可以在finally塊中)‘this.Show’。
編譯、裝載并在AutoCAD中運(yùn)行MODALFORM命令來看看對(duì)話框是否工作。試試選擇一個(gè)實(shí)體并填充窗體中編輯框的值。
?
第四部分 在AutoCAD選項(xiàng)對(duì)話框中加入頁面
?
本章的最后部分將向你介紹如何定義一個(gè)用戶控件,這個(gè)控件可以被作為一個(gè)頁面顯示在AutoCAD的選項(xiàng)對(duì)話框中。我們可以使用這個(gè)頁面來設(shè)置程序運(yùn)行期間的缺省值。在Employee例子中,我們只是在AsdkClass1類中簡(jiǎn)單地設(shè)置了sDivisionDefault 和sDivisionManager字符串。
19) 在工程中加入另外一個(gè)名為‘EmployeeOptions’的用戶控件。在控件中加入兩個(gè)編輯框和標(biāo)簽控件,如下圖所示:
?
使用屬性窗口來設(shè)置編輯框的屬性,設(shè)置如下:
<上面的編輯框>
(Name)??? = tb_EmployeeDivision
Text = <空白>
?
<下面的編輯框>
(Name) = tb_DivisionManager
Text = <空白>
?
使用.NET API來顯示自定義多頁對(duì)話框,需要兩個(gè)步驟。首先,通過傳入要調(diào)用的成員函數(shù)的地址,來知道什么時(shí)候選項(xiàng)對(duì)話框出現(xiàn)。其次是實(shí)現(xiàn)回調(diào)函數(shù)。傳入到回調(diào)函數(shù)中的第二個(gè)參數(shù)是一個(gè)‘TabbedDialogEventArgs’對(duì)象,我們必須使用它來調(diào)用‘AddTab’函數(shù)。AddTab使用一個(gè)標(biāo)題字符串和一個(gè)‘TabbedDialogExtension’對(duì)象的實(shí)例,此實(shí)例封裝了我們的窗體(其實(shí)是用戶控件)。在TabbedDialogExtension的構(gòu)造函數(shù)中,我們輸入窗體的實(shí)例和回調(diào)函數(shù)(OnOK, OnCancel 或OnHelp)的地址。
?
20) 在EmployeeOptions類中,加入一個(gè)名為AddTabDialog的public static函數(shù),它會(huì)添加一個(gè)可供系統(tǒng)調(diào)用的事件處理:
????? public static void AddTabDialog()
????? {
???????? Autodesk.AutoCAD.ApplicationServices.Application.DisplayingOptionDialog+= new TabbedDialogEventHandler(TabHandler);
?????? }
?
在AsdkClass1的Initialize函數(shù)中加入調(diào)用此函數(shù)的代碼。因?yàn)檫@個(gè)函數(shù)是在程序啟動(dòng)的時(shí)候調(diào)用的(因?yàn)轭愐呀?jīng)實(shí)現(xiàn)了IExtensionApplication接口),所以多頁對(duì)話框就被自動(dòng)的加載。
20a) 實(shí)現(xiàn)一個(gè)相同的函數(shù)來移除事件處理,使用C#的-=關(guān)鍵字。
在這里,你可以看到我們?yōu)锳utoCAD中的Application 對(duì)象的DisplayingOptionDialog事件加入了一個(gè)處理函數(shù),此函數(shù)會(huì)調(diào)用‘TabHandler’函數(shù)。所以接下來我們要實(shí)現(xiàn)這個(gè)函數(shù)。
21) 加入下面的代碼來實(shí)現(xiàn)處理函數(shù):
????? private static void TabHandler(objectsender, Autodesk.AutoCAD.ApplicationServices.TabbedDialogEventArgs e)
????? {
???????? EmployeeOptionsEmployeeOptionsPage = new EmployeeOptions();
???????? e.AddTab("AcmeEmployee Options",
??????????? new TabbedDialogExtension(
??????????? EmployeeOptionsPage,
??????????? new TabbedDialogAction(EmployeeOptionsPage.OnOk)));
????? }
?
我們首先實(shí)例化了一個(gè)EmployeeOptions對(duì)象。然后調(diào)用e.AddTab(),在這個(gè)函數(shù)中傳入了一個(gè)TabbedDialogExtension的實(shí)例。TabbedDialogExtension的構(gòu)造函數(shù)使用了EmployeeOptionsPage實(shí)例和一個(gè)TabbedDialogAction對(duì)象。TabbedDialogAction對(duì)象的參數(shù)可以是Ok, Cancel 或Help回調(diào)函數(shù)。在這個(gè)函數(shù)中,我們使用的是OK。
22) 現(xiàn)在剩下的就是確定回調(diào)函數(shù)的內(nèi)容,也就是ONOK的內(nèi)容。前面已經(jīng)說過了,我們只要設(shè)置AsdkClass1的static成員,也就是設(shè)置tb_DivisionManager 和tb_EmployeeDivision編輯框中的值。下面是代碼:
????? public void OnOk()
????? {
???????? AsdkClass1.sDivisionDefault= tb_EmployeeDivision.Text;
???????? AsdkClass1.sDivisionManager= tb_DivisionManager.Text;
????? }
?
編譯、裝載并選擇AutoCAD的選項(xiàng)菜單項(xiàng)來看一下我們的自定義對(duì)話框。試試設(shè)置對(duì)話框中的值并實(shí)例化一個(gè)雇員。你可以使用PRINTOUTEMPLOYEE命令來查看詳細(xì)信息。
?
附加的問題:怎樣讓對(duì)話框的編輯框能自動(dòng)顯示為AsdkClass1中的Manager和Division字符串的內(nèi)容?
?
?
Autodesk官方最新的.NET教程(七)(C#版)
?
第7章 事件
本章將討論AutoCAD中的事件。我們將介紹事件處理函數(shù)的使用,特別是監(jiān)視AutoCAD命令的事件處理函數(shù)和監(jiān)視被AutoCAD命令修改的對(duì)象的事件處理函數(shù)。在解釋怎樣在C#中實(shí)現(xiàn)AutoCAD的事件處理之前,我們將首先簡(jiǎn)要地討論一下.NET中的事件。
第一部分 C#中的事件
事件只是用來通知一個(gè)行為已經(jīng)發(fā)生的信息。在ObjectARX中,我們使用反應(yīng)器(reactor)來處理AutoCAD的事件。而在AutoCAD .NET API中,ObjectARX反應(yīng)器被換成了事件。
事件處理函數(shù)(或者叫回調(diào)函數(shù))是用來監(jiān)視和反饋程序中出現(xiàn)的事件。事件可以以不同的形式出現(xiàn)。
在介紹AutoCAD .NET API中的事件之前,讓我們先來簡(jiǎn)單地了解一下代理。
第1a部分 代理
代理是一個(gè)存儲(chǔ)方法索引的類(概念與函數(shù)指針類似)。代理對(duì)方法是類型安全的(與C中的函數(shù)指針類似)。代理有特定的形式和返回類型。代理可以封裝符合這種特定形式的任何方法。
代理的一個(gè)用途就是作為產(chǎn)生事件的類的分發(fā)器。事件是.NET環(huán)境中第一級(jí)別的對(duì)象。雖然C#把事件處理的許多細(xì)節(jié)給隱藏掉了,但事件總是由代理來實(shí)現(xiàn)的。事件代理可以多次調(diào)用(就是它們可以存儲(chǔ)多于1個(gè)的事件處理方法的索引)。它們保存了用于事件的一個(gè)注冊(cè)事件處理的列表。一個(gè)典型的代理有以下的形式:
public delegate Event (Object sender, EventArgs e)
第一個(gè)參數(shù)sender表示引發(fā)事件的對(duì)象。第二個(gè)參數(shù)e是一個(gè)EventArgs參數(shù)(或者是一個(gè)派生的類),這個(gè)對(duì)象通常包含用于事件處理函數(shù)的數(shù)據(jù)。
第1b部分 +=和-=語句
要使用事件處理函數(shù),我們必須把它與事件聯(lián)系起來。這要通過使用+=語句。+=和-=允許你在運(yùn)行時(shí)連接、斷開或修改與事件聯(lián)系的處理函數(shù)。
當(dāng)我們使用+=語句時(shí),我們要確定事件引發(fā)者的名字,并要使用new語句來確定事件處理函數(shù),例如:
MyClass1.AnEvent += new? HandlerDelegate(EHandler)
前面我們說過要使用-=語句從事件處理函數(shù)中斷開事件(移除聯(lián)系)。語法如下所示:
MyClass1.AnEvent -= new? HandlerDelegate(EHandler)
第2部分 處理.NET中的AutoCAD事件
在ObjectARX中,我們使用反應(yīng)器來封裝AutoCAD事件。在AutoCAD .NET API中,我們可以使用事件來代替ObjectARX反應(yīng)器。
通常,處理AutoCAD事件的步驟如下:
1.?????? 創(chuàng)建事件處理函數(shù)
當(dāng)一個(gè)事件發(fā)生時(shí),事件處理函數(shù)(或稱為回調(diào)函數(shù))被調(diào)用。任何我們想要處理的回應(yīng)AutoCAD事件的動(dòng)作都在事件處理函數(shù)中進(jìn)行。
例如,假定我們只想通知用戶一個(gè)AutoCAD對(duì)象已被加入。我們可以使用AutoCAD數(shù)據(jù)庫(kù)事件”O(jiān)bjectAppended”來完成。我們可以編寫回調(diào)函數(shù)(事件處理函數(shù))如下:
????? public void objAppended(object o, ObjectEventArgs e)
????? {
???????? // 在這里加入處理代碼
????? }
函數(shù)中的第一個(gè)參數(shù)代表AutoCAD數(shù)據(jù)庫(kù)。第二個(gè)參數(shù)代表ObjectEventArgs類,它可能包含對(duì)處理函數(shù)有用的數(shù)據(jù)。
2.?????? 把事件處理函數(shù)與事件聯(lián)系起來
為了開始監(jiān)視動(dòng)作,我們必須把事件處理函數(shù)與事件聯(lián)系起來。在這里,當(dāng)一個(gè)對(duì)象加入到數(shù)據(jù)庫(kù)時(shí),ObjectAppended事件將會(huì)發(fā)生。但是,事件處理函數(shù)不會(huì)響應(yīng)這個(gè)事件,除非我們把它與這個(gè)事件聯(lián)系起來,例如:
???????? Database db;?
???????? db = HostApplicationServices.WorkingDatabase;
???????? db. ObjectAppended += new ObjectEventHandler(objAppended);
3.?????? 斷開事件處理函數(shù)
要終止監(jiān)視一個(gè)動(dòng)作,我們必須斷開事件處理函數(shù)與事件的聯(lián)系。當(dāng)對(duì)象被加入時(shí),我們想要停止通知用戶這個(gè)事件,我們要斷開事件處理函數(shù)與事件ObjectAppended的聯(lián)系。
db. ObjectAppended -= new ObjectEventHandler(objAppended);
?第3部分 使用事件處理函數(shù)來控制AutoCAD的行為
本章的目的是解釋AutoCAD事件怎樣才能被用于控制AutoCAD圖形中的行為。現(xiàn)在,讓我們使用前一章(第六章)的內(nèi)容在AutoCAD圖形中創(chuàng)建幾個(gè)EMPLOYEE塊索引。我們不想讓用戶能改變EMPLOYEE塊索引的位置,而對(duì)于其它的非EMPLOYEE塊索引的位置則沒有這個(gè)限制。我們將混合使用數(shù)據(jù)庫(kù)與文檔事件來做到這一點(diǎn)。
首先,我們想要監(jiān)視將要被執(zhí)行的AutoCAD命令(使用CommandWillStart事件)。特別地,我們要監(jiān)視MOVE命令。另外,當(dāng)一個(gè)對(duì)象要被修改時(shí),我們應(yīng)該被通知(使用ObjectOpenedForModify事件),這樣我們可以確定它是否為一個(gè)EMPLOYEE塊索引。如果這時(shí)就修改對(duì)象可能是無效的,因?yàn)槲覀兊男薷目赡軙?huì)再次觸發(fā)事件,從而引起不穩(wěn)定的行為。所以,我們要等待Move命令的執(zhí)行結(jié)束(使用CommandEnded事件),這時(shí)就可以安全地修改對(duì)象了。當(dāng)然,任何對(duì)塊索引的修改將會(huì)觸發(fā)ObjectOpenedForModify事件。我們還需要設(shè)置一些全局變量來表明一個(gè)MOVE命令在運(yùn)行和被修改的對(duì)象是一個(gè)EMPLOYEE塊索引。
注意:因?yàn)楸菊滦枰容^多的代碼來獲得想要的結(jié)果,所以我們不會(huì)解釋任何與事件處理無關(guān)的代碼,而只是將它們粘貼到事件處理函數(shù)中。這里的重點(diǎn)是成功創(chuàng)建和注冊(cè)事件處理函數(shù)。
第一步:創(chuàng)建新工程
我們以第六章的工程開始。請(qǐng)新加入一個(gè)類AsdkClass2。我們還要加入四個(gè)全局變量。前兩個(gè)是Boolean型的:一個(gè)用來表示我們監(jiān)視的命令是否是活動(dòng)的,另外一個(gè)用來表示ObjectOpenedForModify事件處理函數(shù)是否該被忽略。
????? //全局變量
????? bool bEditCommand;
????? bool bDoRepositioning;?
接下來,我們要聲明一個(gè)全局變量來表示一個(gè)ObjectIdCollection,它用來存儲(chǔ)我們所選擇的要修改的對(duì)象的ObjectID。
????? ObjectIdCollection changedObjects = new ObjectIdCollection();
最后,我們要聲明一個(gè)全局變量來表示一個(gè)Point3dCollection,它用來包含我們所選對(duì)象的位置(三維點(diǎn))。
????? Point3dCollection employeePositions = new Point3dCollection();
第2步:創(chuàng)建第一個(gè)文檔事件處理函數(shù)(回調(diào)函數(shù))
現(xiàn)在我們要?jiǎng)?chuàng)建一個(gè)事件處理函數(shù)。當(dāng)AutoCAD命令開始執(zhí)行的時(shí)候它會(huì)通知我們。我們要檢查GlobalCommandName的值是否為MOVE。
???????? if ( e.GlobalCommandName == "MOVE" )
???????? {
???????? }
如果MOVE命令開始執(zhí)行的話,我們要相應(yīng)地設(shè)置Boolean變量bEditCommand的值,這樣我們可以知道我們所監(jiān)視的命令是活動(dòng)的。同樣地,我們應(yīng)該把另外一個(gè)Boolean變量bDoRepositioning設(shè)置為false來忽略O(shè)bjectOpenedForModify事件處理函數(shù)。兩個(gè)變量設(shè)置好以后,在命令活動(dòng)期間,我們必須要獲得所選塊索引的信息。
我們還應(yīng)該把兩個(gè)集合對(duì)象的內(nèi)容清空。我們只關(guān)心當(dāng)前選擇的對(duì)象。
第3步: 創(chuàng)建數(shù)據(jù)庫(kù)事件處理函數(shù)(回調(diào)函數(shù))
無論什么時(shí)候一個(gè)對(duì)象被打開并要被修改時(shí),數(shù)據(jù)庫(kù)事件處理函數(shù)會(huì)被調(diào)用。當(dāng)然,如果這時(shí)我們監(jiān)視的命令不是活動(dòng)的,我們就應(yīng)該跳過任何被這個(gè)回調(diào)函數(shù)調(diào)用的內(nèi)容。
???????? if ( bEditCommand == false )
???????? {
??????????? return;
???????? }
同樣地,如果我們監(jiān)視的命令已經(jīng)結(jié)束,而ObjectOpenedForModify事件被另一個(gè)回調(diào)函數(shù)再次觸發(fā)的話,而這時(shí)有對(duì)象被修改時(shí),我們要阻止所有由這個(gè)回調(diào)函數(shù)執(zhí)行的動(dòng)作。
???????? if ( bDoRepositioning == true )
???????? {
??????????? return;
???????? }
這個(gè)回調(diào)函數(shù)剩余部分的代碼用來驗(yàn)證我們是否正在處理EMPLOYEE塊索引。如果是的話,我們就獲取它的ObjectID和位置(三維點(diǎn))。下面的代碼可以被粘貼到這個(gè)事件處理函數(shù)函數(shù)。
????? 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步 創(chuàng)建第二個(gè)文檔事件處理函數(shù)(回調(diào)函數(shù))
當(dāng)一個(gè)命令結(jié)束時(shí),第三個(gè)事件處理函數(shù)被調(diào)用。同樣地,我們要檢查全局變量來驗(yàn)證這個(gè)將要結(jié)束的命令是我們監(jiān)視的命令。如果是我們監(jiān)視的,那么我們要重置這個(gè)變量:
???????? if ( bEditCommand == false )
???????? {
??????????? return;
???????? }
???????? bEditCommand = false;
這個(gè)回調(diào)函數(shù)執(zhí)行的動(dòng)作將會(huì)再次觸發(fā)ObjectOpenedForModify事件。我們必須確定在這個(gè)回調(diào)函數(shù)中跳過了所有與此事件有關(guān)的動(dòng)作。
???????? //設(shè)置標(biāo)志來跳過OpenedForModify處理函數(shù)
???????? bDoRepositioning = true;
這個(gè)回調(diào)函數(shù)的剩余代碼用來把EMPLOYEE塊索引和它的關(guān)聯(lián)屬性引用的當(dāng)前(修改過的)位置與它們的初始位置作比較。如果位置改變了,我們?cè)谶@個(gè)回調(diào)函數(shù)中把它們重置這初始的位置。下面的代碼可以被粘貼到這個(gè)事件處理函數(shù)中。
????? 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步 創(chuàng)建命令來注冊(cè)/斷開事件處理函數(shù)
創(chuàng)建一個(gè)ADDEVENTS命令,使用+=語句來把上面的3個(gè)事件處理函數(shù)連接到各自的事件。在這個(gè)命令中,我們還應(yīng)該設(shè)置全局Boolean變量:
???????? bEditCommand = false;
???????? bDoRepositioning = false;
創(chuàng)建另外一個(gè)命令REMOVEEVENTS,使用-=語句把事件處理函數(shù)與事件斷開。
第6步: 測(cè)試工程
要測(cè)試這個(gè)工程,請(qǐng)使用CREATE命令創(chuàng)建一個(gè)或多個(gè)EMPLOYEE塊索引。如果你要作比較的話,你也可以插入一些非EMPLOYEE的塊索引。
在命令行中鍵入ADDEVENTS命令來執(zhí)行它。
在命令行中輸入MOVE命令,然后選擇你想要的塊索引。注意,當(dāng)MOVE命令結(jié)束時(shí),EMPLOYEE塊索引(包括屬性)還留在原處。
執(zhí)行REMOVEEVENTS命令,然后在試一下MOVE命令。注意,EMPLOYEE塊索引現(xiàn)在可以被移動(dòng)了。
附加的問題:添加一個(gè)附加的回調(diào)函數(shù),當(dāng)用戶改變EMPLOYEE塊索引的”Name”屬性時(shí),這個(gè)回調(diào)函數(shù)被觸發(fā)。?
總結(jié)
以上是生活随笔為你收集整理的在.NET中进行AutoCAD二次开发(C#+ObjectArx) (二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Go + C 一款简单的贪吃蛇
 - 下一篇: java实现“进制转换-在线工具”