日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

在.NET中进行AutoCAD二次开发(C#+ObjectArx) (二)

發(fā)布時間:2024/3/13 C# 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在.NET中进行AutoCAD二次开发(C#+ObjectArx) (二) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Autodesk官方最新的.NET教程(五)(C#版)

?

第 5 章 用戶互操作:提示和選擇

背景

提示通常包含一個描述性信息,伴隨一個停止以讓用戶理解所給的信息并輸入數(shù)據(jù)。數(shù)據(jù)可以通過多種方式被輸入,如通過命令行、對話框或AutoCAD編輯窗口。給出的提示要遵循一定的格式,格式要與一般的AutoCAD提示相一致,這一點是非常重要的。例如,關鍵字要用“/”號分隔并放在方括號“[]”中,缺省值要放在“<>”內(nèi)。對于一個AutoCAD用戶來說,堅持統(tǒng)一的格式將會減少信息理解錯誤的產(chǎn)生。

當用戶在AutoCAD命令行中選擇一個實體時,實體是使用選擇機制被選擇的。這種機制包括一個提示,用來讓用戶知道選擇什么并怎樣選擇(如,窗口或單一實體),然后是一個停頓。

試一下諸如PINE這種命令來看一下提示的顯示,PEDIT來看一下使用單一實體或多線來進行選擇。

練習

Prompts:

提示:

在本章中,我們將提示輸入雇員名字、職位、薪水和部門來創(chuàng)建一個雇員塊索引對象。如果輸入的部門不存在,我們將提示輸入部門經(jīng)理的名字來創(chuàng)建一個新的部門。在我們繼續(xù)之前,讓我們試著重用以前的代碼。

為了進行選擇,我們將提示用戶在一個窗口中進行選擇或選擇一個實體,而我們只顯示選擇集中的雇員對象。

在前面的章節(jié)中,我們創(chuàng)建了一個名叫“Earnest Shackleton”的雇員,名字被存儲為“EmployeeBlock”塊定義(塊表記錄)中的MText。如果我們多次插入這個塊,那么我們看到的都是同一個雇員的名字。我們怎樣才能自定義這個塊以使每次插入這個塊的時候顯示不同雇員的名字?這就要使用塊屬性的功能了。屬性是存儲在每一個塊索引實例中的文本,并被作為實例的一部分來被顯示。屬性從存儲在塊表記錄中的屬性定義中繼承相關的屬性。

屬性:

讓我們來把MText實體類型改變?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命令來測試一下CreateEmployeeDefinition()函數(shù):

?????? [CommandMethod("Test")]

?????? public? void Test()

?????? {

?????? ?????? CreateEmployeeDefinition();

?????? }

?

你現(xiàn)在應該可以使用INSERT命令來插入EmployeeBlock塊并對每一個實例確定一個雇員名。

當你插入Employee塊時,請注意一下塊插入的位置。它是正好被放置在所選點還是有些偏移?試試怎樣修復它。(提示:檢查塊定義中的圓心)

修改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ù)的代碼,這樣就可以正確地設置塊索引的名字、職位、部門和薪水和它的擴展字典。

·??????? 替換

?

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)????????????????? 因為我們把雇員的名字從MText替換成塊的屬性定義,因此我們要創(chuàng)建一個相應的屬性索引來顯示雇員的名字。屬性索引將使用屬性定義的屬性。

·???????? 替換:

?

?????? btr.AppendEntity(br); //加入索引到模型空間

?????? trans.AddNewlyCreatedDBObject(br, true); //讓事務處理知道

?

?

?????? 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);

????????????? //打開當前的對象!

????????????? if (ent is AttributeDefinition)?

????????????? {

???????????????????? //設置屬性為屬性索引中的屬性定義

???????????????????? 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);

?????? //讓事務處理知道

?????? trans.AddNewlyCreatedDBObject(attRef, true);

?????? trans.AddNewlyCreatedDBObject(br, true);

?

?

研究一下上面的代碼,看看是怎樣把屬性定義中除顯示用的文本字符串外的屬性復制到屬性索引的。屬性被加入到塊索引的屬性集合中。這就是你怎樣來為每一個實例自定義雇員名字。

5)不要忘記返回雇員塊索引的ObjectId,但要在提交事務處理之后才能返回:

trans.Commit();

return br.ObjectId;

?

6)?????? 測試CreateEmployee。

加入一個Test命令來測試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)理擴展記錄的ObjectId。如果部門經(jīng)理已經(jīng)存在,則不改變經(jīng)理的名字。

1)????????????????? 如果你先前在CreateEmployeeDefinition()中調(diào)用了CreateDivision(),請把它注釋掉,因為我們在這里不需要創(chuàng)建一個部門

2)????   改變CreateDivision()的形式讓它接收部門和經(jīng)理的名字并返回一個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)理這個擴展記錄的ObjectId,但要在提交事務處理后才返回。

trans.Commit();

//返回部門經(jīng)理這個擴展記錄的ObjectId

return mgrXRec.ObjectId;

?

現(xiàn)在把在中CreateEmployeeDefinition調(diào)用的CreateDivision函數(shù)給注釋掉。

4)??? 現(xiàn)在通過使用TEST命令來測試調(diào)用CreateDivision函數(shù)。使用ArxDbg工具來檢查條目是否已被加入到“ACME_DIVISION”下的命名對象字典。

CreateDivision("Sales", "Randolph P. Brokwell")

