1. 程式人生 > >CAD .NET開發 教程(C#)——第四章

CAD .NET開發 教程(C#)——第四章

4資料庫基礎2:新增自定義資料

在這一章中,我們將建立一個新的字典物件,它用來表示我們僱員就職的‘Acme公司(呵呵,當然是虛構的一家公司)的部門。這個部門字典物件將包含一個表示部門經理的記錄。我們還會加入程式碼到僱員建立過程,這個過程會加入一個索引到僱員工作的部門。

我們要說明的是如何在DWG檔案中建立自定義資料,包括每個圖形的自定義資料和個實體的自定義資料。每個圖形的自定義資料是指只在整個圖形中加入一次的資料,它表示物件可以引用的單一型別或特性。每個實體的自定義資料是指是為特定的物件或資料庫中的實體加入的資料。

在下面的示例中,我們將加入每個圖形的自定義資料到命名物件字典

(簡稱NOD)NOD存在於每一個DWG檔案中。每個實體的自定義資料加入到一個名為擴充套件字典的字典(可選)中,它表示每一個僱員。每一個由DBObject派生的物件都擁有儲存自定義資料的擴充套件字典。而在我們的示例中將包含這種自定義資料如名字、薪水和部門。

因此這一章的重點是字典物件和擴充套件記錄(XRecord),它們是我們用來表示自定義資料的容器。

首先讓我們來建立表示公司的條目。在本章的前幾個步驟中,我們將建立如下所示的部門層次結構:

NOD-命名物件字典

ACME_DIVISION-自定義公司字典

銷售(Sales)-部門字典

部門經理-部門條目

請開啟Lab4資料夾下的Lab4工程,或接著

Lab3的程式碼。

<!--[if!supportLists]-->1)

我們首先要做的是定

義一個新的函式,它用來在命名物件字典(NOD)中建立公司字典物件。為這個函式取名為CreateDivision(),,並使用命令屬性來定義CREATEDIVISION命令。

下面是這個函式的程式碼,它的形式非常簡單,只是用來在NOD中建立一個ACME_DIVISION(用來表示公司)

[CommandMethod("CreateDivision")]publicvoidCreateDivision()

{Databasedb=HostApplicationServices.WorkingDatabase;

Transactiontrans=db.TransactionManager.StartTransaction();

try

{//首先,獲取NOD……

DBDictionaryNOD=

(DBDictionary)trans.GetObject(db.NamedObjectsDictionaryId,OpenMode.ForWrite);//定義一個公司級別的字典

DBDictionaryacmeDict;try

{//如果ACME_DIVISION不存在,則轉到catch塊,這裡什麼也不做acmeDict=

(DBDictionary)trans.GetObject(NOD.GetAt("ACME_DIVISION"),OpenMode.ForRead);}catch{

//如果ACME_DIVISION不存在,則建立它並把它加入到NOD……acmeDict=newDBDictionary();

NOD.SetAt("ACME_DIVISION",acmeDict);trans.AddNewlyCreatedDBObject(acmeDict,true);

}trans.Commit();

}finally

{trans.Dispose();

}

}

請仔細閱讀一下上面的程式碼塊的結構,可以通過註釋來了解相關的細節。特別要注意的是我們是如何用一個try-catch塊來處理ACME_DIVISION是否存在?如果ACME_DIVISION字典不存在,GetObject()將會丟擲異常,catch塊被執行,它會建立一個新的字典。

執行這個函式來看它是否可行。可以使用資料庫檢視工具來檢查字典是否已被加入(建議使ARXSDKArxDbg工具)

<!--[if!supportLists]-->2)

接下來,我們要在

ACME_DIVISION字典中加入銷售(Sales)條目。銷售(Sales)條目同樣也是一個字典。由於銷(Sales)字典與ACME_DIVISION字典的關係如同ACME_DIVISION字典與NOD,所以程式碼是類似的。定義下面的程式碼部分在ACME_DIVISION字典中建立一個名為’Sales’的字典。程式碼提示:

DBDictionarydivDict;

try

{divDict=(DBDictionary)trans.GetObject(acmeDict.GetAt("Sales"),

OpenMode.ForWrite);

}catch

執行函式來看‘Sales’條目是否已加入到ACME_DIVISION字典。<!--[if!supportLists]-->3)

