高级会员
注册日期: 06-11
帖子: 14579
精华: 1
现金: 224494 标准币
资产: 234494 标准币
|
【转帖】objectarx&dummies教程(十二)—— deriving from acdbobject
objectarx&dummies教程(十二)—— deriving from acdbobject
objectarx&dummies教程(十二)—— deriving from acdbobject
class 12 - deriving from acdbobject
introduction
i will start with custom objects and then proceed with custom entities on the next class. custom objects can be used for several purposes and they are very powerful. once your application creates and manage a custom object you will be able to construct complex application structures as well as much more intelligent and efficient data storage.
starting with a simple example, suppose that you will need to build an objectarx application which implements some bars that has a length property and several types of shapes. is possible that more than one bar has the same shape and it would be nice if you can provide a single instance of shape's information and share it among all bars using this shape.
the first impulse is to repeat the information on each bar no matter you will duplicate information. this works but will generate additional problems beyond the first problem which is the unnecessary space used to store the same information. suppose that you need to update the shape and you would affect all bars that are using this shape. if your information is repeated in all bars you will need to open each bar and update its information. in other hand, if shape's information is stored into one single place and bars reflect this information you will need only to update this information in one place and all bars using this shape will be updated as soon as you update the shape's information.
autocad use this technique on several features like layers, text styles, groups, etc. you will use exactly a custom object to store this information and share it, through its objectid, among all "clients" of this object.
how to begin
as discussed on previous class, you will need to derive from acdbobject to be able to build your own custom object. this can be easily done using the arxwizard or you can do by yourself creating the class by hand.
after create the class you need build some methods to create, store and acquire those objects instances from its container. well, but where you should store your custom objects? autocad provides a general purpose container called named object dictionary (nod). nod is capable to store and persist any custom object derived from acdbobject. it uses a dictionary like storage structure where you put a unique key (at the same level) and an object instance through its pointer and objectid. there are other custom object containers like extension dictionary that i will avoid due our course audience.
the nod container could (and should) be organized by folders to make your dictionary as much organized as you can. the first node should be your application name to avoid conflict with other third-party objectarx applications that could use the nod at the same time as you. the second level should contain all your business groups of objects. this will really depend on how many and the number of custom object types you have. nod does not prohibit you to stored different classes at the same level but i really recommend you to avoid this except in case you need to stored generic objects together like on a preferences group of objects.
you don't need to always open and close the nod and go deep to find where are your desired object every time you need to access it. you can build some kind of cache of the most used objects through its objectid and manage this cache to be updated for every single opened drawing. remember that nod is part of acdbdatabase object and it is per document. so, you need to care about to build and fill your dictionary for every brand new drawing.
how to persist your custom objects
as i said before, the most used place to store custom objects is the nod which is an acdbdictionary. nod takes care of its child objects because it is a container. so, when the acdbdatabase object is issued to save its data by autocad it also pass this message to its child objects and nod is one of them. once nod receives this message it walks through its structure and call dwgoutfields() for every object stored there. the same process occurs when you open the drawing and the dwginfields() is called by acdbdatabase on nod and consequently on its children. exactly due that you will need to override the dwg filling methods to make possible to persist your custom objects among dwg open/close sessions.
essential functions to override in your custom object class are:
virtual acad::errorstatus dwginfields(acdbdwgfiler* filer);
virtual acad::errorstatus dwgoutfields(acdbdwgfiler* filer) const;
virtual acad::errorstatus dxfinfields(acdbdxffiler* filer);
virtual acad::errorstatus dxfoutfields(acdbdxffiler* filer) const;
if you don't plan to support dxf interface to your custom object you could avoid them.
object's state management
on class 5 we have talked about object states when opening objects. inside your custom object class you need to pay attention to call the proper assert method to make sure that all proper events and processes are fired when your object's state has changed. this is very important!
those functions who change your object's data state must first call the assertwriteenabled() function and then apply the required modifications. functions who only read information from your object and does not affect its data state must call assertreadenabled() function and also i really recommend that you make all these as const functions. this will avoid you to accidentally change the object's state when it is opened for read what will cause an assert error message. if you forget to call the proper assert method strange things may occur like call undo and your object stay unchanged and a lot other bizarre things.
how to create a custom object
to implement your custom object you will need to do the following:
1- derive from acdbobject;
2- implement your data;
3- implement access functions (read/write) with proper assert calls;
4- implement the filling methods persisting and reading your data;
as a baseline, i will present a short example here:
// -------------------------------------------
// class declaration
// -------------------------------------------
class myclass : public acdbobject {
public:
acrx_declare_members(myclass);
myclass() {};
virtual ~myclass() {};
acad::errorstatus getval (int& val) const;
acad::errorstatus setval (int val);
acad::errorstatus getstring (cstring& str) const;
acad::errorstatus setstring (lpctstr str);
virtual acad::errorstatus dwginfields(acdbdwgfiler*);
virtual acad::errorstatus dwgoutfields(acdbdwgfiler*) const;
private:
int m_val;
cstring m_str;
};
// -------------------------------------------
// class definition
// -------------------------------------------
acrx_dxf_define_members(myclass,
acdbobject, acdb::kdhl_current,
acdb::kmreleasecurrent, 0, myclass, mysamp);
// -------------------------------------------
acad::errorstatus myclass::getval (int& val) const {
assertreadenabled();
val = m_val;
return acad::eok;
}
// -------------------------------------------
acad::errorstatus myclass::setval (int val) {
assertwriteenabled();
m_val = val;
return acad::eok;
}
// -------------------------------------------
acad::errorstatus myclass::getstring (cstring& str) const {
assertreadenabled();
str.format("%s",m_str);
return acad::eok;
}
// -------------------------------------------
acad::errorstatus myclass::setstring (lpctstr str) {
assertwriteenabled();
m_str.format("%s",str);
return acad::eok;
}
// -------------------------------------------
acad::errorstatus myclass::dwginfields(acdbdwgfiler* pfiler) {
assertwriteenabled();
acdbobject::dwginfields(pfiler);
adesk::int16 _val = 0;
pfiler->readint16(&_val);
m_val = _val;
char* _temp = null;
pfiler->readstring(&_temp);
m_str.format("%s",_temp);
acutdelstring(_temp);
return pfiler->filerstatus();
}
// -------------------------------------------
acad::errorstatus myclass::dwgoutfields(acdbdwgfiler* pfiler) const {
assertreadenabled();
acdbobject::dwgoutfields(pfiler);
pfiler->writeint16(m_val);
pfiler->writestring(static_cast<const char*>(m_str));
return pfiler->filerstatus();
}
// -------------------------------------------
// -------------------------------------------
// entry point
// -------------------------------------------
acrx::appretcode acrxentrypoint(acrx::appmsgcode msg, void* appid) {
switch (msg) {
case acrx::kinitappmsg:
acrxdynamiclinker->unlockapplication(appid);
acrxdynamiclinker->registerappmdiaware(appid);
myclass::rxinit();
acrxbuildclasshierarchy();
break;
case acrx::kunloadappmsg:
deleteacrxclass(myclass::desc());
break;
}
return acrx::kretok;
}
how to create and store your custom object
the nod container is based on acdbdictionary class which has several methods to read, write and erase entries. your application needs to take care of nod entries and be responsible to create instances of your custom class and store these objects inside the nod. each object stored must have a key defined or a generic key using the star * as its name.
void createmyobjects() {
acdbdictionary *pnamedobj = null;
acdbhostapplicationservices()->workingdatabase()->
getnamedobjectsdictionary(pnamedobj, acdb::kforwrite);
acdbdictionary *pdict = null;
if (pnamedobj->getat("mydict",(acdbobject*&) pdict,
acdb::kforwrite) == acad::ekeynotfound) {
pdict = new acdbdictionary;
acdbobjectid dictid;
pnamedobj->setat("mydict", pdict, dictid);
}
pnamedobj->close();
if (pdict) {
myclass *pobj1 = new myclass();
pobj1->setval(1);
pobj1->setstring("string1");
myclass *pobj2 = new myclass();
pobj2->setval(2);
pobj2->setstring("string2");
acdbobjectid rid1, rid2;
pdict->setat("*m",pobj1, rid1);
pdict->setat("*m",pobj2, rid2);
pobj1->close();
pobj2->close();
pdict->close();
}
}
how to verify if my objects are really stored inside nod?
you will need to iterate the nod entries to find your dictionary and then perform an iteration over its entries. the process should be something like this:
void listmyobjects() {
acdbdictionary *pnamedobj = null;
acdbhostapplicationservices()->workingdatabase()
->getnamedobjectsdictionary(pnamedobj, acdb::kforread);
acdbdictionary *pdict = null;
pnamedobj->getat("mydict", (acdbobject*&)pdict,acdb::kforread);
pnamedobj->close();
if (pdict == null) {
acutprintf("\nthe dictionary mydict does not exist. please create it first!");
return;
}
acdbdictionaryiterator* pdictiter= pdict->newiterator();
myclass *pmyclass;
int _val;
cstring _str;
for (; !pdictiter->done(); pdictiter->next()) {
pmyclass = null;
pdictiter->getobject((acdbobject*&)pmyclass,acdb::kforread);
if (pmyclass != null) {
pmyclass->getval(_val);
pmyclass->getstring(_str);
pmyclass->close();
acutprintf("\nmyclass: val=%d, str=%s",_val,_str);
}
}
delete pdictiter;
pdict->close();
}
stay tuned for the next lab which will require you to build a custom object. see you there!
|