?

使用CREATE命令來創(chuàng)建雇員:

?

我們將加入一個名為CREATE的新命令,此命令用來提示輸入雇員的詳細資料來創(chuàng)建雇員塊索引。讓我們來看一下這個命令是怎樣使用的。

1)????? 讓我們加入一個名為CREATE的新命令,并聲明幾個常用的變量和一個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類來初始化要顯示的提示字符串。

//提示輸入每個雇員的詳細資料

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");

?

注意,提示字符串用尖括號來顯示變量的值。這是AutoCAD用來提示用戶這個值為缺省值。

4)? 當提示用戶輸入職位時,我們也提供了一個關鍵字列表選項,如名字、部門和薪水。如果用戶想要在選擇一個點的時候改變?yōu)槠渌?#xff0c;他可以選擇那個關鍵字。

一個命令提示的例子如下:

Command: CREATE

Enter Employee Position or [Name/Division/Salary]:

?

要創(chuàng)建一個雇員,用戶會選擇一個點而其它的值被設置為缺省值。如果用戶要改變其它的值,如名字,他可以輸入”N”或全名”Name”,然后輸入名字:

Command: CREATE

Enter Employee Position or [Name/Division/Salary]:N

Enter Employee Name <Earnest Shackleton>:

?

如果用戶想要再次選擇缺省的名字,他可以按回車鍵。

讓我們創(chuàng)建用于職位提示的關鍵字列表:

?

//加入用于職位提示的關鍵字

prPos.Keywords.Add("Name");

prPos.Keywords.Add("Division");

prPos.Keywords.Add("Salary");

//設置提示的限制條件

prPos.AllowNone = false; //不允許沒有值

?

?

5)????? 現(xiàn)在讓我們聲明PromptXXXResult變量來獲取提示的結(jié)果:

//prompt results

PromptResult prNameRes;

PromptResult prDivRes;

PromptDoubleResult prSalRes;

PromptPointResult prPosRes;

?

6)????? 直到用戶成功輸入一個點后,循環(huán)才結(jié)束。如果輸入錯誤的話,我們會提示用戶并退出函數(shù):

判斷用戶是否輸入了關鍵字,我們通過檢查promptresult的狀態(tài)來進行:

//循環(huán)用來獲取雇員的詳細資料。當職位被輸入后,循環(huán)終止。

while (!gotPosition)

{

?????? //提示輸入職位

?????? prPosRes = ed.GetPoint(prPos);

?????? //取得一個點

?????? if (prPosRes.Status == PromptStatus.OK)

?????? {

?????? ?????? gotPosition = true;

????????????? position = prPosRes.Value;

?????? }

?????? else if (prPosRes.Status == PromptStatus.Keyword) //獲取一個關鍵字

?????? {

????????????? //輸入了Name關鍵字

????????????? if (prPosRes.StringResult == "Name")

????????????? {

????????????? ?????? //獲取雇員名字

???????????????????? prName.AllowSpaces = true;

???????????????????? prNameRes = ed.GetString(prName);

???????????????????? if (prNameRes.Status != PromptStatus.OK)

???????????????????? {

???????????????????? ?????? return;

???????????????????? }

???????????????????? //如果獲取雇員名字成功

???????????????????? if (prNameRes.StringResult != "")

???????????????????? {

???????????????????? ?????? empName = prNameRes.StringResult;

???????????????????? }

????????????? }

?????? }

?????? else

?????? {

????????????? // 獲取職位時發(fā)生錯誤

????????????? ed.WriteMessage("***Error in getting a point, exiting!!***" + "/r/n");

????????????? return;

?????? }? // 如果獲取一個點

?

}

?

7)????? 上面的代碼只提示輸入名字,請加入提示輸入薪水和部門的代碼。

8)????? 完成提示輸入后,我們將使用獲得的值來創(chuàng)建雇員。

//創(chuàng)建雇員

CreateEmployee(empName, divName, salary, position);

?

9)????? 現(xiàn)在來檢查部門經(jīng)理是否已存在。我們通過檢查NOD中部門的擴展記錄中的經(jīng)理名字來進行。如果檢查到的是一個空字符串,那么我們會提示用戶輸入經(jīng)理的名字。注意,通過修改CreateDivision()函數(shù),獲取經(jīng)理的名字變得簡單了。

string manager = "";

//創(chuàng)建部門

//給經(jīng)理傳入一個空字符串來檢查它是否已存在

Xrecord depMgrXRec;

ObjectId xRecId;

xRecId = CreateDivision(divName, manager);

//打開部門經(jīng)理擴展記錄

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)理沒有被設置,現(xiàn)在設置它

????????????? // 先提示輸入經(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;

?????? ?????????? }

????????????? //設置經(jīng)理的名字

????????????? depMgrXRec.Data = new ResultBuffer(new TypedValue((int)DxfCode.Text, prManagerNameRes.StringResult));

?????? }

}

?

?

?

10)? 測試CREATE命令

?

選擇集:

現(xiàn)在讓我們來創(chuàng)建一個命令,當用戶在圖形中選擇一個雇員對象時,它會顯示雇員的詳細資料。

我們會使用上一章中創(chuàng)建的ListEmployee()函數(shù)在命令行中輸出雇員的詳細資料。

下面是你必須遵循的步驟:

1.? 調(diào)用“LISTEMPLOYEES”命令