現在我們要在這個字典中加入一個特殊的記錄,它可以包含任意的自定義資料。我們要加入的資料型別為擴充套件記錄(XRecord),它可以包含任何東西,因此我們可以讓它包含ResultBuffer類的物件(就是有些人可能非常熟悉的‘resbuf’)。ResultBuffer可以儲存不同型別的預定義資料。擴充套件記錄儲存任意數目的ResultBuffer關係列表,因此可能會很大。下表是可以包含在ResultBuffer中一些資料型別(位於Database類的DxfCode列舉中):

在下面的程式碼部分,我們將建立只包含一個ResultBuffer的擴充套件記錄。這個ResultBuffer包含一個單一的的字串值,它表示’Sales’部門的部門經理的名字。我們使用和加入字典一樣的方法加入擴充套件記錄。唯一的區別是擴充套件記錄與字典的不同:

mgrXRec=newXrecord();mgrXRec.Data=newResultBuffer(newTypedValue((int)DxfCode.Text,"RandolphP.

Brokwell"));

請看一下我們是怎樣使用new來建立一個新的擴充套件記錄。但我們也使用了new來建立一ResultBuffer,傳入的引數是一個名為‘TypedValue’的物件。‘TypedValue’物件和C++resbuf的成員‘restype’是類似的。這個物件一般表示一個特定型別的DXF值,我們使用它來組裝諸如擴充套件資料或擴充套件記錄之類的通用資料容器。在這裡,我們簡單地使用DxfCode.Text鍵值和“RandolphP.Brokwell”資料值來定義一個TypedValue,然後把它作為單一的引數傳入ResultBuffer建構函式(new來呼叫)中。

XRecord’Data’屬性實際上正是擴充套件記錄鏈的第一個ResultBuffer,我們使用它來表示擴充套件記錄鏈是從什麼地方開始的。

所以接下來的程式碼塊看起來和前面兩個非常相似:XrecordmgrXRec;

try

{

mgrXRec=

(Xrecord)trans.GetObject(divDict.GetAt("DepartmentManager"),OpenMode.ForWrite);

}catch{

mgrXRec=newXrecord();mgrXRec.Data=newResultBuffer(new

TypedValue((int)DxfCode.Text,"RandolphP.Brokwell"));

divDict.SetAt("DepartmentManager",mgrXRec);trans.AddNewlyCreatedDBObject(mgrXRec,true);

}

執行函式並使用資料庫檢視工具來確定部門經理已被加入到’Sales’字典。

4)我們已經定義了公司字典,現在我們要把每個僱員的資料加入到前一章定義的塊索引中。我們要加入的資料是:名字、薪水和僱員所屬的部門。要加入這些資料,我們要同前幾個步驟一樣使用擴充套件記錄。因為我們要加入三個條目,所以我們要使擴充套件記錄可以把這些數據聯絡在一起。

一般來說,擴充套件記錄只能存在於字典中。而我們要為每個僱員加入這些資料(就是本章開頭所講的每個圖形的自定義資料和每個實體的自定義資料),那應該怎麼做呢?答案就是:每一個物件或AutoCAD中的實體實際上都有一個名為擴充套件字典(ExtensionDictionary的可選字典。我們可以把擴充套件記錄直接加入到這個字典中。

請回到我們在上一章建立的CreateEmployee()函式。這個函式是我們建立塊索引的地方。讓我們像前面的步驟一樣來建立一個新的擴充套件記錄。因為我們要加入3個條目,因此我們既可以使用ResultBufferAdd方法(它會在擴充套件記錄鏈中加入一個連結),也可以利用ResultBuffer的建構函式(它的一種建構函式可以輸入可變數量的引數)。

無論用哪一種方法,請在CreateEmployee()函式中使用ResultBuffer來建立一個新的XRecordResultBuffer包括以下的型別和值:

Text“EarnestShackleton”(或是你選擇的其它僱員的名字)

Real72000           或者更多的薪水J

Text“Sales”           僱員所在的部門

5)要把上面的擴充套件記錄加入到塊索引,我們必須把它加入到擴充套件字典。通常這個字典是不存在的,除非它被明確地建立,塊索引就是這種情況。要給一個物件建立擴充套件字典,你要呼叫它的成員‘CreateExtensionDictionary()’。這個函式不返回任何值,所以要訪問它建立的擴充套件字典,你還得使用物件的‘ExtensionDictionary’屬性。你可以使用類似於以下的程式碼來建立並訪問擴充套件字典:

br.CreateExtensionDictionary();

DBDictionarybrExtDict=

(DBDictionary)trans.GetObject(br.ExtensionDictionary,OpenMode.ForWrite,false);

由於擴充套件字典也是字典,我們可以和第3步一樣在擴充套件字典中加入擴充套件記錄。請完成有關的程式碼來建立和訪問塊索引的擴充套件字典,加入你在第4步中建立的擴充套件記錄,然後把擴充套件記錄加入到事務處理。

6)返回到NOD……因為在NOD中建立公司字典只需要一次(就像建立Employee塊一樣),因此我們應該把CreateDivision函式的命令屬性去掉,而在CreateEmployeeDefinition()中呼叫這個函式。請自己完成這些改變。當所有這些都做完後,當CREATE命令第一次執行的時候,所有的函式都會被呼叫。