2.? 調(diào)用Editor的GetSelection()函數(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. 最后,把選擇集中的每個ObjectId輸入到ListEmployee()函數(shù)來獲取一個雇員詳細資料的字符串數(shù)組。把雇員的詳細資料輸出到命令行。例如:

?

//獲取saEmployeeList 數(shù)組中的所有雇員

foreach (ObjectId employeeId in idArray)

{

?????? ListEmployee(employeeId, ref saEmployeeList);

?????? //把雇員的詳細資料輸出到命令行

?????? foreach (string employeeDetail in saEmployeeList)

?????? {

?????? ?????? ed.WriteMessage(employeeDetail);

?????? }

??????

?????? ed.WriteMessage("----------------------" + "/r/n");

}

?


Autodesk官方最新的.NET教程(六)(C#版)

?

第6章  更多的用戶界面:添加自定義數(shù)據(jù)

在本章中,我們將介紹.NET API的用戶界面部分能做些什么。我們首先將介紹一個自定義上下文菜單(快捷菜單)。接下來我們將實現(xiàn)一個無模式可??康拿姘?#xff08;一個真正的AutoCAD增強輔助窗口)來支持拖放操作。接著我們將介紹通過模式窗體選取實體。最后,我們將介紹使用AutoCAD的選項對話框來設置雇員的缺省值。

本章還會介紹和上面內(nèi)容有關的API。

?

第一部分 自定義上下文菜單

?

到目前為止,我們所寫的代碼只與CommandMethod屬性定義的命令行進行相互操作。一個AutoCAD .NET程序能通過一個特殊的類來實現(xiàn)裝載時的初始化工作。這個類只要實現(xiàn)IExtensionApplication .NET接口并暴露一個組件級別的屬性(此屬性把類定義為ExtensionApplication),就可以響應一次性的裝載和卸載事件。例子:

[assembly: ExtensionApplication(typeof(Lab6_CS.AsdkClass1))]

?

?? public classAsdkClass1 : IExtensionApplication

?? {

?

1) 現(xiàn)在修改AsdkClass1類來實現(xiàn)上面的接口。要實現(xiàn)這個接口,你必須實現(xiàn)Initialize() 和Terminate()函數(shù)。因為我們要實現(xiàn)的是一個接口,而接口中的函數(shù)總是定義為純虛擬的。

?

????? public voidInitialize()

????? {

???????? AddContextMenu();

???????? EmployeeOptions.AddTabDialog();

????? }?

?????

????? public voidTerminate()

????? {

????? }

?

為了加入自定義上下文菜單,我們必須定義一個‘ContextMenuExtension’類的成員。這個類位于Autodesk.AutoCAD.Windows命名空間中。

要使用ContextMenuExtension,我們必須使用new關鍵字來進行初始化,給必要的屬性賦值,并調(diào)用Application.AddDefaultContextMenuExtension()。上下文菜單的工作方式是:對于每個菜單條目,我們定義一個成員函數(shù)來處理菜單單擊事件。我們可能通過.NET的代理來實現(xiàn)。我們使用C#關鍵字+=和-=確定讓哪個函數(shù)來處理事件。請盡快熟悉這種設計模式,因為在C#中會使用很多。

2) 添加一個‘ContextMenuExtension’成員變量和下面兩個用來添加和移除自定義菜單的函數(shù)。請好好研究一下代碼來看看究竟發(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

???????? {

???????? }

????? }

?

注意我們在代碼中使用了‘CallbackOnClick’函數(shù)。我們希望這個函數(shù)(我們現(xiàn)在還沒有定義)響應菜單項選擇事件。在我們的例子中,我們想要做的是調(diào)用我們的成員函數(shù)‘Create()’。請加入下面的代碼。

????? void CallbackOnClick(objectSender, EventArgs e)

????? {

???????? Create();

????? }

?

現(xiàn)在,從Initialize()中調(diào)用AddContextMenu()函數(shù),同樣地,請在Terminate()中調(diào)用RemoveContextMenu()。

請運行代碼。使用NETLOAD來裝載編譯好的組件,然后在AutoCAD的空白處右擊……你應該可以看到’Acme‘快捷菜單了。如果失敗了,明明你做的都是正確的……為什么呢?

通常,AutoCAD的數(shù)據(jù)是存儲在文檔中的,而訪問實體的命令有權修改文檔。當我們運行代碼來響應上下文菜單單擊事件,我們是從命令結(jié)構(gòu)的外部來訪問文檔。當我們調(diào)用的代碼嘗試通過添加一個雇員來修改文檔時,我們就碰到了錯誤。正確的做法是必須鎖住文檔,這可以通過使用Document.LockDocument()命令來實現(xiàn)。

3) 修改CallbackOnClick來鎖住文檔:

????? void CallbackOnClick(objectSender, EventArgs e)

????? {

???????? DocumentLockdocLock =Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument();

???????? Create();

???????? docLock.Dispose();

????? }

?

注意,我們保留了一個‘DocumentLock’對象的拷貝。要把文檔解鎖,我們只要銷毀這個DocumentLock對象。

再次運行代碼?,F(xiàn)在應該可以看到快捷菜單了。

?

第2部分 無模式對話框、可進行拖放的可??棵姘?/p>

為了使我們的用戶界面和AutoCAD實現(xiàn)無縫鏈接,我們要盡可能在所有的地方使用同樣的用戶界面結(jié)構(gòu)。這會使應用程序看起來與AutoCAD結(jié)合的很好,并有效地減少代碼的重復。一個很好的例子是AutoCAD中的可停靠面板。

使用.NET API,我們可以創(chuàng)建一個簡單的窗體并把它放到面板中。我們可以實例化一個自定義的‘PaletteSet’對象來包含窗體,并可以把這個PaletteSet定義成我們喜歡的樣式。

4) 在解決方案瀏覽器中通過右擊工程來添加一個用戶控件。給它命名為ModelessForm。使用控件工具箱,加入如下所示的編輯框和標簽控件。

?

使用屬性窗口設置三個編輯框的屬性。設置如下:

<首先是最上面的編輯框>

(Name)??? = tb_Name

Text = <請輸入一個名字>

?

<第二個編輯框>

(Name) = tb_Division

Text = Sales

?

<第三個編輯框>

(Name) = tb_Salary

Text = <請輸入薪水>

?

?

要使用.NET API實例化一個面板對象,你必須要實例化用戶控件對象(無模式窗體)和‘PaletteSet’對象。調(diào)用PaletteSet的成員函數(shù)Add來傳遞用戶控件對象。

5) 接下來,我們要加入一個命令來創(chuàng)建面板。在類中加入一個名為CreatePalette的函數(shù)和CommandMethod屬性來定義名為“PALETTE”的命令。

請看一下下面的代碼塊。這是實例化面板的代碼。

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ù)?!畃s’需要在函數(shù)的外部聲明:

????? private Autodesk.AutoCAD.Windows.PaletteSet ps;

?

在函數(shù)的實例化面板代碼之前加入檢查ps是否為null的代碼。

編譯并運行工程。在AutoCAD中裝載組件,運行‘PALETTE’命令來檢查面板是否被裝載。

使用PaletteSet.Style來看看PaletteSetStyles對象。例如:

ps.Style = PaletteSetStyles.ShowTabForSingle;

我們也可以試試諸如透明性的屬性,例如:

?? ps.Opacity =90;

?

注意:要使用PaletteSet 和PaletteSetStyles對象,你必須加入兩個命名空間Autodesk.AutoCAD.Windows和Autodesk.AutoCAD.Windows.Palette

?

在我們繼續(xù)之前,讓我們執(zhí)行一個快速的維護更新:請在AsdkClass1類中加入下列成員:

????? public static string sDivisionDefault = "Sales";

????? public static string sDivisionManager = "Fiona Q.Farnsby";

?

這些值將被用作為部門和部門經(jīng)理的缺省值。由于它們被聲明為’static’,它們在每個程序中只實例化一次,并在組件裝載的時候?qū)嵗?/p>

?

第2a部分 在無模式窗體中加入拖放支持

在這部分,我們將加入允許我們使用面板窗體中編輯框的值來創(chuàng)建一個雇員。當用戶從面板中拖動到AutoCAD中,將會提示輸入職位,一個新的雇員實體將使用這些值來進行創(chuàng)建。

7) 為了支持拖放,我們首先需要一個對象來進行拖動。在編輯框的下面,另外加入一個名為Label4的標簽控件,設置標簽的文本為一些提示性的東西(‘Drag to Create Employee’)。通過這個標簽,我們可以在AutoCAD中處理拖放。

要捕捉到什么時候拖動事件發(fā)生,我們必須要知道什么時候鼠標開始操作。

首先,我們要在類的構(gòu)造函數(shù)中注冊事件,代碼如下:

???????? 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個輸入?yún)?shù),一個object類的sender和與事件有關的參數(shù)。對于MouseMove,我們也要做同樣的事情。

運行這個工程,檢查一下當鼠標經(jīng)過文本的時候,函數(shù)是否被調(diào)用的。

我們還可以進一步知道是不是按了鼠標左鍵:

???????? if (System.Windows.Forms.Control.MouseButtons ==System.Windows.Forms.MouseButtons.Left)

???????? {

???????? }

?

我們需要一個方法來檢測什么時候?qū)ο蟊煌先氲紸utoCAD。我們可以使用.NET的基類DropTarget來實現(xiàn)。要使用它,你只要創(chuàng)建從這個基類派生的類并實現(xiàn)你想要的函數(shù)。在我們這個例子中,我們需要的是OnDrop()。

9) 在工程中加入一個從Autodesk.AutoCAD.Windows.DropTarget派生的類‘MyDropTarget’。如果你把這個類加入到ModelessForm.cs文件中,請把這個類加入到ModelessForm類之后。

????? override public void OnDrop(System.Windows.Forms.DragEventArgs e)

??? {

}

?

在這個函數(shù)中,我們最后會調(diào)用AsdkClass1的成員CreateDivision() 和CreateEmployee,傳入ModelessForm類中的編輯框的值。要實現(xiàn)這個功能,我們需要一個方法來連接ModelessForm實例。最佳的方法是通過DragEventArgs。但首先我們要把鼠標事件連接到MyDropTarget類。

10) 加入下面的代碼到鼠標左鍵(MouseButtons.Left)處理函數(shù)中:

?? Autodesk.AutoCAD.ApplicationServices.Application.DoDragDrop(this, this,System.Windows.Forms.DragDropEffects.All, newMyDropTarget());

?

注意我們傳入’this’兩次。第一次是用于Control參數(shù),第二次是用于傳入用戶自定義數(shù)據(jù)。因為我們傳入的是ModelessForm 類的實例,所以我們可以在放下的時候使用它來獲取編輯框的值。