7)下面的步驟和上面的無關。我們將建立一個函式來遍歷模型空間,以用來查詢加入的Employee物件(這裡其實是塊索引)的數目。在VB.NETC#中,我們可以把模型空間塊表記錄(ModelSpaceBlockTableRecord)當作一個集合,這樣就可以使用ForEach(C#foreach)來遍歷它。請仔細研究一下下面的程式碼片斷:

VB.NET:

DimidAsObjectId首先,定義一個For迴圈要使用的ObjectId變數。ForEachidInbtr

DimentAsEntity=trans.GetObject(id,OpenMode.ForRead,False)'開啟當前的物件!

C#:

foreach(ObjectIdidinbtr)

{

Entityent=(Entity)trans.GetObject(id,OpenMode.ForRead,false);//開啟當前的物件!

一旦我們獲得模型空間物件,你們就可以定義一個ObjectId變數,然後把它用於ForEach迴圈(C#foreach)

現在,我們需要使用一些方法來篩選僱員。我們知道模型空間中的物件都是實體,但不全是僱員。我們需要使用一些方法來加以區分。在這裡,我們可以使用VB.NETTypeOf關鍵字並用CType進行型別轉換(C#GetType函式和typeof):

VB.NET:

IfTypeOfentIsBlockReferenceThen

DimbrAsBlockReference=CType(ent,BlockReference)

C#:

If(ent.GetType()==typeof(BlockReference))

BlockReferencebr=(BlockReference)ent;

上面講的概念對於AutoCAD程式設計是很重要的,因為容器物件經常包含不同型別的物件。你會AutoCAD程式的開發中經常碰到這種型別轉化。

請定義一個名為EmployeeCount()的函式,函式的結構如上所示,它用來統計模型空間中的塊索引的數目。這個函式不會輸出任何東西,但你可以使用逐步除錯程式來檢視整數變數的增加(每發現一個塊索引物件)。

8)接下來,為了把結果輸出到命令列,我們需要使用

Application.DocumentManager.MdiActiveDocument.Editor物件的服務。要使用它,請加入下面的程式碼:

ImportsAutodesk.AutoCAD.EditorInput

ImportsAutodesk.AutoCAD.ApplicationServices

在函式的內部:

Editored=Application.DocumentManager.MdiActiveDocument.Editor;

最後,在迴圈的後面確定找到了多少個塊索引:ed.WriteMessage("EmployeesFound:"+nEmployeeCount.ToString());

第四章結束

下面的程式碼片斷演示了怎樣獲取Employee物件的所有內容,包括ACME_DIVISION字典中的部門經理的名字。這部分要在後面的章節中使用,但因為它和本章有關,因此我們把它放在本章作介紹。如果有時間的話,請閱讀一下其中的程式碼來看看它是怎麼使用的。它可以被直接放到你的類中並可以執行。命令的名字是PRINTOUTEMPLOYEEListEmployee()函式接收一個ObjectId引數,它通過一個ref型別的字串陣列返回值(包含相應的僱員資料)。呼叫它的PrintoutEmployee()函式只是用來在命令列中輸出這些資料。

我們需要一個遍歷並顯示所有僱員資料的命令。publicstaticvoidListEmployee(ObjectIdemployeeId,refstring[]saEmployeeList)

{intnEmployeeDataCount=0;

Databasedb=HostApplicationServices.WorkingDatabase;

Transactiontrans=db.TransactionManager.StartTransaction();//開始事務處理。

try

{

Entityent=(Entity)trans.GetObject(employeeId,OpenMode.ForRead,false);//開啟當前物件!

if(ent.GetType()==typeof(BlockReference))

{

//不是所有的塊索引都有僱員資料,所以我們要處理錯誤boolbHasOurDict=true;

XrecordEmployeeXRec=null;try

{

BlockReferencebr=(BlockReference)ent;

DBDictionaryextDict=

(DBDictionary)trans.GetObject(br.ExtensionDictionary,OpenMode.ForRead,false);

EmployeeXRec=

(Xrecord)trans.GetObject(extDict.GetAt("EmployeeData"),OpenMode.ForRead,false);

}catch

{bHasOurDict=false;//出現了錯誤……字典或擴充套件記錄不能訪問}

if(bHasOurDict)//如果獲得擴充套件字典,而又有擴充套件記錄……

{

//為僱員列表分配記憶體saEmployeeList=newString[4];

//加入僱員的名字

TypedValueresBuf=EmployeeXRec.Data.AsArray()[0];saEmployeeList.SetValue(string.Format("{0}\n",

resBuf.Value),nEmployeeDataCount);

nEmployeeDataCount+=1;

//加入僱員的薪水resBuf=EmployeeXRec.Data.AsArray()[1];saEmployeeList.SetValue(string.Format("{0}\n",

resBuf.Value),nEmployeeDataCount);

nEmployeeDataCount+=1;

//加入僱員所在的部門resBuf=EmployeeXRec.Data.AsArray()[2];stringstr=(string)resBuf.Value;saEmployeeList.SetValue(string.Format("{0}\n",

resBuf.Value),nEmployeeDataCount);

nEmployeeDataCount+=1;

//現在,讓我們從公司字典中獲取老闆的名字//NOD中找到.

DBDictionaryNOD=

(DBDictionary)trans.GetObject(db.NamedObjectsDictionaryId,OpenMode.ForRead,false);

DBDictionaryacmeDict=

(DBDictionary)trans.GetObject(NOD.GetAt("ACME_DIVISION"),OpenMode.ForRead);

//注意我們直接使用擴充套件資料...

DBDictionarysalesDict=

(DBDictionary)trans.GetObject(acmeDict.GetAt((string)EmployeeXRec.Data.AsArray()[2].Value),OpenMode.ForRead);

XrecordsalesXRec=

(Xrecord)trans.GetObject(salesDict.GetAt("DepartmentManager"),OpenMode.ForRead);

//最後,把僱員的資料輸出到命令列resBuf=salesXRec.Data.AsArray()[0];

saEmployeeList.SetValue(string.Format("{0}\n",resBuf.Value),nEmployeeDataCount);

nEmployeeDataCount+=1;

}

}trans.Commit();

}finally