11) 回到OnDrop處理函數(shù),讓我們使用參數(shù)來調(diào)用創(chuàng)建雇員的函數(shù)。首先,添加職位提示的代碼。在AsdkClass1.Create()中已經(jīng)有相關的代碼了,位于‘Get EmployeesCoordinates…’.注釋下面。添加此代碼來提示輸入職位。

12) 接下來,獲取傳入到DragEventArgs 參數(shù)的ModelessForm對象:

???????? ModelessFormctrl = (ModelessForm)e.Data.GetData(typeof(ModelessForm));

?

請注意一下怎樣通過typeof關鍵字把參數(shù)強制轉(zhuǎn)化為ModelessForm的實例。

13) 使用上面的實例來調(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的實例來調(diào)用,那么方法必須被聲明為’ public static’。因為public static 方法只能調(diào)用其它的public static 方法,你需要修改幾個AsdkClass1類中的方法為’ public static’。請你進行相關的修改(應該至少有4項要修改)。

14) 最后,因為我們處理的事件位于AutoCAD命令之外,我們必須再次在會修改數(shù)據(jù)庫的代碼處鎖住文檔。請加入鎖住文檔的代碼,加入的方法與前面的上下文菜單是一樣的。

編譯、裝載并運行組件,使用PALETTE命令。你應該可以使用拖放操作來創(chuàng)建一個雇員了。

?

第三部分  從有模式窗體中選擇實體

本章的以下部分將演示獲取一個用戶在屏幕上選擇的雇員實例的詳細信息,并把信息顯示在一個有模式窗體的編輯框中。這部分的重點是創(chuàng)建一個有模式窗體,并在執(zhí)行選擇操作而窗體要失去焦點時隱藏它。為了獲取雇員的詳細信息,我們將使用第4章結(jié)束時給出的ListEmployee幫助函數(shù)。

首先,我們需要創(chuàng)建一個窗體類。這個類是一個真實的窗體而不是我們在ModelessForm中創(chuàng)建的用戶控件。

15) 在工程中創(chuàng)建一個Windows窗體類。調(diào)用‘ModalForm’類。在窗體中加入以下所示的三個編輯框控件和標簽控件以及兩個按鈕。

?

?

?

使用屬性窗口來設置三個編輯框的屬性。設置如下:

<首先是最上面的編輯框>

(Name)??? = tb_Name

Text = <空白>

?

<第二個編輯框>

(Name) = tb_Division

Text = <空白>

?

<第三個編輯框>

(Name) = tb_Salary

Text = <空白>

?

<上部的按鈕>

(Name)? = SelectEmployeeButton

Text= Select Employee

?

<下部的按鈕>

(Name)? = Close

Text= Close

?

接下來創(chuàng)建按鈕的事件處理函數(shù)?!瓹lose’按鈕可以只簡單地調(diào)用:

???????? this.Close();

?

要顯示對話框,讓我們在類中創(chuàng)建一個把窗體實例化為有模式對話框的命令函數(shù)。下面的實現(xiàn)的代碼:

????? [CommandMethod("MODALFORM")]

????? public voidShowModalForm()

????? {

???????? ModalFormmodalForm = new ModalForm();

???????? Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(modalForm);

????? }

?

編譯、裝載并在AutoCAD中運行MODALFORM命令來看看對話框是否可以工作。試試在對話框的右下角調(diào)整對話框的大小,然后關閉它。注意,重新使用MODALFORM命令時,對話框會出現(xiàn)在你上次離開的地方!這是ShowModalDialog方法的一個特征。大小和位置值被AutoCAD保存了。

?

?‘Select Employee’按鈕首先將執(zhí)行一個簡單的實體選擇。這我們可以通過使用Editor.GetEntity()方法來實現(xiàn),選擇單一的實體比使用選擇集來得方便的多。下面是怎樣使用這個方法的代碼:

??????????? PromptEntityOptionsprEnt = new PromptEntityOptions("Select anEmployee");

??????????? PromptEntityResultprEntRes = ed.GetEntity(prEnt);

?

16) 把上面的代碼加入到SelectEmployeeButton_Click處理函數(shù)中,還要加入必需的數(shù)據(jù)庫、命令行、事務處理設置變量和一個try catch塊。不要忘了在finally塊中銷毀它們。

使用PromptStatus.OK來測試GetEntity的返回值,如果返回不等于,就調(diào)用this.Show并退出處理函數(shù)。

一旦我們獲得的返回值是OK,那么我們就可以使用PromptEntityResult.ObjectId()方法來獲取所選實體的object Id。這個id可以和一個固定的字符串數(shù)組被傳入到AsdkClass1.ListEmployee函數(shù)中來獲取雇員的詳細信息。可以通過以下的代碼說明:

???????? 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) 加入上面的代碼,它會在窗體的編輯框中顯示雇員的詳細信息。

?

在開始測試代碼之前,我們還要記住的是代碼是在有模式對話框中運行的,也就意味著當對話框可見的時候用戶與AutoCAD的互操作是被禁止的。在用戶能夠進行選擇雇員對象之前,我們必須隱藏窗體。當選擇結(jié)束后,我們可以再次站窗體顯示(例如,可以在finally塊的函數(shù)中)

18) 在選擇之前加入隱藏窗體的代碼(例如在try塊之前) ‘this.Hide’ 和選擇結(jié)束后顯示窗體的代碼(例如,可以在finally塊中)‘this.Show’。

編譯、裝載并在AutoCAD中運行MODALFORM命令來看看對話框是否工作。試試選擇一個實體并填充窗體中編輯框的值。

?

第四部分 在AutoCAD選項對話框中加入頁面

?

本章的最后部分將向你介紹如何定義一個用戶控件,這個控件可以被作為一個頁面顯示在AutoCAD的選項對話框中。我們可以使用這個頁面來設置程序運行期間的缺省值。在Employee例子中,我們只是在AsdkClass1類中簡單地設置了sDivisionDefault 和sDivisionManager字符串。

19) 在工程中加入另外一個名為‘EmployeeOptions’的用戶控件。在控件中加入兩個編輯框和標簽控件,如下圖所示:

?

使用屬性窗口來設置編輯框的屬性,設置如下:

<上面的編輯框>

(Name)??? = tb_EmployeeDivision

Text = <空白>

?

<下面的編輯框>

(Name) = tb_DivisionManager

Text = <空白>

?

使用.NET API來顯示自定義多頁對話框,需要兩個步驟。首先,通過傳入要調(diào)用的成員函數(shù)的地址,來知道什么時候選項對話框出現(xiàn)。其次是實現(xiàn)回調(diào)函數(shù)。傳入到回調(diào)函數(shù)中的第二個參數(shù)是一個‘TabbedDialogEventArgs’對象,我們必須使用它來調(diào)用‘AddTab’函數(shù)。AddTab使用一個標題字符串和一個‘TabbedDialogExtension’對象的實例,此實例封裝了我們的窗體(其實是用戶控件)。在TabbedDialogExtension的構(gòu)造函數(shù)中,我們輸入窗體的實例和回調(diào)函數(shù)(OnOK, OnCancel 或OnHelp)的地址。

?

20) 在EmployeeOptions類中,加入一個名為AddTabDialog的public static函數(shù),它會添加一個可供系統(tǒng)調(diào)用的事件處理:

????? public static void AddTabDialog()

????? {

???????? Autodesk.AutoCAD.ApplicationServices.Application.DisplayingOptionDialog+= new TabbedDialogEventHandler(TabHandler);

?????? }

?

在AsdkClass1的Initialize函數(shù)中加入調(diào)用此函數(shù)的代碼。因為這個函數(shù)是在程序啟動的時候調(diào)用的(因為類已經(jīng)實現(xiàn)了IExtensionApplication接口),所以多頁對話框就被自動的加載。

20a) 實現(xiàn)一個相同的函數(shù)來移除事件處理,使用C#的-=關鍵字。

在這里,你可以看到我們?yōu)锳utoCAD中的Application 對象的DisplayingOptionDialog事件加入了一個處理函數(shù),此函數(shù)會調(diào)用‘TabHandler’函數(shù)。所以接下來我們要實現(xiàn)這個函數(shù)。

21) 加入下面的代碼來實現(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)));

????? }

?

我們首先實例化了一個EmployeeOptions對象。然后調(diào)用e.AddTab(),在這個函數(shù)中傳入了一個TabbedDialogExtension的實例。TabbedDialogExtension的構(gòu)造函數(shù)使用了EmployeeOptionsPage實例和一個TabbedDialogAction對象。TabbedDialogAction對象的參數(shù)可以是Ok, Cancel 或Help回調(diào)函數(shù)。在這個函數(shù)中,我們使用的是OK。

22) 現(xiàn)在剩下的就是確定回調(diào)函數(shù)的內(nèi)容,也就是ONOK的內(nèi)容。前面已經(jīng)說過了,我們只要設置AsdkClass1的static成員,也就是設置tb_DivisionManager 和tb_EmployeeDivision編輯框中的值。下面是代碼:

????? public void OnOk()

????? {

???????? AsdkClass1.sDivisionDefault= tb_EmployeeDivision.Text;

???????? AsdkClass1.sDivisionManager= tb_DivisionManager.Text;

????? }

?

編譯、裝載并選擇AutoCAD的選項菜單項來看一下我們的自定義對話框。試試設置對話框中的值并實例化一個雇員。你可以使用PRINTOUTEMPLOYEE命令來查看詳細信息。

?

附加的問題:怎樣讓對話框的編輯框能自動顯示為AsdkClass1中的Manager和Division字符串的內(nèi)容?

?

?