{trans.Dispose();

}

}

[CommandMethod("PRINTOUTEMPLOYEE")]publicstaticvoidPrintoutEmployee(){

Editored=Application.DocumentManager.MdiActiveDocument.Editor;//宣告我們將在下面使用的工具...

Databasedb=HostApplicationServices.WorkingDatabase;Transactiontrans=db.TransactionManager.StartTransaction();try

{//首先,獲取塊表和模型空間塊表記錄

BlockTablebt=

(BlockTable)trans.GetObject(HostApplicationServices.WorkingDatabase.BlockTableId,OpenMode.ForRead);

BlockTableRecordbtr=

(BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace],OpenMode.ForRead);

//現在,我們需要把內容輸出到命令列。這裡可以有一個物件幫助我們://下面的部分,我們將遍歷模型空間:

foreach(ObjectIdidinbtr){

Entityent=(Entity)trans.GetObject(id,OpenMode.ForRead,false);//開啟當前物件!

if(entisBlockReference)

{string[]saEmployeeList=null;//這是正確的...定義

新的列表。

ListEmployee(id,refsaEmployeeList);

if((saEmployeeList.Length==4))

{

ed.WriteMessage("EmployeeName:{0}",saEmployeeList[0]);ed.WriteMessage("EmployeeSalary:{0}",saEmployeeList[1]);ed.WriteMessage("EmployeeDivision:{0}",saEmployeeList[2]);ed.WriteMessage("DivisionManager:{0}",saEmployeeList[3]);

}

finally{

}

}