Autodesk官方最新的.NET教程(七)(C#版)

?

第7章 事件

本章將討論AutoCAD中的事件。我們將介紹事件處理函數(shù)的使用,特別是監(jiān)視AutoCAD命令的事件處理函數(shù)和監(jiān)視被AutoCAD命令修改的對象的事件處理函數(shù)。在解釋怎樣在C#中實現(xiàn)AutoCAD的事件處理之前,我們將首先簡要地討論一下.NET中的事件。

第一部分  C#中的事件

事件只是用來通知一個行為已經(jīng)發(fā)生的信息。在ObjectARX中,我們使用反應器(reactor)來處理AutoCAD的事件。而在AutoCAD .NET API中,ObjectARX反應器被換成了事件。

事件處理函數(shù)(或者叫回調(diào)函數(shù))是用來監(jiān)視和反饋程序中出現(xiàn)的事件。事件可以以不同的形式出現(xiàn)。

在介紹AutoCAD .NET API中的事件之前,讓我們先來簡單地了解一下代理。

第1a部分  代理

代理是一個存儲方法索引的類(概念與函數(shù)指針類似)。代理對方法是類型安全的(與C中的函數(shù)指針類似)。代理有特定的形式和返回類型。代理可以封裝符合這種特定形式的任何方法。

代理的一個用途就是作為產(chǎn)生事件的類的分發(fā)器。事件是.NET環(huán)境中第一級別的對象。雖然C#把事件處理的許多細節(jié)給隱藏掉了,但事件總是由代理來實現(xiàn)的。事件代理可以多次調(diào)用(就是它們可以存儲多于1個的事件處理方法的索引)。它們保存了用于事件的一個注冊事件處理的列表。一個典型的代理有以下的形式:

public delegate Event (Object sender, EventArgs e)

第一個參數(shù)sender表示引發(fā)事件的對象。第二個參數(shù)e是一個EventArgs參數(shù)(或者是一個派生的類),這個對象通常包含用于事件處理函數(shù)的數(shù)據(jù)。

第1b部分 +=和-=語句

要使用事件處理函數(shù),我們必須把它與事件聯(lián)系起來。這要通過使用+=語句。+=和-=允許你在運行時連接、斷開或修改與事件聯(lián)系的處理函數(shù)。

當我們使用+=語句時,我們要確定事件引發(fā)者的名字,并要使用new語句來確定事件處理函數(shù),例如:

MyClass1.AnEvent += new? HandlerDelegate(EHandler)

前面我們說過要使用-=語句從事件處理函數(shù)中斷開事件(移除聯(lián)系)。語法如下所示:

MyClass1.AnEvent -= new? HandlerDelegate(EHandler)

第2部分  處理.NET中的AutoCAD事件

在ObjectARX中,我們使用反應器來封裝AutoCAD事件。在AutoCAD .NET API中,我們可以使用事件來代替ObjectARX反應器。

通常,處理AutoCAD事件的步驟如下:

1.?????? 創(chuàng)建事件處理函數(shù)

當一個事件發(fā)生時,事件處理函數(shù)(或稱為回調(diào)函數(shù))被調(diào)用。任何我們想要處理的回應AutoCAD事件的動作都在事件處理函數(shù)中進行。

例如,假定我們只想通知用戶一個AutoCAD對象已被加入。我們可以使用AutoCAD數(shù)據(jù)庫事件”O(jiān)bjectAppended”來完成。我們可以編寫回調(diào)函數(shù)(事件處理函數(shù))如下:

????? public void objAppended(object o, ObjectEventArgs e)

????? {

???????? // 在這里加入處理代碼

????? }

函數(shù)中的第一個參數(shù)代表AutoCAD數(shù)據(jù)庫。第二個參數(shù)代表ObjectEventArgs類,它可能包含對處理函數(shù)有用的數(shù)據(jù)。

2.?????? 把事件處理函數(shù)與事件聯(lián)系起來

為了開始監(jiān)視動作,我們必須把事件處理函數(shù)與事件聯(lián)系起來。在這里,當一個對象加入到數(shù)據(jù)庫時,ObjectAppended事件將會發(fā)生。但是,事件處理函數(shù)不會響應這個事件,除非我們把它與這個事件聯(lián)系起來,例如:

???????? Database db;?

???????? db = HostApplicationServices.WorkingDatabase;

???????? db. ObjectAppended += new ObjectEventHandler(objAppended);

3.?????? 斷開事件處理函數(shù)

要終止監(jiān)視一個動作,我們必須斷開事件處理函數(shù)與事件的聯(lián)系。當對象被加入時,我們想要停止通知用戶這個事件,我們要斷開事件處理函數(shù)與事件ObjectAppended的聯(lián)系。

db. ObjectAppended -= new ObjectEventHandler(objAppended);

?第3部分  使用事件處理函數(shù)來控制AutoCAD的行為

本章的目的是解釋AutoCAD事件怎樣才能被用于控制AutoCAD圖形中的行為?,F(xiàn)在,讓我們使用前一章(第六章)的內(nèi)容在AutoCAD圖形中創(chuàng)建幾個EMPLOYEE塊索引。我們不想讓用戶能改變EMPLOYEE塊索引的位置,而對于其它的非EMPLOYEE塊索引的位置則沒有這個限制。我們將混合使用數(shù)據(jù)庫與文檔事件來做到這一點。

首先,我們想要監(jiān)視將要被執(zhí)行的AutoCAD命令(使用CommandWillStart事件)。特別地,我們要監(jiān)視MOVE命令。另外,當一個對象要被修改時,我們應該被通知(使用ObjectOpenedForModify事件),這樣我們可以確定它是否為一個EMPLOYEE塊索引。如果這時就修改對象可能是無效的,因為我們的修改可能會再次觸發(fā)事件,從而引起不穩(wěn)定的行為。所以,我們要等待Move命令的執(zhí)行結(jié)束(使用CommandEnded事件),這時就可以安全地修改對象了。當然,任何對塊索引的修改將會觸發(fā)ObjectOpenedForModify事件。我們還需要設置一些全局變量來表明一個MOVE命令在運行和被修改的對象是一個EMPLOYEE塊索引。

注意:因為本章需要比較多的代碼來獲得想要的結(jié)果,所以我們不會解釋任何與事件處理無關的代碼,而只是將它們粘貼到事件處理函數(shù)中。這里的重點是成功創(chuàng)建和注冊事件處理函數(shù)。

第一步:創(chuàng)建新工程

我們以第六章的工程開始。請新加入一個類AsdkClass2。我們還要加入四個全局變量。前兩個是Boolean型的:一個用來表示我們監(jiān)視的命令是否是活動的,另外一個用來表示ObjectOpenedForModify事件處理函數(shù)是否該被忽略。

????? //全局變量

????? bool bEditCommand;

????? bool bDoRepositioning;?

接下來,我們要聲明一個全局變量來表示一個ObjectIdCollection,它用來存儲我們所選擇的要修改的對象的ObjectID。

????? ObjectIdCollection changedObjects = new ObjectIdCollection();

最后,我們要聲明一個全局變量來表示一個Point3dCollection,它用來包含我們所選對象的位置(三維點)。

????? Point3dCollection employeePositions = new Point3dCollection();

第2步:創(chuàng)建第一個文檔事件處理函數(shù)(回調(diào)函數(shù))

現(xiàn)在我們要創(chuàng)建一個事件處理函數(shù)。當AutoCAD命令開始執(zhí)行的時候它會通知我們。我們要檢查GlobalCommandName的值是否為MOVE。

???????? if ( e.GlobalCommandName == "MOVE" )

???????? {

???????? }

如果MOVE命令開始執(zhí)行的話,我們要相應地設置Boolean變量bEditCommand的值,這樣我們可以知道我們所監(jiān)視的命令是活動的。同樣地,我們應該把另外一個Boolean變量bDoRepositioning設置為false來忽略ObjectOpenedForModify事件處理函數(shù)。兩個變量設置好以后,在命令活動期間,我們必須要獲得所選塊索引的信息。

我們還應該把兩個集合對象的內(nèi)容清空。我們只關心當前選擇的對象。

第3步: 創(chuàng)建數(shù)據(jù)庫事件處理函數(shù)(回調(diào)函數(shù))

無論什么時候一個對象被打開并要被修改時,數(shù)據(jù)庫事件處理函數(shù)會被調(diào)用。當然,如果這時我們監(jiān)視的命令不是活動的,我們就應該跳過任何被這個回調(diào)函數(shù)調(diào)用的內(nèi)容。

???????? if ( bEditCommand == false )

???????? {

??????????? return;

???????? }

同樣地,如果我們監(jiān)視的命令已經(jīng)結(jié)束,而ObjectOpenedForModify事件被另一個回調(diào)函數(shù)再次觸發(fā)的話,而這時有對象被修改時,我們要阻止所有由這個回調(diào)函數(shù)執(zhí)行的動作。

???????? if ( bDoRepositioning == true )

???????? {

??????????? return;

???????? }

這個回調(diào)函數(shù)剩余部分的代碼用來驗證我們是否正在處理EMPLOYEE塊索引。如果是的話,我們就獲取它的ObjectID和位置(三維點)。下面的代碼可以被粘貼到這個事件處理函數(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)建第二個文檔事件處理函數(shù)(回調(diào)函數(shù))

當一個命令結(jié)束時,第三個事件處理函數(shù)被調(diào)用。同樣地,我們要檢查全局變量來驗證這個將要結(jié)束的命令是我們監(jiān)視的命令。如果是我們監(jiān)視的,那么我們要重置這個變量:

???????? if ( bEditCommand == false )

???????? {

??????????? return;

???????? }

???????? bEditCommand = false;

這個回調(diào)函數(shù)執(zhí)行的動作將會再次觸發(fā)ObjectOpenedForModify事件。我們必須確定在這個回調(diào)函數(shù)中跳過了所有與此事件有關的動作。

???????? //設置標志來跳過OpenedForModify處理函數(shù)

???????? bDoRepositioning = true;

這個回調(diào)函數(shù)的剩余代碼用來把EMPLOYEE塊索引和它的關聯(lián)屬性引用的當前(修改過的)位置與它們的初始位置作比較。如果位置改變了,我們在這個回調(diào)函數(shù)中把它們重置這初始的位置。下面的代碼可以被粘貼到這個事件處理函數(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)建命令來注冊/斷開事件處理函數(shù)

創(chuàng)建一個ADDEVENTS命令,使用+=語句來把上面的3個事件處理函數(shù)連接到各自的事件。在這個命令中,我們還應該設置全局Boolean變量:

???????? bEditCommand = false;

???????? bDoRepositioning = false;

創(chuàng)建另外一個命令REMOVEEVENTS,使用-=語句把事件處理函數(shù)與事件斷開。

第6步: 測試工程

要測試這個工程,請使用CREATE命令創(chuàng)建一個或多個EMPLOYEE塊索引。如果你要作比較的話,你也可以插入一些非EMPLOYEE的塊索引。

在命令行中鍵入ADDEVENTS命令來執(zhí)行它。

在命令行中輸入MOVE命令,然后選擇你想要的塊索引。注意,當MOVE命令結(jié)束時,EMPLOYEE塊索引(包括屬性)還留在原處。

執(zhí)行REMOVEEVENTS命令,然后在試一下MOVE命令。注意,EMPLOYEE塊索引現(xiàn)在可以被移動了。

附加的問題:添加一個附加的回調(diào)函數(shù),當用戶改變EMPLOYEE塊索引的”Name”屬性時,這個回調(diào)函數(shù)被觸發(fā)。?



總結(jié)

以上是生活随笔為你收集整理的在.NET中进行AutoCAD二次开发(C#+ObjectArx) (二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。