// acdb definitions
#include // ads defs
#include i/ unlock applicationDCL (Dialog Control Language) Dialogs 365
ee
finclude
#include // Symbol Tables
H#include // DCL style dialogs
finclude // asDblArray
finclude //
acdbHostAppl icationServices()
// TODO: add your own includes
#include “RectWindow.h” // RectWindow class
finclude “ArchWindow.h” // ArchWindow class
finclude “ApexWindow.h” // ApexWindow Class
// Function prototypes
// entry point for this application
extern “C” AcRx::AppRetCode acrxEntryPoint(
ACRx::AppMsgCode msg, void* );
// helper functions
void initapp (void);
void unloadapp( void);
J/_user defined functions
void windo();
Adesk: :Boolean
getMode SpaceRecord(AcDbB1ockTableRecord*&e?
pB1kTableRecord) :
// Dialog call back function prototypes
static void CALLB dlgOk&tnCB(ads_callback_packet?
static votd CALLB dgPickPtBtnCB(ads_cal1back_packet
Static’ votd cALLB di gWinTypeRbCB(ads_callback_packeto
esate void CALLB d1gEbHei ghtCB(ads_cal1back_packeto
esate void CALLB dl gEbWidthCB(ads_cal1back_packet?
ctatig void CALLE d1gEbColsCB(ads_cal!back_packet?
Statte vota CALLB di gEbRowsCB(ads_callback_packeto
*cpkt) sstatic void CALLB dIgEbXValCB(ads_callback_packeto
*cpkt);
static void CALLB d1gEbYValCB(ads_callback_packeto
*epkt);
J/ Global variables, place holders for the values
// obtained from the dialog box
double g_winHt = 20.00;
double g_winWidth = 20.00;
int g_Rows = 1;
int g_Cols = 1;
char g_strWinType[10] = (“rb_rect™):
AcGePoint2d g_startPt(0.0, 0.0);
Adesk: :Boolean g_bProcesd = Adesk::kFalse;
MTL LLL
TTT
// acrxentryPoint(interral)
1/ This function is the entry point for youre
application.
MTL TLL LLL TAD
WLLL
AcRx: :AppRetCode acrxEntryPoint (ACRx: :
msg, void* ptr)
i
switch (msg) (
case ACRx::kInitAppisg :
acrxUnTockApplicazion(ptr);
acrxRegisterAppNo:MDIAWare( ptr);
initapp):
break:
case AcRx::kUnToadAapisg :
unloadApp():
break:
case ACRK:
break:
case AcRx::kUnloadDvoMsg :
break;DGL (Dialog Control Language) Dialogs 367
$$$
default:
break;
) 11 switch
return AcRx::kRetOK;
)
void initApp(void)
{
// T0D0: init your application
// register a command with the AutoCADé
command mechanism
acedReg0mds->addConmand(“CH6_APPS”, “WINDO”, <
“wrnno”, ACRX_CMN_MODAL, windo):
acutPrintf (“Enter \"WINDO\" at the commande
Prompt to draw a window. \n”
}
void unloadApp( void)
(
// TODO: clean up your application
// Remove the command group added via
acedRegCmds->addCommand
acutPrintf(“Ss%s", “Goodbye\n", “Removingg
command group \"CH6_APPS\"\n"
acedRegCmds - >removeGroup( “CH6_APPS”) ;
)
// TODO: add your other functions here
void windo()
t
int rez // Return code
char dclFile{TILE_STRLIMIT]; // TILE_STRLIMITS
= 255 in adsdig.h file
char dc1Name[801
int delld, dbStat;
box status
// del 1D and dialog?ads_hdlg digHdl; // Dialog Box handle
AcDbBlockTableRecord *pB1kTableRecord;
RectWindow *pRectWindow; // RectWindow class
ArchWindow *pArchWindow: // Derived ArchWindow?
class
ApexWindow *pApexWindow: // Derived ApexWindowe
class
RectWindow *pGenericWind;
AcGePoint3d pickPt; // Lower left point ofé
the window
char buffer(80]; // Used in the conversion of #
numbers to string
dbstat = 5; // Set an initial value for dbstaté
must be greater than 1
strepy(dclFile, “ch6_3.dc1");
strepy(delName, “ch6
re
if(re
{
acutPrintf (“Bs Bs", “\nError loading Ch6_3.0CLe
file. ", dclFile);
return;
}
ads_load_dialog(dclFile, &dclId);
RTNORM)
while(dbStat >= DLGSTATUS)
fi
// Now that the dialog is loaded, display it
re = ads_new_dialog(dclName, delId, NULLCB,&
adighd1);
if(re t= RTNORM)
(
acutPrintf("\nError displaying dialog. “);
return;
)DCL (Dialog Control Language) Dialogs 369
$$$
// Now that the dialog is visible on the screen
// initialize it.
if(stremp(g_strWinType, “rb_rect") = 0)
(
ads_set_tile(dighd], “rb_rect”, “1");
)
else if(stremp(g_strWinType, “rb_arch”) == 0)
(
ads_set_tile(digHd], “rb_arch", “1");
)
else if(stremp(g_strWinType, “rb_apex”) == 0)
{
ads_set_tile(dlgHdl, “rb_apex”, “1”);
)
else
{
ads_set_tile(dIgHdl, “eb_height”, “20.00”
ads_set_tile(dighd], “eb_width”, “20.00");
}
// Convert the doubles for the height and width
acdbRTOS(g_winHt, 2, 3, buffer);
ads_set_tile(digid!, “eb_height”, buffer);
acdbRTOS(g_winWidth, 2, 3, buffer);
ads_set_tile(digdl, “eb_width”, buffer);
// Convert the integers for the rows and columns
-itoa(g Cols, buffer, 10):
ads_set_tile(digHdi, “eb.
—itoa(g Rows, buffer, 10
ads_set_tile(digHdi, “eb_rows”, buffer);
acdbRT0S((ads_real)g_startPt.x, 2, 3, buffer);
ads_set_tile(dlgHdl, “eb_xval”, buffer);
acdbRT0S((ads_real)g_startPt.y, 2, 3, buffer);
re = ads_set_tile(digidl, “eb_yval", buffer);
ols”, buffer);
ads_action_tile(dighd!, “accept”, digOkBtncB);
ads_action_tile(digid!, “btn_pick",&
dl gPickPtBtncB);
ads_action_tile(digidl, “rb_rect”,
dl gWinTypeRbCB) ;370
ads_action_tile(dighd1, “rb_arch",&
d1gWinTypeRbCB) :
ads_action_tile(dIgHd1, “rb_apex",
d1gWinTypeRbCB) :
ads_action_tile(digid!, “eb_height”.&
di gEbHeightcB)
ads_action_tile(dighd1, “eb_width".&
d1gEbWidthcB);
ads_action_tile(digd1, “eb_cols”, d1gEbColsCB):
ads_action_tile(digHd1, “eb_rows”, d1gEbRowsCB)
ads_action_tile(digd1, “eb_xval", dIgEbXValCB);
ads_action_tile(digHd1, “eb_yval”, dlgEbYValCB);
re = ads_start_dialog(dlgldl, &dbStat);
if(re I= RTNORM)
{
acutPrintf("\nFailed to initialize thee
dialog box. “);
return
)
switch(dbStat)
i
case 4:
acedInitGet(NULL. NULL):
re = acedGetPoint(NULL,
“\nPick lower left window insertion’
point : “, asDblArray(pickPt));
switch(re)
(
case RICAN:
case RTERROR:
return;
case RTNONE:
gistartPt.x = 0;
g_startPt.y =
break;
case RTNORM:DCL (Dialog Control Language) Dialogs 371
gistartPt.x = pickPt.x;
g_startPt.y = pickPt.y;
break;
1 // inner switch
break;
1/1 switch
) 1 white
J/ Unload the dialog box
ads_unload_dialog(dclld);
if (g_bPraceed)
(
J/ What kind of window did the user select?
if(stremp(g_strWinType, “rb_rect") == 0)
i
pRectWindow = new RectWindows
pGenericWind = pRectWindow;
)
else if(stremp(g_strWinType, “rb_arch”) == 0)
{
pArchWindow = new ArchWindow;
pGenericWind = pArchWindow;
)
else if(stremp(g_st-WinType, “rb_apex”) == 0)
(
PApexWindow = new ApexWindows
pGenericWind = pAsexWindow;
)
else
i
return;
)
/1 Fill out the window class object
// with the input values
pGenericWind->setWindowLength(g_winWidth):
pGenericWind->setWindowHeight(g_winHt);
pGenericWind->setWindowCols(g_Cols);pGenericWind->setWindowRows(g_Rows):
pGenericWind->setWindowStartPoint(g_startPt);
if (1getModel SpaceRecord(pB1kTableRecord))
{
return;
}
pGenericWind- >drawWindow(pB1kTableRecord);
if(pGenericWind != NULL)
(
delete pGenericWind;
)
pBIkTableRecord->close();
JI Set the boolean back to false, because
// in the next invocation of the application
// AutoCAD remembers the values of the global
// variables.
g_bProceed = Adesk::kFalses
V// if g_bProceed
else
{
acutPrintf("\nUser cancelled. *
)
)
J/ Dialog Box callback functions
static void CALLB d1g0kBtnCB(ads_callback_packet#
*cpkt)
{
char strWinHt(1001;
char strWinWidtht100];
char strWinRows[20];
char strWinCols(20];
char strWinTypeC10];
char strxPt(20:
char stryPt(20.
int resDOL (Dialog Control Language) Dogs 373
—$—
double winkt, winWidth;
double xPt, yPt;
‘int winRows, winCols:
// Get the window height
re = ads_get_tile(cpkt->dialog, “eb_height”,&
strWinit, 80);
if(re I= RTNORM)
(
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf(“\nError in Height edit box.
return;
}
winlt = atof(strWinkt);
if(wint <= 0.0)
t
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf("\nInvalid value for window Height.&
return;
}
// Get the window width
re = ads_get_tile(cpkt->dialog, “eb_width”,&
strWinWidth, 80);
if(re t= RTNORM)
(
ads_done_dialog(cpk:->dialog, DLGOK);
acutPrintf(“\nError in Width edit box. “);
return;
)
winWidth = atof(strWiaWidth);
if(winWidth <= 0.0)
{
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf("\nInvalid value for window width. &
“);
return;
)
// Get the number of window columns
re = ads_get_tile(cpkt->dialog, “eb_cols”,¢
strWinCols, 20);a4
if(re != RTNORM)
{
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf(“\nError in Columns edit box. “):
return;
)
winCols = atoi(strWinCols);
if(winCols <1)
t
acedAlert(“Invalid value for windows
columns. \nMinimum value = 1");
ads_mode_tile(cpkt->dialog, “eb_cols”,#
MODE_SETFOCUS) ;
)
// Get the number of window rows
re = ads_get_tile(cpkt->dialog, “eb_rows”,¢
strWinRows, 20);
if(re 1= RTNORM)
(
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf("\nError in Rows edit box. “);
return;
d
WinRows = atoi(strWirRows);
if(winRows < 1)
acedAlert(“Invalid value for window
rows. \nMinimum value = 1");
ads_mode_tile(cpkt->dialog, “eb_rows”.
MODE_SETFOCUS) ;
)
// The following ads_get_tile() call will placed
in strWinType
// the key of the radio button that is selectede
‘in other words
// the radio button that has a value of “1”. Thed
key brc_wintype
7/ is @ boxed_radio_column tile that contains®
radio_button tiles.
re = ads_get_tile(cpkt->dialog, “brc_wintype”,
strWinType, 10DGL (Dialog Control Language) Dialogs 378
$$
if(re I= RTNORM)
{
ads_done_dialog(cpkt->dialog, DLGOK):
acutPrintf("\nerror in window type.
return;
)
// Get the Window starting x and y values
ro = ads_get_tile(cpkt->dialog, “eb_xval”,¢
strxPt, 20);
ro = ads_get_tile(cpkt->dialog, “eb_yval”.¢
strYPt, 20);
xPt = atof(strxPt);
yPt = atof(strvPt);
ads_done_dialog(cpkt->dialog, DLGOK):
// Transfer the values obtained to the global
// variables.
Qwinlt = winHt;
gwinWidth = winWidth;
giols = winCols
g_Rows = winRows:
strepy(g_strWinType, strWinType);
gistartPt.set(xPt, yPt);
g_bProceed = Adesk::kTrue:
)
static void CALLB digPickPtatnCB(ads_callback_packet?
*cpkt)
fl
ads_done_dialog(cpkt->dialog, 4);
)
static void CALLB digWinTypeRbCB(ads_callback_packet?
*cpkt)
(376
char strWinType(10]:
int res
// The following ads_get_tile() call will place
in strWinType
// the key of the radio button that is selecteds
in other words
// the radio button that has a value of “1”. Thee
key brc_wintype
// is a boxed_radio_column tile that containsé
radio_button tiles.
rc = ads_get_tile(cpkt->dialog, “brc_wintype".&
strWinType, 10);
if(re RTNORM)
(
ads_done_dialog(cpkt->dialog, DLGOK)
acutPrintf(“\nError in window type.
return;
)
strepy(g_strWinType, strWinType);
)
static void CALLB dlgEbHei ghtCB(ads_callback_packeto
*epkt)
(
int re:
char strWinHtl100];
double winkt;
if(cpkt->reason = CER_LOST_FOCUS)
i
// Copy the value to the global variable
// Get the window height
rc = ads_get_tile(cpkt->dialog, “eb_height”.#
strWinkt, 80);
if(re I= RTNORM)
(
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf(“\n€rror in Height edit box. “);
return:
}DGL (Dialog Control Language) Dialogs 37
een
Winkt = atof(strWinHt);
if(winktt <= 0.0)
i
acedAlert(“Invalid value for window height");
ads_mode_tile(cpkt->dialog, “eb_height”.¢
MoDE_SETFOCUS);
}
else
{
// Copy value ouz to global
gwinlit = winkt;
)
)
)
static void CALLB d1gEbWidthCB(ads_cal1back_packet#
¥cpkt)
(
int rez
char strWinwidth[1007;
double winkidth;
if(cpkt->reason = CBR_LOST_FOCUS)
{
// Copy the value zo the global variable
// Get the window height
rc = ads_get_tile(cpkt->dialog, “eb_width",&
strWinWidth, 80);
if(re 1= RTNORM)
(
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf("\nError in Width edit box.
return;
)
winWidth = atof(strWinWidth);
if(winWidth <= 0.0)
(
acedAlert(“Invalid value for window
ads_mode_tile(cpkt->dialog, “eb_widt!
MODE_SETFOCUS);
y
else
{
dth”);// Copy value out to global
gwinWidth = winWidth;
}
}
}
static void CALLB d1gEbCo1sCB(ads_cal Iback_packet#
*cpkt)
t
int re:
char strWinCols(20);
‘int winCols:
if(cpkt->reason == CBR_LOST_FOCUS)
(
// Copy the value to the global variable
// Get the window height
re = ads_get_tile(cpkt->dialog, “eb_cols".¢
strWinCols, 20):
if(re l= RTNORM)
i
ads_done_dialog(cpkt->dialog, DLGOK):
acutPrintf(“\nError in Columns edit box. “):
return:
)
winCols = atoi(strWinCols);
if(winCols < 1)
{
acedAlert(“Invalid value for window?
columns. \nMimimum = 1°};
ads_mode_tile(cpkt->dialog. “eb_cols”.@
MODE_SETFOCUS);
?
else
{
IJ Copy value ou: to global
gCols = winCols;
)
)
)
static void CALLB dIgEbRowsCB(ads_callback_packeto
*epkt)DOL. (Dialog Control Language) Dielogs 379
a
int res
char strWinRows(20];
int winRows;
if(cpkt->reason == CBR_LOST_FOCUS)
fi
// Copy the value to the global variable
1/ Get the window height
ro = ads_get_tile(cpkt->dialog, “eb_rows”,&
strWinRows, 20);
if(re != RTNORM)
t
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf("\nError in Rows edit box. “);
return:
)
winRows = atoi(strWinRows);
if(winRows <1)
(
acedAlert(“Invalid value for window
rows. \nMiminum = 1");
ads_mode_tile(cpkt->dialog, “eb_rows”,@
MODE_SETFOCUS);
)
else
(
// Copy value out to global
g_Rows = winRows:
)
)
)
static void CALLB dlgEbXValCB(ads_callback_packet®
*epkt).
{
int res
char strXPt(20];
double xPt;
if(cpkt->reason == CBR_LOST_FOCUS)
i
// Copy the value to the global variable
// Get the window heightre = ads_get_tile(cpkt->dialog, “eb_xval”.&
strxPt, 20);
if(re I= RTNORM)
{
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf("\nError in X Value edit box. *
return;
)
xPt = atof(strxPt);
gistartPt.x = xPt;
)
}
static void CALLB dlgEbYValCB(ads_cal lback_packeto
*cpkt)
{
int res
char_strYPt(201;
double yPt:
if(cpkt->reason == CBR_LOST_FOCUS)
t
// Copy the value <0 the global variable
// Get the window height
re = ads_get_tile(cpkt->dialog, “eb_yval",#
strYPt, 20);
if(re = RTNORM)
(
ads_done_dialog(cpkt->dialog, DLGOK);
acutPrintf(“\nError in Y value edit box.
return;
}
yPt = atof(strvPt);
gistartPt.y = yPt:
)
)
// User defined functionsDGL (Dialog Control Language) Dialogs 381
$$
Adesk: :Boolean
getMode! SpaceRecord(AcDbB1ockTableRecord*ke?
pBIkTableRecord)
(
AcDbDatabase *pCurDb;
AcDbBlockTable *p81kTable:
Acad: :ErrorStatus es;
pCurDb = acdbHostApplicationServices()#
—>workingDatabase();
es = pCurDb->getBlockTable(pB1kTable, &
AcDb: :kForRead)
if(es I= Acai
acutPrintf(“\nFailed to open Block Table for ag
read operation.”
return Adesk::kFalse;
)
0k)
es _= pBlkTable->getAt(ACDB_NODEL_SPACE, &
pBlkTableRecord, AcDb: :kForWrite) :
iffes I= Acad::e0k)
(
acutPrintf(“\nFailed to open MODEL SPACE for ag
write operation."):
pB1kTable->close()
return Adesk: :kFal
}
JJ We don’t need the block table anymore so wee
can close it.
pBlkTable->close();
return Adesk::kTrue;
)
‘Wall, the first thing to notice about this application is that there are more callback firnc~
tions than in the previous version of the application. After the declaration ofthe call-
back functions I initialize the global functions.
‘Let’s move on and look at the windo() function: in it | initialize the int variable dbStat
to a value of 5, and then J call ads load_dialog(). We then enter the while loop for
the first time, in which case dbStat has a value of S, which is greater than DLGSTA-TUS. Inside the while loop we make a cal! to ads_new_dialog(), which displays the
dialog on screen, but we do not have control ofthe dialog at this point. We then ini-
tialize the tiles of the dialog, after which we have numerous calls to ads_action_tile()
function. Remember, the purpose of the ads_action_tile() is to associate a callback
function with a tile. Finally, we hand over control to the dialog with a call to
ads_start.dialog(.
Following the ads_start_dialog() is a switch statement with a single case that has a
value of 4. What causes dbStat to have a value of 4? And if t has a value of 4, how
can you call acedGetPoint(), as shown in the code? Affe all, isnt the dialog active?
Thave to admit that it’s not very obvious fiom the code how the dialog is hidden. At
this point, this is our first pass through the while loop and dbStat has a value of 5,
so the ease statement would be bypassed in this case anyway. We will revisit how
dbStat gets a value of 4 in a moment, so please endure.
At this point the dialog is active on the screen, patiently waiting for user input.
Assuming the window type, height, width, columns, and rows have been selected, the
user now selects the < Pick button. What happens? The ads_action_tile() function
hhas associated the < Pick button with the digPickPtBtnCBQ function. Here is the
digPickPeBtnCBQ function listing:
static void CALLB dlgPickPtBtnCB(ads_callback_packet
*cpkt)
(
ads_done_dialog(cpkt->dialog. 4):
)
The sole purpose of the function is to hide the dialog with a single call to
ads_done_dialog). However, here we have a user-defined value of dbStat, which is
4. Now the dialog disappears from the screen, but instead of being terminated, itis
in a temporary suspended state. When we now enter the switch statement, dbStat will
have a value of 4, in which case the user is now asked to select a point. As a result of
selecting a point, the global variable g startPt is assigned the values ofthe point select-
ed. We once again loop through the while loop and redisplay the dialog, followed by
initialization and a fresh call to ads_startdialog(, which will give dbStat a new value
of DLGSTATUS. If the user selects the < Pick button again, the process is repeated.
‘How do we set the dbStat variable to a value of less than DLGSTATUS? Simple: the
user can select either the OK or the Caneel button, which will give dbStat a value of
DLGOK or DLGCANCEL, causing us to pop out of the while loop. If the user
selected the OK button, the digOkBtnCBQ function will be called, which is similar
in nature tothe previous application. This will cause the global variable g bProceed
to be TRUE and a window of the appropriate type will be drawn as a result.DCL (Dialog Control Language) Dialogs 383
$$
So what are the other callback functions and ads_action_tile() functions? Every
‘time we hide the dialog as a result of the < Piek button being selected, the dialog is
redisplayed and initialized. If the user has entered a value in the Hleight edit_box for
‘example, and then the user selects the < Piek button, eventually, when the dialog reap-
pears, the edit_box will contain the value that the user entered prior to selecting the
‘< Pick button.
Let's look at some of these functions in det. Ifthe user selects one of the radio_but-
tons, the callback function dlgWinTypeRbCBQ is executed. The purpose of the
fanction is to get the key of the radio_button selected and copy to the global variable
&.strWinType the value of the local variable steWinType.
Now let’s look at the callback for the Height edit box. Here is the listing for the call-
‘back fanction:
static void CALLB dlgEbHei ghtCB(ads_callback_packet#
epkt)
{
int re;
char strWinHt(100];
double winkt;
if(cpkt->reason == CBR_LOST_FOCUS)
(
// Copy the value to the global variable
// Get the window height
re = ads_get_tile(cpkt->dialog, “eb_height”,&
strWinkt, 80);
if(re t= RTNORM)
(
ads_done_dialog(cokt->dialog, DLGOK);
acutPrintf("\nError in Height edit box. “);
return;
)
winkt = atof(strWinit);
if(winlt <= 0.0)
{
acedAlert(“Invalid value for window height”):
ads_mode_tile(cpkt->dialog, “eb_height”,
MODE_SETFOCUS)
}
else
fi
// Copy value out to global
g.winlit = winkt;)
)
)
In this function, we look at a different field for the callback packet, namely epkt->rea-
son, and verify that it is identically equal to the predefined symbol
CBR LOST_FOCUS. The CBR_LOST_FOCUS will occur as a result of moving the
focus away from the edit box. The CBR_ prefix stands for ‘call back reason.” We have
not discussed call back reasons yet. The CBR codes are used with edit_box, list_box,
slider, and image_button tiles. Table 6.2 shows a listing of all the possible CBR codes:
CBR Code Value
CBR_SELECT The user selected the tile.
CBR LOST_FOCUS | Foredit baxes, the user moved to another tile but
didrit make a final selection,
CBR_DRAG For sliders, the user changed the value by dragging
the indicator (or equivalent) but didn't make a final
selection.
CBR_DOUBLE_CLICK | For list boxes or image buttons, the user double-
clicked to make a final selection.
‘Table 6.2 Possible CBR Values
In the dlgEbHeightCB() function, we check the value entered in the Height edit_ box
and then transfer the value of the local variable winHt to the global variable g_winHt.
All ofthe other callback functions are similar in nature to the dlgEbHeightCBQ func
tion, Let’ move on and talk about displaying nested dialogs (sub-dialogs).
NESTING DIALOG BOXES
Before I talk about the application that implements the nested dialog, I need to
discuss the Ch6_4.de file. Here is the listing for the C6_4.del file:
ché_4 : dialog
t
label = “Window Parameters”;
: rowDGL (Dialog Control Language) Dialogs 385
————
: boxed_radio_col umn
{
label = “Window Type";
key = “bre_wintype”:
: radio_button
{
label = “Rect’
key = “rb_rect
value =
mnemonic
: radio_button
key =
mnemonic = “A”;
radio_button
label = “apex”;
key = “rb_ape)
mnemonic = "X’
)
} // boxed_radio_column
boxed_column
label = “Specifications”:
: edit_box
{
Jabel leight";
key = “eb_height”;
mnemonic = “Hi
edit_width = 8;
edit_box
label = “Width”
key = “eb_width’
mnemonic = “W";
edit_width = 8;
2 edit_boxlabel = “Columns”;
key = “eb_cols”:
mnemonic =
edit_width =
value = “17;
edit_box
label = “Rows”:
key = “eb_rows'
mnemonic = “R”
edit_width = 8;
value = “17;
}
} // boxed_column
VT row
: boxed_row
label = “Start Point”;
“ < Pick “5
ytn_pick”:
key =
mnemonic = “X";
edit_width = 8
value = “0.00;
edit_box
label = “Y Value":
key = “eb_yval”:
mnemonic = “Y"s
edit_width = 8
value = "0.00";
)
) 11 boxed_rowDCL. (Dial Control Language) Dialogs 387
$$$
: row
: button
label = “Stats...
key = “btn_stats”;
: button
label = “Ok”;
key = “accept”;
is_default = true;
button
label = “Cancel
key = “cancel”;
is_default = true;
)
)
}
stats : dialog
i
label = “Window Statistics”:
: concatenation
{
text_part
label = “Type”:
text_part
label = “Rows”;
text_part
label = “Col
width = 6;
text_part
label = “Height”;text_part
fi
label = “Width”;
)
}//concatenation
Vist_box
key = “Ib_stats”;
value
width = 40;
height = 3;
tabs = “6 12 18 27"
1//1ist_box
ok_only;
)
‘This file is very similar to the Ch6_3.dd/ file. The first major difference is that addi-
tion of a second dialog named stats. Figure 6.7 shows the dialog that the stats part
of the Ch6_4.dcl file produces:
Figure 6.7 Nested DCL Dialog
‘As you can see, the concatenation tile ties all the text. part stings together. The lay-
out of the concatenation depends on the width of the text_part ties. The text_part
tiles that do not have the width specified use the default width. Getting the layout cor-
rect is purely a matter of trial and error.
Following the concatenation tie i alist_box tile. The listbox tile has a width of 40
and a height of 3, which means the list_box will display 40 characters horizontally and
3 lines of text vertically. The text that appears inside the list box is a tab-formattedDL (Dialog Control Language) Dialogs 309
string (\e) that you will see when we look at the codes look at the tabs attribute in the
list_box. Tabs values are a quoted string of integers. The integers represent the loca~
tions for the tab stops in the width of the list_box. Again, coming up with the cor-
rect values is a matter of trial and error.
Finally, there is a single ok_only, which is a predefined tile that is part of the base.de!
file. If you look at the bottom of the Cb6_4de file, you will see that I have deleted the
‘ok_cancel predefined sub-assembly and replaced it with a row tile containing three
button tiles as follows:
: row
(
: button
label = “Stats.
key = “btn_stats.
)
: button
(
label = “Ok”
key = “accept
is_default = true:
button
label = “Cancel”;
key = “cancel”;
is_cancel = true;
)
}
‘There are two buttons that have the keys accept and eancel; both of these tiles have
the attribute is_default = true; and is_cancel = true; respectively: These two buttons
replace the ok cancel predefined sub-assembly. Therefore, in order for the buttons to
react to the selection of the OK or Cancel button, they need to have the attribute
{is default = true; defined. Ifyou did not have the attribute is_defaule and/or the is_ean-
ccel defined, the DCL file will not load and you will be prompted with a message from
AutoCAD. When you define your own buttons, you can define their widths. In this
case each button fills up the space with equal widths, as shown in Figure 6.8:Corea
Figure 6.8 Using User-Defined Buttons
Figure 6.9 shows what the nested dialog will look like when activated from the
Stats... button:
Figure 6.9 Nested DCL DialogDOL (Dialog Cntel Langue) Dialogs 391
Displaying a nested dialog is identical to the procedure for displaying a normal dia-
log. The ads_toad_dialog(), ads_new_dialog(, ads_start dialog), and
‘ads_unload_dialog( functions are al called from within a callback function,
SAMPLE Al ‘ATION CH6_4
The only difference between this application and the previous application is the
addition of an extra callback function. Therefore, I'm going to list only the callback
function, the function prototype, and the ads_action_tile() call that sets up the
association between the Stats... button and the nested dialog.
Here is the function prototype:
static void CALLB dlgStatsBtnCB(ads_callback_packet#
*epkt);
Here is the ads_action_tile() call within the user-defined windoQ function just
prior to the call to the ads_start_dialog() for the main dialog,
// The action tile to launch the nested dialog
ads_action_tile(dlgHdl, “btn_stats”, digStatsBtncB);
‘And finally, her isthe listing of the function dlgStatsBenCBQ:
// Callback function to load the nested dialog
static void CALLB digStatsBtnCB(ads_callback_packet®
*epkt)
i
char sub_dciName[20] = {“stats"};
char sub_dclFile{TIL€_STRLIMIT] = (“ch6_4.dc1");
char str_list(100];
char strWinType(201;
int subdclid, subDbStat:
ads_hdlg subHd1;
int re;
rc = ads_load_dialog(sub_dclFile, &subdclId);
if(re t= RTNORM)
(
acutPrintf(“Ss %s", “\nError load OCL file”.&
sub_dcl Fite);
return;
)
re = ads_new_dialog(sub_dcIName, subdclId.@
NULLCB, &subHdTif(re != RTNORM)
(
acutPrintf("\nError displaying dialog”):
return;
)
if(stromp(g_strWinType, “rb_rect) == 0)
" serepy(strintype. Rect");
tise if(stromp(g_strkinType, “rb_arch”) == 0)
‘ strepy(strWinType, “Arch”):
tise if(stremp(g_strWinType, “rb_apex”) == 0)
‘ strepy(strWinType, “Apex”);
tise
{
; strepy(strWinType, “—"
I/ Initialize the sub dialog here
sprintf(str_list, “Zs\tti\t@i\te.31f\ts.31f".
strWinType, g_Rows, g_Cols, gwinlt,@
g_winWidth) ;
// Build up the list
ads_start_list(subHdi, “Ib_stats”, LISTLNEW, 0);
ads_add_list(str_list);
ads_end_listQ;
re = ads_start_dialog(subHd1, &subDbStat);
if(re != RTNORM)
{
acutPrintf("\nFailed to initialize the dialoge
box.");
return;
)
ads_unload_dialog(subdcl Id);
)DCL (Dialog Control Language) Dialogs 393
$$
‘Pay attention to how we build up the tabbed string using the sprintf function and how
we add the string to the list_box using the combination of ads_start_list(),
‘ads_add_listQ), and ads_end list(). If you need to add more than one string to the
listbox, you can make repeated calls to ads_add_list() prior to calling ads_end_list).
‘There are notes in the documentation on how to handle list_box tiles.
‘Wal, that brings us to the end of this chapter. In the next chapter we will look at how
to use MFC-style dialogs (Microsoft Foundation Classes) in ObjectARK and some
‘other MFC issues that are associated with ObjectARK.MFC Dialogs and ObjectARX’s
UI Extensions
In this chapter we will explore how to use MFC (Microsoft Foundation Classes) style
dialogs within ObjectARX as well as ObjectARX’s UI Extensions. This chapter is not
a lesson on MEC; its primary purpose is to show you how to use MFC within
‘ObjectARX. This chapter assumes you know how to use AppWizard and
Class Wizard and that you know how to use ClassWizard to add member variables,
DDX, DDV, and message mapping functions to your applications. (Ifall of the pre-
viously mentioned terms are foreign to you, perhaps you need to review MFC.)
‘We will look at resources first, and then at modal and modeless dialogs, followed by
a look at property pages and wizards. Then we will take a look at some of the com-
‘mon controls and user interfaces offered by MFC and discuss how they are used in
ObjectARX. We will also discuss resource-only DLLs. All of the sample applications
for this chapter were built with the ObjectARX 2000 AppWizard. I suggest you read
the documentation that comes with the ObjectARX 2000 AppWizard to become
familiar with this environment. First let's quickly review the project settings that are
required for MFC and ObjectARX.
PROJECT SETTINGS
‘HE you want to make your life easy with regard to creating ObjectARX 2000 appli-
‘ations that use MFC, I strongly suggest you use the ObjectARX ClassWizard
that comes with ObjectARX 2000. The ObjectARX ClassWizard is located in the
ObjectARX 2000\uti Ob/ARXWiz folder. However, I suggest you download the lat-
est version from the ADN web site. It is located at:
‘Here you will find a link “ObjectARX 2000 Wizards”.396
lease note that this application is constantly being updated, so be sure to check back
at the ADN web site from time to time. Refer to the documentation that comes with
the ObjectARX ClassWizard for information on how to use/install the wizard.
Believe me, this is by far the easiest way to create Object ARX applications, using MFC
or otherwise. When you select the MFC check box on the wizard, leave all the
defaults set. The wizard will create a set of starter files for you; it can also be used in.
‘your application just like Class Wizard can be used in MFC applications.
In your Visual C++ applications project settings, add the ObjectARX 2000\Inc direc~
tory for the various ObjectARK header files that ObjectARX uses. From the Tools
menu in Visual C+4, select the Options... menu item. When presented with the
Options dialog, select the Directories tab. Make sure that that Include files is select
ed in the Show Directories for: drop-down list. Add the path to your ObjectARX
2000\nc directory as Figure 7.1 shows:
G\Program Fies\DevStudio\VCMNCLUDE
Program Fles\DevStudo\VC\MFC\include
G.\Program Files\DevStucb\VC\ATL\nclude
Figure 7.1 Visual C¥+ Settings for ObjecARX Library Files
‘We need to repeat the process outlined above except that this time, make sure the Show
Directories for: drop-down lis is selected at the Library files option. Add the path
‘to your OdjettARX 2000\Lib directory as Figure 7.2 shows:‘MBC Dialogs and Object ARK UI Extensions 397
G:\Progam Fies\DevStudo\VC\UB
G.AProgiam Fies\DevStudo\VC\MFCMS
INDevStudo\VEALIB
INDevStudio\ VEEL
Figure 7.2 Visual C+ Settings for ObjectARX Include Files
AAlll of the sample applications for this chapter were created with the ObjectARX
ClassWizard in combination with MFC ClassWizard. Let's move on and talk
about Windows resources, AutoCAD, ObjectARX, and MFC.
RESOURCES
In Windows application development, you will be hard pressed to find an application
that does not use resources. Applications that employ graphical user interface elements,
be they dialogs, controls, or menu items, refer to these elements by means of resources,
‘The resource file contains IDs for all the user interface elements. When writing
standalone Windows applications, you usually don't have to worry about resources.
However, ObjectARX applications are not standalone Window applications—they are
“in-process” service DLLs. They do, however, have an ARK file extension as opposed
toa DLL file extension. These ARX applications, in conjunction with MFC and
graphical user interface elements they employ, use resources. These resources are
not the same as the resources found in AutoCAD; they may be of the same type but
have different resource IDs. The same holds true for any other type of DLL you may
write, be it a resource-only DLL or an Extension DLL. This applies to all kinds of
‘Windows programming, not just AutoCAD and ObjectARK.So by now, you should be getting the picture that resources are important. Even more
important is the management of those resources. In ObjectARX 2000, resource
management is handled much better than in ObjectARX 2.02 for AutoCAD R14.
However, in ObjectARX 2.02, resource management was not that difficult.
‘ObjectARX 2000 provides a class CAcModuleResourceOverride that handles
resouice management for you, a8 we will se later in our applications. In ObjectARX
2.02 and MFC, there are three ways to handle resources. The first method is to use
a class called CTemporaryResourceOverride (see Object ARK 2.02 documentation).
The second method is to allow AutoCAD to search through the resource chain
until it finds your resource ID. What happens if it finds a resource ID with the
same resource ID as the one located in your application? Well, as the story goes: “first
come first served.” Finally, the third method is to explicitly change resource handles
to that of your ObjectARX 2.02 application. When you are finished with the user
interface elements that are required by your application, you explicitly switch back to
AutoCAD’s resources. In ObjectARX 2.02, you are responsible for the switching;
‘AutoCAD will not magically switch you back. The reason I mention resource han-
dling in ObjectARX 2.02/AutoCAD Ri4 is that we will see the use of explicit
resource handling in some aspects of ObjectARX/AutoCAD 2000 (but not nearly as
Our first application demonstrates the use of a modal dialog. The modal dialog that
this application uses will be used in later sample applications. It will be that same win-
dow drawing application that the last chapter used except that, instead of using
DCL dialogs, we are now going to demonstrate MFC dialogs. In ObjectARX 2000
there are a number of UI/MEC Extension classes. In our sample applications we use
pure MFC and ObjectARX’s UI/MFC Extension classes, Broadly peaking, for
UV/MEC Extensions there are two major classes, namely CAdUIXXX and
CACUIXXX. CAcUI classes are derived from CAdUi and tend to be specific to
AutoCAD. CAdUi can be used by any application that interacts with any Autodesk
product. You can use the CAdUi controls in applications that are AutoCAD-specif-
icor in standalone applications that in some manner interact with Autodesk products.
Here I will show you how to use the CAcUW/CAdUI controls in your application; you
‘ean refer to the documentation for a complete discussion of the classes and methods
offered.
In the last application we talked about the Single Document Interface (SDI) and port-
ing considerations. Throughout this chapter we will discuss global data in detail
and how to handle it All of the applications for this chapter are MDI (Multiple
Document Interface) aware. All of the applications are well behaved, law-abiding
MDI-aware citizens.(MPC Dialogs and ObjetARX's UL Extensions 399
eee ion
MODAL DIALOGS AND SAMPLE APPLICATION CH7_1
Using the resource editor, create a dialog IDD_WINDOTYPE, as shown in Figure 7.3:
Figure 7.3. Modal Dialog
‘Table 7.1 shows the resource IDs for the appropriate controls in the dialog.
All other controls in the dialog are IDC_STATIC. The OK and Cancel buttons are
standard buttons. The reason that the frst radio button has a ‘Group’ style set is to
indicate where the grouping mechanism starts. The Group box also has the ‘Group’
bit se; this indicates the end of the group mechanism. Now Class Wizard knows how
to manage the three radio buttons as a group. Figure 7.4 shows the tab order for the
dialog.and Type Resource ID Resource Settings
‘Dialog “Window IDD_WINDOTYPE | Style « Popup’, Border =
Parameters” ‘Dialog Frame’, Title Bar,
System menu
IDC_RB_TYPERECT _ | Visible, Group, Tab Stop
and Auto. Caption =
‘Rect?
Radio button ~ “Arch? IDC_RB_TYPEARCH | Visible, Tab Stop and
Radio button ~"apeX”
‘IDC_RB_TYPEAPEX
Group Box-“Window
Type”
IDC_STATIC
Edit box - “Height” IDC_EDIT HEIGHT | Visible, Tab Stop, Auto
HScroll, Border.
Edit box— “Width” IDC_EDIT_WIDTH | Visible, Tb Stop, Auto
HScroll, Border.
Editbox “Columns” IDC_EDIT_COLS Visible, Tab Stop, Auto
Scroll, Border.
Edit box “Rows” IDC_EDIT_ROWS | Visible, Tab Stop, Auto
HScroll, Border.
‘All other elements IDC_STATIC Default properties, see
illustration.
Table 7.1, Resource IDs forthe Diolog Contos of Fgure 73MFC Dialogs and ObjetARX'$ UL Extensions 401
Figure 7.4 Modal Dialog Tab Order
‘When you have finished creating the dialog, use the ObjectARX 2000 AppWizard
to create « CWindoTypeDlg class based or the CAcUIDialog class. For this you will
select the ObjectARK MFC Support toolbar button of the ObjectARX 2000
AppWizard. As I suggested eatlies, you should read the ObjectARK Class Wizard doc-
‘umentation for a description of how this is done. You will also use ClassWizard to hook
up the controls to the various member variables.
Let's take a look at the implementation of the Cs7_1.CPP file, which was generat-
ed by the ObjectARX 2000 AppWizard:
// Ch7_l.cpp : Initialization functions
#include “StdAfx.h”
#include “StdArx.h”
#include “resource.h”
include
HINSTANCE _hd1l Instance =NULL ;
// This command registers an ARX command.
void AddCommand(const char* cmdGroup, const chart#
emdint, const char* cmdLoc,
const int cmdFlags, const AcRxFunctionPtry
cmdProc, const int idlocal = -1);
// NOTE: DO NOT edit te following lines.
T/({APX_ARX_MSGvoid InitApplication();
void UnloadApplication(
7) AFX_ARX_MSG
// NOTE: DO NOT edit the following lines.
7 ({AFX_ARX_ADDIN_FUNCS
7) AFX_ARX_ADDINFUNCS
MALT TLL LLL TLL,
MALL
u"
// Define the sole extension module object.
AC_IMPLEMENT_EXTENSION_MODULE(Ch7_1DLL);
11 Now you can use the CAcModuleResourceverrided
class in
// your application to switch to the correct?
resource instance.
1/ Please see the ObjectARX Documentation for mored
details
TITTLE
TTT
77 DLL Entry Point
extern “C”
BOOL WINAPI D11Main(HINSTANCE hinstance, DWORD®
dwReason, LPVOID /*1pReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
(
// Extension DLL one time initialization
Ch7_1DLL.AttachInstance(hinstance);
InitacUiDLLO:
) else if (dwReason == DLL_PROCESS_DETACH) (
// Terminate the library before destructors®
are called
Ch7_1DLL.DetachInstance();
)
return TRUE; // 0«
)
ITLL TTT
IATL TLLMFC Dialogs and Object ARK: UI Extensions 403
—$—____~__
11 ObjectARX EntryPoint
extern “C” AcRx: :AppRetCode
acrxEntryPoint(AcRx: :ApaMsgCode msg, void* pkt)
(
switch (msg)_{
case AcRx::kIni tAppMsg:
// Comment out the following line if your
/1 application should be locked into memory
acrxDynamicLinker->unlockAppl ication(pkt) s
acrxDynamicLinker->-egisterAppMOIAware(pkt) ;
chd1lInstance =::GetModuleHandle (“Ch7_1.arx”)
Initappl ication(
brea
case ACRx: :kUn]oadAppitsg:
UnloadApplication();
breaks
}
return AcRx::kRetOK:
// Init this application. Register your
// commands, reactors...
void InitApplication()
(
JJ NOTE: DO NOT edit the following lines.
7 AAPX_ARX_INIT
AddCommand(*CH7_APPS", “WINDO", “WINDO”,
ACRX_CMD_MODAL, windo);
T/)YAFX_ARX_INIT
// 7000: add your initialization functions
acutPrintf(“\nType \*WINDO\” to execute”);
1
// Unload this application. Unregister all objects
// registered in InitAppl ication.
void UnloadApplication()
{
// NOTE: DO NOT edit the following lines.
J HUCAPX_ARX_EXIT
acedRegCmds->removeGroup(“CH7_APPS");
JA) YAPX_ARK_EXIT// TODO: clean up your application
)
// This function registers an ARX command.
// It can be used to read the localized command name
// from a string table stored in the resources.
void AddCommand(const char* cmdGroup, const char«o
cmdInt, const char* cm¢Loc,
const int cmdFlags, const AcRxFunctionPtre
cmdProc, const int idiccal)
(
char cmdLocRes{65];
// If idlocal is not -1, it’s treated as an 10 for
// a string stored in the resources.
if CidLocal != -1) {
HMODULE hModule = GetModuleHandle(“Ch7_1.arx");
17 Load strings from the string table and&
register the command.
riloadString(hModule, idLocal, cmdLocRes, 64);
acedRegCmds->addConmand(cmdGroup, cmdInt, &
cmdLocRes, cmdFlags, cndProc);
) else
11 idlocal is -1, so the ‘hard coded’
17 Vocalized function name is used.
acedRegCnds->addConmand(cmdGroup, cmdInt.&
cmdloc, cmdFlags, cmdProc);
)
The first item that I want you to note is the following:
AC_IMPLEMENT_EXTENSION_MODULE(Ch7_1DLL);
All ObjectARX applications for this chapter are Extension DLLs. Inside DilMain you
will see a call to
InitaciDLLO:
‘This initializes the AcUi DLL so that we can use those controls in our applications.
‘We have seen how to register commands in earlier applications and this is done in
InitApplication.(MPC Dialogs and Objet ARXs UI Extensions — 405
———__~_
‘The rest of the items in this file are generated by the ObjectARX 2000 AppWizard,
so let’s move on to our user-defined command windoQ. By the way, I should men-
tion here that the ObjectARX 2000 AppWizard also has a command CMD toolbar
button that will enable you add as many commands as you like to your application,
‘The commands for this application are in the Ch7_1Commands.CPP file as follows:
TELLTALE
// ObjectARX defined commands
itinclude “StdAfx.h”
#include “Stdarx.h”
#include “WindoTypeDlg.h”
// This is command *WINDO*
void windot)
{
71 TODO: Implement the command
// When resource from this ARX app is needed,&
just
// instantiate a local CAcModuleResourcedverride
CAcModuleResourceOverride resOverride;
CWindoTypeDl ge
dig(Chnd: : FromHand1e(adsw_acadMainwind()));
if(d1g.DoModal() == 100k)
(
// Do something useful here
acutPrintf(“\nWindow type = 45".
windInfo.windType) ;
acutPrintf(“\nWindow height = %.21f",¢
windInfo.m_dWindHt)
acutPrintf(“\nWindow width = %.21f",3
windInfo.m_dWindWt)
acutPrintf(“\nlindow cols = %d",o
windInfo.m_nCols);
acutPrintf(“\nWindow rows = 2d",
windInfo.m_nRows);
}
)
The most important aspect in using MFC elements in your application is resource
‘management, as mentioned earlier. This is atte easier in AutoCAD 2000 than it wasfor AutoCAD R14 applications. In Object ARX 2000, simply include the following
in your application before you display your dialog box:
CAcModuleResourceOverride resOverride;
Creating an instance of CAcModuleResourceOverride, its constructor will auto-
‘matically swap resources for you. When this function (winde) goes out of scope, the
destructor will automatically switch back to AutoCAD's resources—that’ all there is
to it. Ifyou leave out the CAcModuleResourceOverride, you may get some funny
looking labels in your dialog, because chances are, you are using AutoCAD resource
IDs. The call to dig. DoModald will display our dialog, so let’s take a look at the
ObjectARK 2000 AppWizard created CWindoTypeDig header fle WindoTypeDig.h:
1/—____________—_.-
#if !defined(ARX_WINDOTYPEDLG_H_19990606_105025)
fidefine ARX_WINDOTYPEDLG_H_19990606_105025
Le
#if _MSC_VER > 1000
pragma once
dendif // _MSC_VER > 1000
0@™
#include “resource.h”
class CWindoTypeDIg : public CAcUiDialog {
DECLARE_DYNAMIC (CWindoTypeD1g)
public:
CWindoTypeDlg (CWnd* pParent =NULL, HINSTANCE®
hinstance =NULL) ;
//(AFX_DATA(CHindoTypeD1g)
enum ( 10D = IDD_WINDOTYPE };
CAcUiNumericEdit m_ctrlEdHei ght;
CAcUiNumericEdit m_ctrlEdWidth;
int munWindType;
int mnCols:
int _monRows:
J) )AFX_DATA
CString mstrHeight;
CString mstrWidth:‘MFC Dialogs and Objet RX UI Extensions 497
———_______.
// (CAFX_VIRTUAL(CHindoTypeD19)
protected:
virtual void DoDataéxchange(CDataxchange* pDX):¢
11 DDX/DDV_ support
JI) YAFX_VIRTUAL
protected:
11 ((AFX_MSG(CHindoTypeD19)
afx_msg LONG OnAcadKeepFocus(UINT, UINT):
virtual BOOL OnInitDialog();
afx.msg void Onkil]focusEdi thei ght();
afx_msg void Onkil]focusEdi twidth();
virtual void On0K():
//))AEXMSG
DECLARE_MESSAGC_MAP()
a
11 ((AFX_INSERT_LOCATION} }
1/ Microsoft Visual CH will insert additionalg
declarations immediately before the previous line.
fendif //----
Idefined(ARX__WINDOTYPEDLG_H_19990606_105025)
Notice that the class that CWindoTypeDI is derived from is CAcUiDialog, which,
by the way, is derived from CAdUiDialog and from CDialog. Examining the head
er file above, we have some edit controls that are CAeUiNumericEdlt, which are uli~
mately detived from CEdit controls. These controls were added through Visual
C++’s MFC ClassWizard Member Variables tab. When associating a control with a
‘member variable, in the Category: drop-down list box you will see the following types:
Value, Control, Autodesk Control, AutoCAD Controt—MFC ClassWizard is
very smart! All of the Messages Map items are standard MIFC messages with the sin-
gle exception of OnAcadKeepFocus, which we will discuss later because it’s more
appropriate to modeless dialogs. Let’s take a look now at the implementation file
Wind TypeDig.pp:
WindoTypeDlg.cpp : implementation file
finclude “StdAfx.h”
finclude “StdArx.h”
finclude “resource.h”
finclude “WindoTypeDIg.1”LL
dHifdef _DEBUG
fidefine new DEBUG_NEW
fundef THIS_FILE
static char THIS_FILEC] = _FILE_;
Hendif
UL
IMPLEMENT_DYNAMIC (CWindoTypeDIg, CAcUiDialog)
BEGIN_MESSAGE_MAP(CWindoTypeD1g, CAcUiDialog)
71 (TAFX_MSG_MAP(CWi ndoTypeD1a)
ON_MESSAGE(WM_ACAD_KEEPFOCUS, OnAcadkeepFocus)¢
/7 Needed for modeless dialog.
ON_EN_KILLFOCUS( IDC_EDIT_HEIGHT,
Onki11 focusEditHei ght)
ON_EN_KILLFOCUS( IDC_EDIT_WIDTH,
Onki11 FocusEditwidth)
7) YAFX_MSG_MAP
END_MESSAGE_MAP()
i WindoTypeD1g (Cnd* pParent
P*=NULL*/, HINSTANCE hinstance /*=NULL*/) :
CAcUiDialog (CWindoTypeDlg::I0D, pParent,
hinstance) (
//((AFX_DATA_INIT(CWindoTypeD1g)
mnWindType = -1;
mnCols = windInfo.mnCols;
mnRows = windInfo.mnRows;
/7)VAFX_DATA_INIT
)
void CindoTypeD1g: :DoDataExchangee
(CDataéxchange *pDX) {
CAcUi Dialog: :DoDataExchange (pOX) ;
7/{(AFX_DATA_MAP (CHindoType01 9)
DDX_Control(pOX, IDC_EDIT_HEIGHT, m_ctrlEdHeight);
DDX_Control(pDX, IDC_EDIT_WIDTH, mctrlEdWidth);
DDX_Radio(pDX, TDC_RB_TYPERECT, m_nWindType);
DDX_Text(pDX, IDC_EDIT_COLS, m/nCols):
DDV_MinMaxInt(pOX, mnCols, 1, 10);
DDX_Text(pDX, IDC_EDIT_ROWS, mnRows);
DDV_MinMaxint (pDX, mnRows, 1, 10);MPC Dialogs and ObjetARX's UI Extensions 409
$1
1) AFX_DATA_MAP
)
// Needed for modeless dialogs to keep focus.
// Return FALSE to not keep the focus, return TRUE®
to keep the focus
LONG CWindoTypeD1g::OnAcadKeepFocus(UINT, UINT)
{
)
us
return TRUE:
BOOL CWindoTypeDlg: :OnInitDialog()
i
CAcUIDialog: :OnInitDialog();
/1 TODO:
Add extra initialization here
CButton *pWindoType;
CString strWindType(windInfo.windType) ;
if(strWindType == “Rect”)
t
pWindoType = (CButton *)g
GetD1 gl tem( TDC_RB_TYPERECT)
pWindoType->SetCheck(1):
)
else if(strWindType == “Arch")
(
pWindoType = (CButton *)
GetD1gItem(T0C_RB_TYPEARCH) ;
pWindoType->SetCheck(1):
)
else // It must be an “Apex” type of window
(
pWindoType = (CButton *)¢
GetD1 gl tem(IDC_RB_TYPEAFEX) ;
pWindoType->SetCheck(1);
MctrlEdHeight.SetRange(10.00, 120.00);
mctrlEdWidth.SetRange(10.00, 120.00);410
acdbRToS(windinfo.m_dWindHt, 2, 3.8
m_strHei ght. GetBuffer(80));
m_ctrlEdiei ght. SetWindowText(m_strHei ght);
acdbRToS(windinfo.m_dWindWt, 2, 3,0
m_strWidth.GetBuffer(80)):
mctrlEdWidth. SetWindowText(m_strWidth);
return TRUE; // return TRUE unless you set thee
focus to a control
// EXCEPTION: OCX Property Pages®
should return FALSE
}
void CWindoTypeDlg: :Onki11focusEdi tHei ght()
{
// T0D0: Add your control notification handlerd
code here
m_ctrl€dHeight.Convert();
if(1m_ctriEdHeight.Validate())
(
fxMessageBox("Sorry, Range = 10.00 tog
120.00");
m_ctrlEdHeight.SetFocus();
mictriEdHeight.SetSel(0, -1);
)
)
void CWindoTypeD1g: :OnKi11focusEditwidth()
(
// TODO: Add your control notification handlerg
code here
m_ctrlEdWidth.Convert();
if(1mctrlEdWidth.Validate())
t
rrAfxMessageBox("Sorry, Range = 10.00 to#
120.00"):
mctrlEdWidth.SetFocus();
mctrlEdWidth.SetSel(0, -1);
)
)
void CWindoTypeD1g: :On0K()(MPC Dialogs and ObjetARX's UI Extentions 41)
—_—$
J/ TODO: Add extra validation here
if (1UpdateData(TRUE))
i
return;
)
mctrlEdHei ght. GetWindowText(m_strHei ght):
mctrlEdWidth.GetWindowText(m_strWidth);
windInfo.m_dWindWt = atof(m_strWidth
windInfo.m_dWindHt = atof(m_strHei ght)
windInfo.mnCols = mnCols;
windInfo.m_nRows = m_nRows;
+ switch (mnWindType)
(
case 0:
strepy(windInfo.windType, “Rect”);
break;
case 1:
strepy(windInfo.windType, “Arch");
break:
case 2:
strepy(windinfo.windType, “Apex”);
break;
)
CAcUIDiatog: :0n0K();
)
In the constructor there is a reference to windinfo, which suggests that this is a
structure of some type. One important item that we have not yet discussed is glob-
al data, Tim going to be brief here, because I wll discuss global data in much more detail
Jater. Global data was easy to manage in AutoCAD R14 because in that environment
you could only have a single drawing file open at a time. However, as you now
know, in AutoCAD 2000 you can have multiple drawing files open. What we want
to achieve is global data on a per drawing (per document—more later) basis and this
structure (for want of a better term for right now) helps us achieve this. The values in
this constructor were added by hand and not through the MFC ClassWizard. The
DoDataExchange is pretty straightforward in that it was handled by the MFC
‘Class Wizard. In OntnitDialog( look at the following lines of code:an
mctrl£dHeight.SetRange(10.00, 120.00);
mctrl£dWidth.SetRange(10.00, 120.00);
‘This SetRange() is part of the CAcUiEdit control functionality. CAcUiNumericEdit
is a special numeric edit control (as the name suggests). Look at one of the
‘OnKillFocus message map functions. Here we have two more useful Autodesk edit
control functions, namely Convert() and Validate(); everything else is MFC. What
Twant to encourage you to do is investigate what functionality you get by using the
‘Autodesk controls. The other controls in this application are handled by MIC, so what
‘we end up with is a combination of Autodesk technology and standard MFC usage.
I did not go overboard on this application and error check all the other controls.
‘There are two additional classes that I did not talk about, namely AsdkDataManager
and CDoeData. Both of these classes were created by the ObjectARX 2000
AppWizard. The purpose of these classes is to help you manage global data on a per
document (per drawing) basis. As an expeciment try the following:
|. Create two new drawings in AutoCAD 2000.
2. Execute the Windo command from one of the active drawings. Enter some
values in the dialog and select the OK button.
3. From the second drawing, execute the Windo command again. This time
enter some values different from the ones entered previously for the first
drawing. Select the OK button.
4. Now make the first drawing the active drawing and execute the Windo com-
‘mand. Did you notice anything? The frst drawing remembered the values you
had used earlier
5. Now make the second drawing the active drawing and execute the Windo
‘command, Surprise, surprise! The second drawing also remembers the values
you had put in previously.
‘That is what I mean by global data on a per document (per drawing) basis. How is this
achieved? For the answer to this question, let's first look at the class definition for
‘CDocData and then at the constructor for the CDocData class. Here is the listing for
the header file DocData.t:
J/ docdata.h : include file for document specific
data
" an instance of this class is automaticallyg
created
ul and managed by the AsdkDataManager class
W see the AsdkOmgr.h DocData.cpp for mored
detail
we(MPC Dialogs and ObjectARX's UI Extensions 413
—$—$_~___
!defined(AFX_DOCDATA_H_68C98B7F_1BEE_11D3_A7A7_¢
000000000000__INcLUDED_)
define
AFX_DOCDATA_H__68C9887F_1BEE_11D3_A7A7_o000000000008
—INCLUDED__
dif MSC_VER > 1000
‘#pragma once
endif // MSC_VER > 1000
MATT TLL
uw
// Here you can store the document / database
// related data.
Ww
class CDocData
{
public:
CDocData():
~CDocData();
JJ NOTE: 00 NOT edit the following lines.
1 LAFX_ARX_DATA(CDocJata)
1) APX_ARX_DATA
// TODO: here you can add your variables
uv which depends on a document / database.
char_windTypet5];
double m_dWindt;
double m_dWindwt
int mnRows:
int mnCols:
h
endif
Here you can see thatthe data for our application is contained inside a class. The con-
structor for this class initializes the data as follows in the listing for DacDatacpp:
finclude “StdAfx.h”
finclude “StdArx.h”
// The one and only document manager object
// You can use the DocVars object to retrievea4
J] document specific data throughout youre
appl ication
AsdkDataManager DocVars;
uy
// Implementation of the document data class.
uW
CDocData: :CDocDatat)
fl
// NOTE: DO NOT edit the following lines.
//((AFX_ARX_DATA_INIT(CDocData)
7) )AFX_ARX_DATA_INIT
// T000: add your own initialization.
strepy(windType, “Rect”):
mdWindHt = 10-00;
m_dWindWt = 10.00;
manRows = 1;
manCols = 1;
)
CDocData: :~CDocData()
{
J/ NOTE: DO NOT edit the following lines.
H/((AFX_ARX_DATA_DEL(CDocData)
J) )AFX_ARX_DATA_DEL.
71 T0D0: clean up.
)
(Olay, so far so good. But where is the windlnfo variable? Before we take a look at that,
notice the following from the previous code, a global instance of AsdkDataManager
in DoeVars:
AsdkDataManager DocVars;
‘AsdkDataManager manages CDocData classes. Now let's take a look at the Stddrx.)
file (not to be confused with the Std4fs.b fle that MFC creates):
uN
11 StdArx.h : include file for ObjectARX/DBXe
include files
// this file is only included once by your stdafx.h(MPC Dialogs and ObjetARXs UI Extensions 15
$$
Hite
def ined(AFX_STOARX_H__68C98B78_18£E_1103_A7A7_¢
000000000000__INCLUDED_)
define
AFX_STOARX_H__68C98B78_1BEE_1103_A7A7_0000000000008
—INCLUDED_,
#if MSC_VER > 1000
pragma once
#endif // _MSC_VER > 1000
1 ((AFX_ARX_INC_SELECTED
1/)) AFX_ARX_INC_SELECTED
7 LAFX_ARX_INC_OTHERS
17) )AFX_ARX_INC_OTHERS.
include “AdskOMgr.h” // Utility classe
for document data
#include “docdata.h” “1 Your documents
specific data
// Declare it as an extern here so that it
// becomes available in all modules
extern AsdkDataManager DocVars;
define windInfo DocVars.docData()
T/A APX_ARX_FUNC
1) JAFX_ARX_FUNC
void windo();
J/ T0D0: Here you can add your own includes /¢
declarations
endif
Here is where you will find windInfo, which is a #define. So everywhere you see a
windInfo as follows,
windInfo.mnCols = mncols;
it becomes at compile time:
DocVars.docData().m_nCols = m_nCols:416
‘This brings up the next question: what is AsdkDataManager? First, I don't want to
get into it too deeply, because i's not necessary for us to understand all the implica
tions ofthis. Suffice it to say it manages a singe instance of CDoeData for every draw-
ing file that is open. When you switch from one drawing file to another, it knows
which instance of CDoeData belongs to the active document (drawing). A suggest
cd practice is that all global data be placed in these CDocData classes and that
‘AsdkDataManager manage that data for every drawing. We will be visiting global data
and the Multiple Document Environment (MDE) in more detail in a later chapter.
If you are familiar with MFC, Multiple Document Interface (MDI) is more famil-
iar to you. Welcome to the marketing name of MDI, namely MDE, as far as
AutoCAD 2000 is concerned.
MODAL DIALOGS AND SAMPLE APPLICATION CH7_2
Ifyou followed along with the last chapter, which dealt with DCL style dialogs, the
CB7_2.arx application is the MFC equivalent of the Cb6_4.arx application of the last
chapter. Ifyou recall, the DCL application demonstrated two techniques. The first
technique showed how to hide the dialog to allow the user to select a point with
acedGetPoint(). You may recall that in order for a point to be selected on the screen
with a DCL dialog you had to hide the dialog, allow the point to be selected, and then
bring the dialog back to life. You could not select a point while the dialog was visi-
ble. This application also displayed the technique of displaying a nested dialog. The
(Ch7_2.ars application is the MFC equivalent. In this application we display a modal
dialog box. The modal dialog box has a < Pick button that lets you hide the dialog facil-
itating the use of acedGetPoint() after which the dialog comes back to life. There is
also a Stats... button, which launches a nested dialog that displays various statistics
about the window parameters the user selected.
‘Wait a minute, hold the phone! What do you mean by “hide” a modal dialog? Well,
for those of you who asked that question, go straight to the head of the class, and for
those of you who still think it’s Monday morning, time to wake up! The very nature
of a modal dialog is just that: modal, meaning you cant do anything while it's still alive.
‘You certainly cant select a point on the AutoCAD screen. Well, as it turns out,
‘modal dialogs aren't quite so modal afterall (reminds me of “the truth is out there”
and “don't believe everything you hea”). It turns out that some of my colleagues here
showed me some tricks with the various bits and bobs of MFC that will allow you to
hide a modal dialog. Figure 7.5 shows the main dialog for this application:
Well, as you can see, there is nothing very different here. It looks very similar to its
DCL counterpart. Figure 7.6 shows what the nested dialog looks like:MPC Dialogs and ObjatARXs UI Extensions 417
$$
Figure 7.5 Hiding Modal Dialogs
Figure 7.6 Nested Modal Dialog
‘Now this looks a litte different from the DCL equivalent. This dialog contains one
‘of Windows’ new common controls, namely a CListCtrl shown in a ‘report’ view style.
Yes folks, welcome to MFC. In Windows we have buckets of controls that DCL just
doesn't have.418
First, Table 7.2 shows the settings for the class CWindoTypeDlg:
Resource Name
and Type Rescue ID Resource Settings
Dialog-"Window IDD_WINDOTYPE | Style = Popup, Border =
Parameters” ‘Dialog Frame’, Tile Ba,
System menu
Radio button—"Rec®” | IDC_RBTYPERECT | Visible, Group, Tab Stop
and Auto. Caption =
“Rect?
Radio button — “Arch”
IDC_RB_TYPEARCH
Visible, Tab Stop and
Auto.
Caption ='8Archt
Radio button—“apeX” | IDC_RB_TYPEAPEX | Visible, Tab Stop and
Auto,
Caption =ape8iX”
Group Box—“Window | IDC_STATIC Visible, Group
Tet Caption =" Window Type’
Edit box "Height™ IDC EDIT HEIGHT — | Visible, Tab Stop, Auto
Scroll, Border
Edit box — “Width” IDC_EDIT WIDTH | Visible, Tab Stop, Auto
HScroll, Border.
Edit box —“Colurns™ 1DC_EDIT_COLS Visible, Tab Stop, Auto
HScroll, Border.
Edit box— “Rows? IDC_EDIT_ROWS | Visible, Tab Stop, Auto
HScroll, Border.
Push button=" 1000
pragma once
d#endif // _MSC_VER > 1000
«a
#include “Resource.h”
class CWindoTypeDlg : public CAcUiDialog {
DECLARE_DYNAMIC (CWindoTypeD1g)
public:
void updateGlobalDocbata();
CWindoTypeD1g (CWnd* pParent =NULL, HINSTANCES
hinstance =NULL) ;MBC Dialogs and ObjetARX's UI Extensions 424
$<
1/ {AFX_DATA(CHindoTypeD1g)
enum { IDD = IDD_WINCOTYPE };
CAcUiPickButton mctrIBtnPickPt;
int munCols;
int mnRows;
CAcUiNumericEdit m_ctriEdHei ght;
CAcUiNumericEdit m_ctr1EdWidth;
CAcUiNumericEdit m_ctriEdXval;
CAcUiNumericEdit m_ctr1EdYVal;
int mnWindTypes
IV )AFX_DATA
CString mstrHeight;
CString mstrWidth;
CString mstrXxVa
CString mstrYVal;
1 (AFX_VIRTUAL(CHindoTypeD1g)
protected:
virtual void DoDataExchange(CDataéxchange*
pOX); — // DDX/DDV support
11) )AFX_VIRTUAL
protected:
7 LAFX_MSG(CHindoTypeD19)
afxmsg LONG OnAcadKeepFocus(UINT, UINT);
virtual BOOL OnInitDialog();
afx_msg void Onkill fccusEdi thei ght(
afxmsg void Onkill fecusEdi tWidth();
virtual void On0k();
afx.msg void OnBtnPickPt();
afx_msg void OnBtnStats();
11) YAFX_MSG
DECLARE_MESSAGE_MAP()
ng
7 (AFX_INSERT_LOCATION) }
// Microsoft Visual C++ will insert additional
declarations immediately before the previous line.
fendif //
!defined(ARX__WINDOTYPEDLG_H__19990606_204349)m
All the member variables were created with the help of MFC ClassWizard and
ObjectARX 2000 AppWizard. As discussed before, we will see how
DoDataExchange() interacts with the variables when we get to the implementation
file for this class. The user-defined functions are OnBtnStats), OnBtnPickPt(, and
exactly as before, the message map functions OnKillfocusEditHelght(),
OnKilfocusEditWidth0), and OnAcadKeepFocus(). Look at the member control
m_etriBtnPickPt: its a CAcUIPickButton, 2 specialized AutoCAD owner-draw
point-picking button that is used in various locations throughout AutoCAD 2000.
CAcUIPickPointButton is derived from CAdUIOwnerDrawButton, which in turn
is derived from CButton. Here is the listing for the implementation file for this class,
WindoTypeDigcppt
WindoTypeD1g.cpp : implementation file
#include “StdAfx.h”
#include “StdArx.h"
#include “resource. h”
#include “WindoTypeD!g.h”
#include “WindoStatD1g.h”
ffinclude “geassign.h”
He
ifdef DEBUG
fdefine new DEBUG_NEW
fundef THIS_FILE
static char THIS_FILEC] = _FILE_:
fendi f
a$$ _—_————_-
IMPLEMENT_OYNAMIC (CWindoTypeDlg, CAcUiDialog)
BEGIN_MESSAGE_MAP(CWindoTypeD1g, CAcUiDialog)
T/((AFX_MSG_MAP(CHindoTypeD1g)
ON_MESSAGE(WM_ACAD_KEEPFOCUS, OnAcadKeepFocus)
// Needed for modeless dialog.
ON_EN_KILLFOCUS(1DC_€DIT_HEIGHT, &
Onki11focusEdi tei ght)
ON_EN_KILLFOCUS(1DC_€DIT_WIDTH, &
Onki11 focusedi tWidth)
ON_BN_CLICKED(IDC_BTN_PICKPT, OnBtnPickPt)
ON_BN_CLICKED(IDC_BTN_STATS, OnBtnStats)
J) )AFX_MSG_MAP.
END_MESSAGE_MAP()MPC Dialogs and Objet ARX's UI Extensions 423
$<
It a
CWindoTypeD1g::CWindoTy2eD1g (CWnd* pParent
/*=NULL*/, HINSTANCE hInstance /*=NULL*/) :@
CAcUiDialog (CWindoTypeDlg::10D, pParent, Instance)
{
//((AFX_DATA_INIT(CWindoTypeD1g)
mnCols = windInfo.mnCol
m_nRows = windInfo.m_nRow:
mnWindType = -1;
IT ))AFX_DATA_INIT
)
void CWindoTypeDlg::DoDataExchange (CDataxchanged
*p0X) {
CAcUiDialog: :DoDataéxchange (pDX) ;
7/ (AFX_DATA_MAP(CHi ndoTypeD1 a)
DDX_Control(pOX. IDC_TN_PICKPT, mctriBtnPickPt);
DDX_Text(pDX, IDC_EDIT_COLS, mncois);
DDV_MinMaxInt(pOX, maCols, 1, 10);
DDX_Text(pDX, TDC_EDIT_ROWS, mnRows);
DDV_MinMaxInt(pDX, maoRows, 1, 10);
DDX_Control(pOX, 10C_EDIT_HEIGHT, m_ctr1EdHeight);
DDX_Control(pOX, IDC_EDIT_WIDTH, mctrlEdWidth);
DDX_Control(pOX, IDC_EDIT_XVAL, mctr1EdXVal);
DDX_Control(pOx, IDC_EDIT_YVAL, mctrlEdYVal
DDX_Radio(pDX, TOC_RB_TYPERECT, m_nWindType)
TYYAFX_DATA_MAP
}
// Needed for modeless dialogs to keep focus.
// Return FALSE to not keep the focus, return TRUE®
to keep the focus
LONG CWindoTypeDlg::OnAcadkeepFocus(UINT, UINT)
t
)
return TRUE:
ur
BOOL CWindoTypeDIg: :OnInitDialog()
(
CAcUiDialog: :OnInitDialog();
// TODO: Add extra initialization herems
CString str_edboxVal;
CButton *pWindoTypes
CString strWindType(windInfo.windType);
if(strWindType == “Rect”)
ci
plindoType = (CButton *)&
GetD1g1 tem(TDC_RB_TYPERECT) ;
pWindoType->SetCheck(1);
else if(strWindType == “Arch”)
{
pWindoType = (CButton *)¢
GetDI gi tem(TDC_RB_TYPEARCH) ;
pWindoType->SetCheck(1):
}
else // It must be an “Apex” type of window
(
pWindoType = (CButton *)o
GetD1gI tem(1DC_RB_TYPEAPEX) ;
pWindoType->SetCheck(1);
)
mctrlEdHeight.SetRarge(10.00, 120.00);
m_ctrlEdWidth.SetRance(10.00, 120.00);
acdbRToS(windinfo.m_cWindHt, 2, 3.0
str_edboxVal .GetBuffer(80));
m_ctrl€dHeight.SetWirdowText(str_edboxVal);
acdbRToS(windinfo.mcWindWt, 2, 3.0
str_edboxVal .Get8uffer(80)):
mctrl EdWidth.SetWincowText(str_edboxVal);
acdbRToS(windInfo.mcXval, 2, 3,0
str_edboxVal .GetBuffer(80));
m_ctrlEdxVal . SetWindowText (str_edboxVal);
acdbRToS(windInfo.mdYval, 2, 3.2
str_edboxVal .GetBuffer(80));
m_ctrl€dyVal.SetWindowText(str_edboxVal);
m_ctr1BtnPickPt.AutoLoad();MBC Dialogs and ObjatARX UI Extensions 405
$$
return TRUE; // return TRUE unless you set thee
focus to a control
Jf EXCEPTION: OCK Property Pagesd
should return FALSE
)
void CWindoTypeDlg: :OnKil1 focusEditHeight()
t
/1 T0D0: Add your control notification handlerd
code here
m_ctrlEdHeight.Convert();
if(1m_ctrlEdieight.Validate())
fi
AfxMessageBox(“Sorry, Range = 10.00 toe
120.00");
mctrlEdHeight SetFocus():
mctrl€dieight.Setsel(0, “1):
}
}
void CWindoTypeD1g: :Onk‘11 focusEditwidth()
i
// T0D0: Add your control notification handlere
code here
m_ctrlEdWidth.Convert();
if(1mctridWidth, Val idate())
{
riAfxMessageBox(“Sorry, Range = 10.00 tod
120.00");
mctrlEdWidth.SetFocus();
mctrlEdWidth.SetSel(0, -1);
)
)
void CWindoTypeD1g: :On0k()
(
/1 TODO: Add extra validation here
if (UpdateData (TRUE))
fl
return;
)
updateGlobalDocData():
CAcUiDialog: :On0K();28
void CWindoTypeD1g: :OnBtnPickPt()
{
J/ T0D0: Add your control notification handlere
code here
// Here we hide our nodal dialog
// to allow the user to pick a point
AcGePoint3d pkPt;
int retCode;
UpdateData (TRUE);
updateGlobalDocData():
BeginEditorCommand(); // This replaces the nexté
commented out lines
/1CHnd* pacadWnd =
Chind: : FromHand1e(adsw_acadMainWind());
/Ipkcadkind->EnableWindow(TRUE); // Enable thee
parent window
J/ which in our case is the
71 AutoCAD main window.
//ShowWindow(SH_HIDE); // Hide our dialog
//pAcadWind->SetFocus(); // Give AutoCAD the focus.
acedInitGet(NULL, NULL);
retCode = acedGetPoint(NULL, “\nPick lower lefte
corner of window: *,
asDb1Array(pkPt));
switch(retCode)
{
case RICAN :
case RTNONE :
pkPt.set(0.0, 0.0, 0.0);
break;‘MFC Dialogs and ObjectARXs UI Extensions 427
$$ —_$_$__~___
case RTNORM :
break;
case RTERROR :
CancelEditorCommand(); // Most likely won’t®
get here
// 1 just wanted to let you know
// Cancel€ditorCommand() exists.
break;
)
windInfo.m_dXval = pkPt.x
windInfo.m_dyVal = pkPt.y
CompleteEditorCommand(); // This replaces thee
next commented out lines
//ShowWindow(SW_SHOW); // Display our dialog again
//SetFocus(); // Reset the focus back to ourselves
//pAcadiind->EnableWindow(FALSE); // Disable
AutoCAD window
//EnableWindow( TRUE); // Enable our dialog
OnInitDialog’
)
void CWindoTypeD1g: :OnBtnstats()
{
/1 TODO: Add your control notification handler?
code here
if (UpdateData (TRUE))
(
return;
)
updateGlobalDocData();
CWindostatD1g dig;
d1g.DoModa1();
void CWindoTypeDlg: : updateGlobalDocData()m_ctr]EdHei ght .GetWindowText (m_strHei ght);
m_ctrlEdWidth.GetWindowText(m_strWidth) ;
mctr1EdXVal .GetWindowText(m_strXxVal);
m_ctrlEdYVal .GetWindowText(m_str¥Val);
windInfo.m_dWindWt
windInfo.m_dWindHt
windInfo.m_dxVal
windInfo.m_dYVal
windInfo.mnCols = my
windInfo.m_nRows = m_nRows;
atof(mstrWidth):
tof(m_strHeight) :
atof(m_strXval);
atof(m_strYVal);
switch (mnWindType)
(
case 0:
strepy(windInfo.windType, “Rect”):
break;
case 1: .
strepy(windInfo.windType, “Arch”):
break:
case 2:
strcpy(windInfo.windType, “Apex”);
break:
)
}
Look at the DoDataExhange() function. Here, with the help of ClassWizard, we can
map the controls of the dialog edit boxes end radio buttons to the member variables
of the class, complete with DDX (Dialog Data eXchange) and DDV (Dialog Data
‘Validation) mechanisms. This is followed by our “message map,” which responds to
the user selecting the Stats... button and the AutoCAD-style pick point button by
calling the user-defined functions OnBtnStats() and OnBenPickPt() respectively.
‘Taking a closer look at the OnBtnStats() function, we call the UpdateData() func~
tion with an argument of TRUE. This call will transfer values from the controls to the
member variables. We then call the user-defined function updateGlobalDocData(),
which transfers values from the member variables to the global (document-specific)
variables, after which we call our second nested dialog (discussed later). That's all there
is to nesting dialogs in MFC—simply call another dialog. The virtual OnOK( is iden-
tical to the OnBtnStats() function in transferring data from the member variable to
the global variables. Before we leave the OnOK( function, we call the base class‘MRC Dials and Objet RX UL Extensions 929
—$___~_—
CAcUIDialog:OnOK() method. So finaly, on to the big secret of hiding modal
dialogs.
Hiding a modal dialog in MFC is simpler than it is in DCL. In hiding a dialog you
make the dialog invsible—the dialog is stil alive. Focus is passed back to AutoCAD,
the point is selected, and the dialog is made visible again with a return of focus to the
dialog. Here are the details of how this is dane in AutoCAD Ri4—the lines of code
are in the AutoCAD 2000 sample application but they are commented out.
GetParent()->EnableWindow(TRUE); // Enable thee
parent window
// which in our case is the
17 AutoCAD main window.
ShowWindow(SW_HIDE); — // Hide our dialog
GetParent()->SetFocus(); // Give AutoCAD the focus.
In AutoCAD 2000, you simply call BeginEditorCommand(). This simply replaces
the code above.
Selecting a point is as before. Just a quick aside: we are using acedinitGet(NULL,
NULL) here to force an RTNONE. If you remember previously, the function
asDbIArray() returns incorrect values ifthe user just presses ENTER; here we expli
itly set the point selected to (0,0). Now back to where we were for AutoCAD RV
ShowWindow(SH_SHOW); // Display our dialog again
SetFocus(); // Reset the focus back to ourselves
GetParent()->EnableWindow( FALSE); // Disabled
AutoCAD window
EnableWindow(TRUE); // Enable our dialog
However, the lines above are commented out in our sample, and in AutoCAD 2000
you simply call CompleteEditorCommand(). After our point is selected, we simply
redisplay our dialog again and set focus back to it. Disabling the parent window, which
is AutoCAD, follows this. That’s how you hide a modal dialog—pretty simple when
‘you know how, even much easier in AutoCAD 2000. In a sample application later,
‘when we are looking at tabbed dialogs and wizard-style dialogs, there is a slight vari-
ation to hiding the dialog.
We launch our second nested dialog from ‘CWindoTypeDig::OnBtnStats(), which is
simply a matter of calling DoModal( for the second dialog, Here isthe listing for the
WindoStatsDig.h file:
Hite
Idefined(AFX_WINDOSTATDLG_H__EF266281_1CD9_1103_&
ATAC_000000000000__INCLUDED_)defines
‘AFX_WINDOSTATOLG_H__£F266281_1CD9_1103_A7AC_¢
000000000000__INCLUDED_
#if MSC_VER > 1000
pragma once
#endif // _MSC_VER > 1000
// WindoStatOlg.h : header file
“a
TTL TTL
TTT
// CWindoStatD1g dialog
class CWindoStatD1g : public CDialog
(
11 Construction
public:
CWindoStatDIg(CWnd* pParent = NULL): //@)
standard constructor
11 Dialog Data
J (AFX_DATA(CHindoStat1g)
enum { TDD = IDD_WINDOSTAT };
// NOTE: the ClassWizard will add data members@
here
T/SYAFX_DATA
11 Overrides
// ClassWizard genereted virtual functiong
overrides
7/ (AFX_VIRTUAL (CWI ndoStatD1g)
protected:
Virtual void DoDataExchange(COataExchange* pDX):
// DDX/ODV. support
17) YAFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
// (AFX_MSG(CWindoStatD1g)
virtual BOOL OnInitDialog();‘MBC Dialogs and Objet ARX's UI Extensions 43)
JD)VAFXMSG
DECLARE_MESSAGE_MAP()
}
J ((AFX_INSERT_LOCATION) }
// Microsoft Visual C++ will insert additionale
declarations immediately before the previous line.
endif 110
!def ined (AFX_WINDOSTATOLG_H__€F266281_1CD9_1103_¢
A7AC_000000000000__INCLIDEO_)
About the only thing special here is that we used MFC ClassWizard to set up the
‘OninitDialog(). Here is the listing for the WindoStatsDlg.cpp file:
J/ WindoStatD1g.cpp : implementation file
ul
finclude “Stdafx.h”
#include “StdArx.h
finclude “resource.h”
#include “WindoStatD1g.h”
fifdef DEBUG
fdefine new DEBUG_NEW
fundef THIS_FILE
static char THIS_FILEC] = FILE;
ftendif
MUTT
ITT
// CWindoStatOlg dialog
CiindoStatD1g: :CWindoStatD1g(CWnid* pParent /*=NULL*/)
: CDialog(CWindoStatDig::IDD, pParent)
{
/I((AFX_DATA_INIT(CWindoStatD19)
1 NOTE: the ClassWizard will add membere
initialization here
A) YAPX_DATALINIT
)m
void CWindoStat01g: :DoDataExchange(CDataExchange*
pox)
(
(Dialog: :DoDataéxcharge(pOX);
J/(AFX_DATA_MAP(CHirdoStatD1g)
J/ NOTE: the ClassWizard will add DDX and DDVe
calls here
1) AFX_DATA_MAP.
)
BEGIN_MESSAGE_MAP(CWincoStatD1g, CDialog)
7 (AFX_MSG_MAP(CWindoStatD1g)
11) \AFX_MSG_MAP
END_MESSAGE_MAP()
TTL LTT LLL TLL
MALTA
// CWindoStatDlg message handlers
BOOL CWindoStatO1g: :OnInitDialog()
fl
CDialog
nInitDialog():
// TOD0: Add extra initialization here
CListCtri* plist;
plist = (CListCtrI*) GetDlaltem(IDC LIST &
WINDOSTAT) ;
char* arCols[5] = (
“Height”, “Width");
ype", “Cols”, “Rows”,
LV_COLUMN 1vCol;
lvCol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT:
lvCol.fmt = LVCFNT_LEFT;
IvCol.cx = 50;
for(int nColumn = 0; nColumn < 5; nColumn++)
(
1vCol.pszText = arCols{nColumn];
pList->InsertColumn(nColumn, &1VCol):
}MFC Dialogs and Objet ARK UI Extensions 433
$$$
if(stromp(windInfo.windType, “Rect") == 0)
‘ pList->InsertItem(0, “Rect);
tise if(stremp(windInfo.windType, “Arch”) == 0)
‘ plist->Insertitem(0, “Arch”
tise
; pList->InsertItem(0, “Apex”);
char temp[80];
pList->SetItemText(0, 1, itoa(windInfo.mncols.#
temp, 10)):
pList->SetItemfext(0, 2, itoa(windInfo.mnRows,¢
temp, 10)):
acdbRToS(windInfo.m_dWindHt, 2, 3, temp);
pList->SetItemText(0, 3, temp);
acdbRToS(windInfo.m_dvindWt, 2
pList->SetItemText(0, 4, temp);
temp):
return TRUE; // return TRUE unless you set the?
focus to a control
// EXCEPTION: OCX Property Pages®
should return FALSE
}
A\ll the action here happens in OnInitDialog() with the initialization of the CListCtrl.
During our design of the dialog we gave the list control a ‘report’ style layout. Here
is where we initialize the control, giving the control a header. The header of the con~
trol is tied into an LV_COLUMN structure. You can refer to the MFC documenta
tion for more information on the structure LY_ COLUMN. One of the fields of the
LV_COLUMN structure is the mask field Ths field i initialized with bit-coded flags
and basically tells the control how to behave and what parameters are valid for the con~
‘tol. The combination of bit-coded flags we are adding here tells the control that the
control will allow text formatting (LVCF_FMT), that the width of the column can be
set and adjusted with the mouse (LVCF_WIDTH), and also that the columns can
accept text (LVCF_TEXT). The fe field tells us that the text in the column head-
er will be left justified (LWCFMT_LEFT), ard finally, we make the width of each col-a
‘umn 50 pixels. In the for loop is where we put the text for the columns and insert the
columns in the control with the InsertColumn() function.
Inserting lines of text in the list control is a simple call to SetitemText(). We could
use a for loop to insert multiple lines of text in the control, but here we are only insert-
ing one line of text. The SetlternText() also fills out the columns of text fora particular
line of text. Again, this is also zero based. In later applications, instead of using C and
ADS functions for doing our string formatting, we will use MFC’s CString class.
MODELESS DIALOGS AND SAMPLE APPLICATION CH7_3
‘You will be happy to know that handling modeless dialogs in ObjectARX is not much
different from handling modeless dialogs in MFC. The dialog for this application is
almost identical to the dialog for the previous application with the exception of
removing the Stats... button and renaming the OK button (IDOK) to Draw Window.
Figure 7.7 shows the dialog for this application.
Pri re ee ee)
Figure 7.7 Modeless Dialog
‘When you have finished creating the dialog, use the ObjectARX 2000 AppWizard
to create a class called CWindoTypeDlg, derived from CAcUiDialog. Here is the
windoQ) command function that launches our modeless dialog from the
CB7_3Commands.cpp file:
MUTT TTT
// ObjectARX defined commands‘MPC Dialogs and Objet RX UI Extensions
285
aaa
finclude “Stdafx.h
fHinclude “StdArx.h
include “WindoTypeD!g.h”
extern CWindoTypeD1g* gpDlg;
// This is command ‘WINDO”
void windo()
{
// TODO: Implement tte command
if(!gp01g)
(
CAcModuleResourceOverride resOverride:
gpD1g = new CWindoTypeD1g(acedGetAcadFrame());
gp01g->Create(IDD_WINDOTYPE) ;
else
i
rrAfxMessageBox(“Modeless Dialog is alreadyd
active!");
}
)
‘We create the modeless dialog in the windoQ) function. We initialize our
WindoTypeDlg pointer, using the new operator to create an instance of the
(WindoTypeDig class with the AutoCAD frame window as the parent of the mod-
less dialog. Next, we call the create() function with the resource ID of our dialog.
‘That's all there is to creating a modeless dialog. Now remember, when our dialog is
active and visible, we can switch over to AutoCAD and execute any AutoCAD
‘command and then switch back to our dia‘og. Where we have to be careful is when
we close the dialog; we will see this when we discuss the CWindoTypeDlg class.
‘There are a number of differences between the CWindoTypeDig class of this appli-
cation and that of the previous applicatior, mainly due to the fact that we are now
behaving as a modeless dialog. Here is the listing of the WindoTypeDik.h file:
Mr =
#if !defined(ARX_WINDOTYPEDLG_H__19990608_210816)
#define ARX__WINDOTYPEDLG_H__19990608_210816
Ip#if MSC_VER > 1000
fpragma once
#endif // _MSC_VER > 1000
#include “Resource.h”
MS
class CWindoTypeDIg : public CAcUiDialog
DECLARE_DYNAMIC (CWindoTypeD1g)
public:
void updateGlobalDocData():
CWindoTypeDlg (Chind* pParent -NULL, HINSTANCES
hinstance =NULL)
7 ({AFX_DATA(CHindoTypeD1 9)
enum { IDD = IDD_WINDOTYPE };
CAcUiNumericEdit mctrl€dHeight;
CAcUiNumericEdit mctriEdWi del
CAcUiNumericEdit mctriEdXVal
CAcUiNumericEdit mctriEdYVal;
int mnCols;
int mnRows;
int mnWindType:
CAcUiPickButton mctriBtnPickPt;
TI) YAFX_DATA
Cstring mstrHetght;
CString m_strWidth;
CString mstrXVal;
CString mstrYVal;
1 ((AFX_VIRTUAL (CHI ndoTypeD1g)
protected:
Virtual void DoDataExchange(CDataExchange* pDX);
J DDX/DDV support
virtual void PostNcDestroy();
J) )AFX_VIRTUAL
protected:
7/((AFX_MSG(CWindoTypeD1g)
afx_msg LONG OnAcadkeepFocus(UINT, UINT);
virtual void On0K();
virtual BOOL OnInitDialog();(MPC Dialogs and ObjecARX's UI Extensions 437
$$$
afx_msg void Onkil] fccusEditHeight();
afx_msg void Onkill fccusEdi tWidth();
afx_msg void OnBtnPickPt();
virtual void OnCancel();
JD) YAFXMSE
DECLARE_MESSAGE_MAP()
i
1 ({AFX_INSERT_LOCATION} }
// Microsoft Visual C++ will insert additionalg
declarations immediately before the previous line.
fendif //--
Idef ined(ARX__WINDOTYPEDLG_H__19990608_210816)
In this application we used Class Wizard to respond to the PostNeDestroy() and
‘OnCancel() functions, whose implementation we will se later. The OnOKO func
tion is called in response to the selection of the Draw Window button, which is still
the (IDOK) resource ID. We do not destroy the dialog here; we just draw a window.
The dialog still lives until we select the Caneel button. Here is the listing for the
WindoTypeDig.cpp file.
WindoTypeD1g.cpp
Finclude
#include
#include :
Hinclude “WindoTypeD1g.h”
#include “geassign.h”
implementation file
extern CWindoTypeD1g* gp0lg;
I
fifdef DEBUG
fidefine new DEBUG_NEW
fundef THIS_FILE
static char THIS_FILEL)
itendif
FILE:
up :
IMPLENENT_DYNAMIC (CWindoTypeDIg, CAcUiDialog)88
BEGIN_MESSAGE_MAP(CHindoTypeDIg, CAcUiDia1og)
7/((AFX_MSG_MAP(CWindoTypeD1a)
‘ON_MESSAGE(WH_ACAD_KEEPFOCUS, OnAcadKeepFocus)
11 Needed for modeless dialog.
‘ON_EN_KILLFOCUS( IDC_€DIT_HEIGHT.
OnkiT1 focusEditHei ght)
ON_EN_KILLFOCUS( IDC_EDIT_WIDTH,
Onkil1 focusEditwidth)
ON_BN_CLICKED(IDC_BTN_PICKPT, OnBtnPickPt)
7) )AFX_MSG_MAP
END_MESSAGE_MAP()
VL -
CWindoTypeD1g: :CWindoTypeDlg (CWnd* pParente
/*=NULL*/, HINSTANCE hinstance /*=NULL*/) 72
CAcUiDialog (CWindoTypeDlg:: 10D, pParent, hInstance)
(
// ((AFX_DATA_INIT(CWindoTypeD1g)
mnCols = windInfo.mnCols;
m_nRows = windInfo.m_nRows ;
mnWindType = -1;
JT)VAFX_DATA_INIT
}
void CWindoTypeD1g: :DoDataExchange (CDataéxchanged
*p0X) {
CAcUiDialog::DoDataFxchange (pOX) :
7 ((AFX_DATA_MAP(CHindoTypeD1g)
DDX_Control(pOX, IDC_EDIT_HEIGHT. mctriEdHeight):
DDX_Control(pOX, IDC_EDIT_WIDTH, mctrledwidth);
DDX_Control(pOX, IDC_EDIT_XVAL, m_ctr1EdXVal):
DDX_Control(pOX, IDC_EDIT_YVAL, mctriEdvVal):
DDX_Text(pDX, IDC_EDIT_COLS, m_nCols);
DDV_MinMaxInt(pOX, mnCols, 1, 10);
DDX_Text(pDX, 1DC_EDIT_ROWS, mnRows);
DDV_MinMaxInt(pOX, mnRows, 1. 10);
DDX_Radio(pDX, IDC_RB_TYPERECT, m_nWindType) :
DDX_Control(pOX, “IDC_BTN_PICKPT, mctriBtnPickPt):
TI) YAFX_DATA_MAP
)
11 Needed for modeless dialogs to keep focus.
// Return FALSE to not keep the focus. return TRUE?
to keep the focus(MPC Dialogs and ObjetARX's UI Extentions 439,
——$—___~
LONG CWindoTypeD1g: :OnAcadkeepFocus(UINT, UINT)
{
}
“ue
return TRUE;
void CWindoTypeD1g: :0n0K()
(
// TODO: Add extra validation here
CString strMsgl, str¥sg2, strMsg;
if (1UpdateData(TRUE))
{
return:
)
updateGloba1DocData(
strisgl = “If you were to draw a window, &
then\n\n”;
strMsg2.Format(“Type\t = &s\nWidth\t <0
4.21f\nHeight\t = £.21f\nRows\t = &d\nCols\t =
HA\MK\t = B.21F\nY\t = 8.218"
windInfo.windType,
windInfo.mdiindwt,
windInfo.m_diindit,
windInfo.m_nRows,
windInfo.mnCols,
windInfo.mdXval,
windInfo.m_dYva1):
strMsg = strMsgl + strMsg2;
Chind: :MessageBox(strMsg, “Selected Windowg
Parameters”, MB_ICONINFORMATION) ;
)
BOOL CWindoTypeD1g: :OnInitDialog()
{
CAcUIDialog: :OnInitDialog();// TODO: Add extra initialization here
CString str_edboxVal:
CString fName:
CString mainTitle("Window Parameters Modelesso
ooy
‘AcApDocument *pCurrDoc = acDocManagerd)
->curDocument();
fName = pCurrDoc->docTitle();
mainTitle += fName:
this->SetWindowText (mainTitle);
CButton *pWindoTypes
CString strWindType(windInfo.windType);
if(strWindType —= “Rect”)
i
pindoType = (CButton *) GetD1gItem( IDC_RB_¢
TYPERECT);
piindoType->SetCheck(1);
)
else if(strWindType == “Arch”)
i
pWindoType = (CButton *)¢
GetD1 gi tem(10C_R8_TYPEARCH) ;
piindoType->SetCheck(1);
)
else // It must be an “Apex” type of window
{
piindoType = (CButton *)¢
GetD1gItem(1DC_RB_TYPEAPEX) ;
phindoType->SetCheck(1);
)
mctrlEdHeight.SetRange(10.00, 120.00);
m_ctrlEdWidth.SetRange(10.00, 120.00);
acdbRToS(windinfo.m_dWindHt, 2, 3,0
str_edboxVal .GetBuffer(80)):
m_ctrlEdHei ght . SetWindowText(str_edboxVal);
acdbRToS(windinfo.mdWindWt, 2, 3,0
str_edboxVal .GetBuffer(80));(MBC Dialogs and ObjeARX's UI Extensions 444
$$
m_ctrlEdWidth. SetWindowText(str_edboxVal) ;
acdbRToS(windInfo.mdXval, 2, 3,0
str_edboxVal .GetBuffer(80)):
m_ctrlEdXVal.SetWindowText(str_edboxVal);
acdbRToS(windinfo.mdVal, 2, 3,0
str_edboxVal .GetBuffer(30));
m_ctrlEdYVal.SetWindowText(str_edboxVal):
m_ctr1BtnPickPt.AutoLoad();
return TRUE; // return TRUE unless you set thee
focus to a control
// EXCEPTION: OCX Property Pages®
should return FALSE
)
void CWindoTypeD1g: :Onki11 focusédi tei ght()
t
// TODO: Add your, control notification handlers
code here
mctrl€dHeight.Convert();
if(Im_ctrledHeight.Yalidate())
(
riAfxMessageBox(“Sorry, Range = 10.00 tog
120.00");
mctrlEdHeight.SetFocus();
mctrl€dHeight.SetSel(0, -1);
)
)
void CWindoTypeD1g: :Onki11 focusEditwidth()
{
/1 TODO: Add your control notification handlery
code here
m_ctrlEdWidth.Convert();
if(1mctrl EdWidth.Valsdate())
i
AfxMessageBox(“Sorry, Range = 10.00 to
120.00");
mctrlEdWidth.SetFocus();
mctrlEdWidth.SetSei (0, -1);
})
void CWindoTypeD1g: :OnEtnPickPt()
{
/1 TODO: Add your control notification handlerd
code here
// Here we hide our nodeless dialog
// to allow the user to pick a point
AcGePoint3d pkPt;
int retCode;
CString str_edboxVal;
UpdateData (TRUE);
updateGlobalDocData();
J{ShowWindow(SW_HIDE); // Hide our dialog
BeginEditorCommand( );
acedinitGet(NULL, NULL);
retCode = acedGetPoint(NULL, “\nPick lower lefte
corner of window asDblArray(pkPt));
switch(retCode)
(
case RICAN:
case RTNONE :
pkPt.set(0.0, 0.0, 0.0):
break:
case RTNORM =
break;
case RTERROR :
break:
)
windInfo.m_dxval
windInfo.m_dYVal
pkPt. x
pKPt.ysMPC Dialogs and ObjocARX'+ UI Extensions 443
—————__~
CompleteEditorCommand(); // This replaces thed
next commented out Tines
//ShowWindow(SH_SHOW); // Display our dialog again
// Update our X and Y Edit Boxes
acdbRToS(windInfo.m_dXVal ae
str_edboxVal .GetBuffer(80));
m_ctrlEdxVal .SetWindowText (str_edboxVal):
acdbRToS(windInfo.m dYVal,
str_edboxVal .GetBuffer(80)):
m_ctrlEdyVal. SetWindowText(str_edboxVal);
38
)
void CWindoTypeDig:
(
updateGobalDocData()
m_ctrlEdHei ght. GetWindowText(m_strHei ght);
m_ctrlEdWidth.GetWindowText(m_strWidth) :
m_ctrlEdXVal.GetWindowText(m_strXVal)
mctrlEdYVal.GetWindowText(m_strYVal);
windInfo.m_dWindWt = atof(mstrWidth);
windInfo.m_dWindHt = atof(m_strHeight);
windInfo.m_dXVal_ = atof(m_strXval
windInfo.m_dYVal = atof(m_strYVal
windInfo.m_nCols = mnCol
windInfo.m_nRows = mnRows;
switch (mnWindType)
(
case 0:
strepy(windInfo.windType, “Rect”);
break;
case 1:
strepy(windInfo.windType,
break;
‘Arch”) ;
case 2:
strepy(windInfo.windType, “Apex”):
break;void CWindoTypeDlg: :OnCancel()
// TODD: Add extra cleanup here
1 Do NOT call CAcUiDialog: :OnCancel();
// Remember we're modeless,
// Call DestroyWindow() instead!
DestroyWindow();
)
void CWindoTypeD1g: :PostNcDestroy()
(
/1 1000: Add your specialized code here and/ore
call the base class
delete this:
if(gp01g != NULL)
{
gpD1g = NULL;
}
CAcUi Dialog: :PostNcDestroy();
)
‘The constructor and the DoDataExchange() are the same as in the previous appli-
cation. Look at the message map, in particular ON_MESSAGE (WM_ACAD_KEEP-
FOCUS, onAcadKeepFocus). So just whats WM_ACAD_KEEPFOCUS? It certainly
is not one of the messages managed by MFC. This particular Windows message is
fired off by AutoCAD itself. You will find its definition in two locations: in the
sadscodes.b and in the remfeapi. files of the ObjectARX 2000\Inc directory. Now what
is it used for? When you use modeless dialog in AutoCAD, AutoCAD always tries
to regain focus. The effect is, as soon as yeu move your mouse off the modeless dia
log, the modeless dialog loses focus. If you want to see this effect, instead of return
ing TRUE from the CWindoTypeDig:onAcadKeepFocus(, return FALSE. Then run
the application after you have rebuilt it and you will see the focus switch back and forth
between the modeless dialog and the AutcCAD frame window. To avoid this effect,
‘AutoCAD sends out a WM_ACAD_KEEPFOCUS message and if you retumn TRUE,
your modeless dialog will keep focus. Your modeless dialog will lose focus only when
you click inside the AutoCAD main frame area or graphics screen,(MPC Dialogs and ObjecARXs UI Extentions 445
——___.
‘The CWindoTypeDigOnOK( function is called when the user selects the Draw
‘Window button, Remember, the Draw Window button is really the button with the
resource ID of IDOK. This function pops up a Message Box that shows what values
you have selected. It does not dismiss the dialog, as would happen in a modal dialog.
Hiding the dialog to select a point on the graphics screen is handled by the
‘WindoTypeDig:OnBinPickPt() function. Hiding a modeless dialog is different from
hiding a modal dialog and easier too. We simply make the dialog invisible, select our
point, and make the dialog visible once agtin. There is no switching focus from the
dialog to the AutoCAD window and back again—we simply call ShowWindow() with
cither SW_HIDE or SW_SHOW; this works in AutoCAD R14/2000. Here I use the
BeginEditorCommand(/CompleteEditorCommand(), which is AutoCAD 2000
only. Either way works in AutoCAD 2000.
Closing a modeless dialog is a two-step process, very similar to closing a modeless dia~
log in MFC. When the user selects the Cancel button (IDCANCEL), the
‘WindoTypeDig-OnCancel() function is called, in which we call DestroyWindow().
(In hindsight, I should have renamed the Cancel button to Close. Oh well, too
late now) So this represents the first step; take particular note of the comments
where I caution you NOT to call the base class CDialog::OnCancel()—that is just
for modal dialogs. Our dialog window in no longer visible at this stage, but the dia-
Jog object is still very much alive. We used ClassWizard to add a handler for the
PostNcDestroy(). So how does PostNeDestroy() get called and what docs the Ne part
stand for anyway? The Ne part stands for non-client. When Windows destroys a win
dow, the dialog will receive a WM_NCDESTROY message and the default
OnNeDestroy() is called—this is standard Windows behavior. The OnNeDestroy()
function calls the PostNeDestroy() function, and this is where we can perform our
cleanup because PostNeDestroy() is a virtual function. In this function we delete the
pointer to the dialog frame window and call the base class CDialog:PostNeDestroy().
So that’s how a modeless dialog gets destroyed.
Before we move on to our next sample applications, let's revisit global data. We have
already seen the classes CDocData and AsdkDataManager. We have not discussed
the CDocReact user-defined class yet. For this application, these classes are the
same as previously described. Now we are about to jump way ahead of ourselves here
when discussing the class CDocReact. The class CDocReact is a reactor class and
responds to events in AutoCAD. This class is derived from the
AcApDocManagerReactor class. We will discuss notifications and reactors toward
the end of this book. Basically, the AcApDocManagerReactor is responsible for events
that happen at a document (drawing) level. Let’s go through a demonstration first.
|. Create two drawing files and tile the drawing windows vertically2, From one of the drawing windows execute the WINDO command and enter
‘some values in the dialog. Select the Draw Window button; this will write
‘out the data for the active document.
13 Switch the drawing window and enter some new values for the dialog and
select the Draw Window button agai
4, Now, with the modeless dialog acive, switch from drawing window to draw-
ing window.
5, Notice that the title bar of the modeless dialog reflects the name of the cur~
‘rent drawing in addition to the data changing when you switch from drawing
to drawing.
We can achieve this through the use of a reactor, in this case
AcApDocManagerReactor. What we are responding to is when the user switches
from drawing to drawing: CDocReact::documentActivated(). Here is the listing for
the documentedActivated() function, which is located in the rDocManagerReactorpp
file.
void CDocReact: :documentActivated(AcApDocument*¢
pactivatedDoc)
t
// TODO: implement this function.
if (p01)
{
CString fName;
CString mainTitle(*Window Parameters Modeless -@
AcApDocument *pCurrDoc = acDocManagere
—>eurDocument();
fName = pCurrDoc->docTitle():
mainTitle += fName;
gp1g->SetWindowText(mainTitle);
gpD1g->SetFocus();
CButton *pWindoType:
Chind *pEdBox:
CString str_edboxVal;
CString strWindType(windInfo.windType);
if(strWindType — “Rect”)
(
pWindoType = (CButton *) gpDlge
—>GetD1 gl tem( IDC_RB_TYPERECT) ;(MBC Dialogs and ObjeARX's UI Extensions 447
SE
pWindoType->SetCheck(1)
pWindoType = (CButton *) gpDlge?
—>GetD1 gI tem(IDC_RB_TYPEARCH.
pWindoType->SetCheck(0);
pWindoType = (CButton *) gpDlge
—>Get D1 g1 tem( IDC_RB_TYPEAPEX
pWindoType->SetCheck(0
else if(strWindType == “Arch”)
cl
pWindoType = (CButton *) gpDlge?
—>GetD1 gI tem( 1DC_RB_TYPEARCH:
pWindoType->SetCheck(1
pWindoType = (CButton *) gpDlge
—>Get01 gI tem(IDC_RB_TYPERECT:
pWindoType->SetCheck(O
pWindoType = (CButton *) gpDlge
=>GetD1 gI tem(IDC_RB_TYPEAPEX) ;
pWindoType->SetCheck(0) ;
else // It must be an “Apex” type of window
(
pWindoType = (CButton *) gpDlgg
=>GetD1 gl tem(IDC_RB_TYPEAPEX) ;
pWindoType->SetCheck(1);
pWindoType = (CButton *) gpDlgg
=>GetO1 gl tem(IDC_RB_TYPEARCH) :
pWindoType->SetCheck(0);
pWindoType = (CButton *) gpDlge
=>Get01 gl tem(IDC_RB_TYPERECT) ;
pWindoType->SetCheck(0);
PEd80x = gpD1g->GetD1 gitem(10C_EDIT_COLS);
PEdBox->SetWindowText (itoa(windInfo.m_nCols.
str_edboxVal .GetBuffer(80), 10));
PEdBox = gpD1g->GetD1 gltem( 1DC_EDIT_ROWS) ;
PEdBox->SetWindowText (toa (windInfo.m_nRows
str_edboxVal .GetBuffer(80), 10));
acdbRToS(windinfo.n_dWindHt, 2, 3,
str_edboxVal .GetBuffer(80));
gpDlge>m_ctrlEdHei ght . SetWindowText(str_edboxVal);
acdbRT0S(windInfo.m_dWindWt,
str_edboxVal .GetBuf fer(B0)):
‘gpD1g->m_ctrlEdWidth. SetWindowText(str_edboxVa1):
3e
acdbRToS(windInfo.mdXval, 2, 3.0
str_edboxVal .GetBuffer(80));
‘gp01g->m_ctriEdXVal.SetWindowText(str_edboxVal);
acdbRToS(windinfo.mdYVal, 2, 3,0
str_edboxVal .GetBuffer(80));
‘gpD1g->m_ctr1 EdyVal.SetWindowText(str_edboxVal);
}
)
As you can see from this code, when we switch documents we call upon the services
of the global document manager aeDoeManager to retrieve the ttl of the current doc
ument, with which we change the title of the modeless dialog. Also notice how we are
retrieving the global data that pertains to the active document and updating the
fields of the modeless dialog accordingly.
TABBED-STYLE DIALOGS AND SAMPLE APPLICATION
CH7_4
‘Tabbed-style dialogs were introduced with Windows 95 and allow the developer to
pack a lot of information into limited screen real estate. A good example of a tabbed
dialog is the Preferences dialog in AutoCAD. Tabbed dialogs are created through two
classes in MFC, namely CPropertySheet and CPropertyPage. These can be used in
AutoCAD 2000; however, ObjectARX 2000 provides its own derived equivalents,
namely CAcUiTabMainDialog and CAcUITabChildDialog respectively.
(CPropertySheet/CAcUiTabMainDialog is the main frame (parent) of all the tabs in
a tabbed-style dialog. A CpropertyPage/CAcUiTabChildDialog represents each
tab. So let’s go ahead and create one of these tabbed-style dialogs. This application is
in terms of AutoCAD 2000 and CAcUiTabMainDialog; however, we have provided
1 pure MFC version of this application, which uses CPropertySheet/CPropertyPage,
namely Ch7_4MFC, if you want to investigate further.
Figure 7.8 shows the tabbed-style dialog for this application.
We will use the resource editor to create three dialogs, the first of which is shown in
Figure 7.9; it is the dialog for the CWindTypePg class that will be derived from
CPropertyPage.MPC Dialogs and ObjectARX's UI Extensions 449
Career
Figure 7.9 CWindTypePe Property Page
When inserting a new
the Resource... menu
‘our project, from the Insert menu in Visual C++, select.
‘When the Insert Resource dialog appears, expand theDialog option in the tree view. Select the dialog based on IDD_PROPPAGE_SMALL.
‘Table 7.4 shows the settings for the CWindTypePg dialog.
Resource Name
and Type Resource ID Resource Settings
Dialog—"Type IDD_PAGE_WINDTYPE | Style = Child’, Border =
‘None’, Title Bas,
Disabled
Radio button="Rect” | IDC_RB_TYPERECT __| Visible, Group, Tab Stop
and Auto, Caption =
‘BRect?
Radio button=“Arch” | IDC_RB_TYPEARCH _| Visible, Tab Stop and
Auto.
Caption = 8Arch’
Radio button-“apeX” | IDC_RB-TYPEAPEX _| Visible, Tab Stop and
Auto.
Caption = ape8X°
Group Box=“Window | IDC_STATIC Visible, Group
Type” Caption = "Window
Type
‘Allother elements IDC_STATIC Default properties, see
illustration.
‘Table 7.4 Resource IDs for the Dialog Controls of gure 7.9
‘When you have finished creating the dialog, use the Object ARK 2000 AppWizard
to create a class CWindTypePg based on the CAcUiTabChildDialog class. Make sure
you pick CAcUiTabChildDialog as the base class. Figure 7.10 shows the dialog and
‘Table 7.5 shows the settings for the CwindSpeesPg. After you have finished, use
(Class Wizard to create a class based on CacUiTabChildDialog.Figure 7.10 CWindSpecsPe Property Page
‘MPC Dialogs and ObjctARX's UI Extensions 451
Resource Name
and Type
Resource ID
Resource Settings
Dialog - “Window Type”
IDD_WINDOTYPE
‘Style = ‘Child’, Border =
‘None’, Title Bar,
Disabled
Group Box-"Window
Type
1DC_STATIC
Visible, Group
Caption = “Window Type’
Edit box - “Height” IDC_EDIT_HEIGHT || Visible, Tab Stop, Auto
HScroll, Border.
Edit box — “Width? IDC_EDITWIDTH _ | Visible, Tab Stop, Auto
HScroll, Border.
Edit box “Columns” IDC_EDIT_COLS Visible, Tab Stop, Auto|
Scroll, Border.
Edit box “Rows? IDC_EDIT_ROWS —_| Visible, Tb Stop, Auto
Hcroll, Border.
All other elements IDC_STATIC Default properties, see
illustration
Table 7.5 Resource IDs for the Dialog Controls of Figure 7.10Figure 7.11 shows the dialog and Table 7.6 shows the settings for the CwindStartPePg.
‘After you have finished, use ClassWizard to create a class based on
‘CAcUiTabChildDialog.
Start Point
Figure 7.11 CWindStartPtPg Property Poge
—e, Resource ID Resource Settings
Dialog "Stat Point” | IDD_PAGE_WINDSTRIPT | Style - Child’, Border
= ‘None’ Tie Bar,
Disabled
Push button—*< Pick? | 1DC_BTN_PICKPT Visible, Tab Stop
uit box "8X Value” | 1DC_EDIT_XVAL Visible, Tab Stop, Auto
HScrol, Border
Bit box—"®Y Value” | 1DC_EDIT_YVAL Visible, Tab Stop, Auto
HScroll, Border.
Pash button~"Stas..” | IDC_BIN_STATS Visible, Tab Stop
Allotherclements | IDC_STATIC Deefule properties, ee
ilusration.
‘Table 7.6 Resource IDs for the Dialog Controls of Figure 7.11(MPC Dialogs and ObjectARXs UI Extensions 453
$$
Here is the listing for the Cb7_4Commands.cpp file:
MTL TLL
// ObjectARX defined commands
#include “StdAfx.h”
#include “StdArx.h”
J/#include “Resource.h”
J/ This is command ‘WINDO’
void windo()
{
// T0D0: Implement the command
CAcModuleResourceOverride resOverride;
CWindoMainSht windosht;
if(windoSht .DoModal() == 100K)
(
windInfo.m_dWindHt = windoSht.m_specPg.m_dHei ght:
windInfo.m_dWindWt = windoSht.m_specPg.m_dWidth;
windInfo.m_nCols = windoSht.m_specPg.m_nCols;
windInfo.m_nRows = windoSht.m_specPg.m_nRows;
windInfo.m_dXVal = windoSht.mstartPtPg.m_dxval;
windInfo.m_dyVal = windoSht.m_startptPg.m_dYVal;
windInfo.m_nWindType =
windoSht .m_windPg.m_nWindType:
switch(windInfo.m_nWindType)
(
case 0 : // Rect
AfxMessageBox(“Here is where we would haved
drawn an Rect window.");
break;
case 1: // Arch
AfxMessageBox(“Here is where we would haved
drawn an Arch window.”);
break;case 2: // Apex
AfxMessageBox(“Here is where we would haved
drawn an Apex window.”}:
break;
) 17 switch
)
)
Nothing unusual here; CWindoMainSht is derived from CAcUITabMainDialog and
everything else we have seen before.
Let's take a look at the header file for the CWindoMainSht class, namely
WindoMainSht.b:
I —-
#if_Idefined(ARX_WINDCMAINSHT_H_19990626_105244)
define ARX_WINDOMAINSHT_H_19990626 105244
[a a
#if _MSC_VER > 1000
pragma once
#endif // _MSC_VER > 1000
finclude “Resource.h”
#include “StdArx.h”
a0
class CWindoMainSht : public CAcUiTabMainDialog {
DECLARE_DYNAMIC (CWindoMainSht)
public:
CWindoMainSht (CWnd* pParent =NULL, HINSTANCES
hInstance =NULL) ;
J/{(AFX_DATACHi ndoMeinSht )
enum ( TDD = IDD_WINDOSHT };
CAcUiTab m_tabCtrl;
TI) YAFX_DATA
CindTypePg mwindPg;
CWindSpecsPg m_specPg:(MFC Dialogs and Objet ARX's UI Extensions 455
———————u§~
CWindStartPtPg m_startPtPg;
// (AFX_VIRTUAL(CWindoMainSht)
protected:
virtual void DoDataExchange(CDataExchange* pDX):
// DDX/ODV. support.
J) VAFX_VIRTUAL
protected:
//((AFX_MSG(CHi ndoMaiaSht )
afx_msg LONG OnAcadKeepFocus(UINT, UINT);
virtual BOOL OnInitDialog();
HIV AFX_MSG.
DECLARE_MESSAGE_MAP()
Ve
//((AFX_INSERT_LOCATION} }
// Microsoft Visual C++ will insert additionalé
declarations immediately before the previous line.
fendif //
!defined(ARX__WINDOMAINSHT_H_19990626_105244)
Perhaps the most interesting item here is the embedded control m_tabCtri, which is
derived from CAcUITab. This isthe parent for all ofthe tabs, as we will see later. Here
is the implementation for WindoMainSht.cp file:
Wr -
J/--== WindoMainSht.cpp : implementation file
#include “Stdafx.h”
#include “Stdarx.h”
#include “resource. h”
include “WindoMainSht..”
WI
ifdef _DEBUG
iidefine new DEBUG_NEW
undef THIS_FILE
static char THIS_FILEC] = _FILE_;
endif
uy
IMPLEMENT_DYNAMIC (CWindoMainSht, CAcUiTabMainDialog)BEGIN_MESSAGE_MAP(CWindoMainSht, CAcUiTabMainDialog)
77 ((AFX_MSG_MAP (CHindoMainsht)
ON_MESSAGE(WM_ACAD_KEEPFOCUS, OnAcadKeepFocus)
/1 Needed for modeless dialog.
17) VAFX_MSG_MAP-
END_MESSAGE_MAP()
ML —_—-
CWindoMainsht: :CWindoMainSht (CWnd* pParenté
J*=NULL*/, HINSTANCE hinstance /*=NULL*/) :2
CAcUiTabMainDialog (CWindoMainSht::1DD, pParent,&
hInstance) (
//{AFX_DATA_INIT(CWindoMainsht)
ID) \AFX_DATALINIT
)
void CWindoMainSht: :DoDataExchange (CDataExchanged
*p0X) {
CAcUiTabMainDialog: :DoDataéxchange (pDX) ;
//((AFX_DATA_MAP(CWindoMainSht)
DDX_Control(pOX, IDC_TAB1, m_tabCtr1);
7/)YAFX_DATA_MAP.
)
J/ Needed for modeless dialogs to keep focus.
// Return FALSE to not keep the focus, return TRUE?
to keep the focus
LONG CWindoMainsht: :OnAcadKeepFocus(UINT, UINT)
(
return TRUE:
)
I
BOOL CWindoMainSht: :OntnitDialog()
{
CAcUiTabMainDialog: :OnInitDialog();
// TODO: Add extra initialization here
SetAcadTabPointer(&m_tabCtrl);MPC Dialogs and ObjecARX's UI Extensions 457
———_____~_
m_tabCtr1.AddTab(O, _T(“Type"), CWindTypePg: :100,%
&am_windPg);
m_tabCtri.AddTab(1, _T(“Specs”), CWindSpecsPg
&m_specPg);
m_tabCtrl.AddTab(2, _T(*Start Point"), &
CWindStartPteg::10D, &mstartPtPg);
100,
return TRUE; // return TRUE unless you set the?
focus to a control
J EXCEPTION: OCK Property Pages#
should return FALSE
)
‘The most interesting function here is perhaps OninitDialog(), which is where we add
our tabs to the embedded tab control. In the resource editur lovk ut the IDD_WIN-
DOSHT dialog and you will see that we have placed a tab control on the dialog and
used ClassWizard to hook up the control to m_tabCtrl. Look at how we get a
pointer to the embedded tab control using SetAcadTabPointer(). Once we have the
pointer to our tab control, we simply call the AddTab() method of
CAcUiTabMainDialog.
Now let's look at the CWindStartPePg derived property page. We are only going to
look at the CWindStartPtPg class because the other two property page classes are very
similar. Here is the listing for the WindStartPPg.h file:
Ih
ifndef ARX_WINDSTARTPTPG_H__19990626_105823
#define ARX__WINDSTARTPTPG_H__19990626_105823
UL
#if _MSC_VER > 1000
pragma once
#endif // _MSC_VER > 1000
finclude “Resource.h”
aL
class CWindStartPtPg : public CAcUiTabChildDialog (
DECLARE_DYNAMIC (CWindStartPtPg)
public:
CWindStartPtPg (CWind *pParent -NULL, HINSTANCES
hDialogResource =NULL) ;virtual void OnTabActivation (BOOL bActivate) ;
virtual BOOL OnTabChanging () ;
public:
// (AFX_DATA(CHindStartPtPg)
enum { IDD = IDD_PAGE_WINDSTRTPT };
double mdxVa
double mdYVals
J) )AFX_DATA
//((AFX_NIRTUAL(CHindStartPtPg)
protected:
virtual void DoDataExchange(CDataExchange* pOX):
11 DDX/DDV support
1) YAFK VIRTUAL
protected:
7 LAFX_MSG(CWindStartPtPg)
afx_msg void OnBtnPickPt();
LDVYAFX_MSG
DECLARE_MESSAGE_MAP()
Ve
MTT LLL LLL LLL
MMA
J/((AFX_INSERT_LOCATION) )
11 Microsoft Developer Studio will insert additional
declarations immediately before the previous line.
endif //---- ARX_WINDSTARTPTPG_H_19990626_105823
‘As you can see, there is nothing too unusual here. What I want to look at is the imple~
‘mentation of OnBtnPickPt() and in particular, how we hide the property page and
its parent property sheet. Here is the implementation of WindStartPtPg.cppr
=u __§€§_§_| @ ——-
J/—- WindStartPtPg.cpp : implementation file
#include “StdAfx.h”
#include “Stdarx.h”
#include “resource.h”
#include “WindStartPtPa.h”
#include “geassign.h”(MPC Dialogs and ObjetARXs UI Extensions 459
Ws
iifdef _DEBUG
fidefine new DEBUG_NEW
fundef THIS FILE
static char THIS_FILEL] = _FILE_;
endif
It a
IMPLEMENT_DYNAMIC (CWindStartPtPg, &
CAcUiTabChi1dDialog)
BEGIN_MESSAGE_MAP(CWindStartPtPg, &
CAcUiTabChi1dDialog)
7 LAFX_MSG_MAP(CWindStartPtPg)
ON_BN_CLICKED( IDC_BTN_PICKPT, OnBtnPickPt)
1) )AFX_MSG_MAP
END_MESSAGE_MAP()
a ___+_§_—___—_—_.
CWindStartPtpg: :CWindStartPtPg (CWnd* pParent
/*=NULL*/, HINSTANCE hinstance /*=NULL*/) :@
CAcUiTabChildDialog (pParent, hInstance) {
//((AFX_DATA_INIT(CWindStartPtPg)
mdXVal = windInfo.m_dxVal;
mdYVal = windinfo.m_dyVa
/7)\AFX_OATA_INIT
)
void CWindStartPtPg: :DodataExchange (CDatafxchange
*pOX) (
CAcUiTabChi1dDialog: :DoDataExchange (pDX)
7/(CAFX_DATA_MAP(CHindStartPtPg)
DDX_Text(pDX, IDC_EDIT_XVAL, mdXVa1);
DOX_Text(pDX, IDC_EDIT_YVAL, m_dYVal);
JIV)AFX_DATALMAP.
)
I
void CWindStartPtP:
bActivate) {
//---~ T0D0: Add your code here
)
nTabActivation (BOOL?
BOOL CWindStartPtPg::OnTabChanging () {
//---~ TODO: Add your code herereturn (TRUE) ;
)
1
void CWindStartPtPg: :OnBtnPickPt()
(
/1 T0D0: Add your control notification handler
code here
// Were we hide our nodal dialog property sheet
11 to allow the user to pick a point
AcGePoint3d pkPt:
int retCode:
GetParent()->GetParent()->EnableWindow( TRUE);
// Enable the parent window
J/ which in our case is the
JJ AutoCAD main window.
GetParent ()->ShowWindow(SW_HIDE); // Hide oure?
dialog
GetParent()->GetParent()->SetFocus(); // Gived
AutoCAD the focus.
acedInitGet(NULL, NULL);
retCode = acedGetPoint(NULL, “\nPick lower lefte
corner of window: “, asDblArray(pkPt)):
switch(retCode)
"case RICAN:
case RTNONE :
pkPt.set(0.0, 0.C, 0.0);
break;
case RTNORM :‘MPC Dialogs and Objet ARK UI Extensions 464
~~
break;
)
mdXVal = pkPt. x:
mdVal = pkPt.y;
GetParent()->ShowWindow(S_SHOW); // Display ouré
dialog again
GetParent()->SetFocus); // Reset the focus back#
to ourselves
GetParent()->GetParent()->EnableWindow( FALSE): //@
Disable AutoCAD window
GetParent()->EnableWindow(TRUE); // Enable oure
dialog
JI Transfer the data values from the memherd
variables
11 to the dialog.
UpdateData( FALSE);
}
Here we don't use BeginEditorCommand(/CompleteEditorCommand() combi~
nation as we did previously. We simply use MFC functionality with a double call to
GetParent() to enable the AutoCAD mair. frame window. Then we hide our dialog
and hand focus over to the AutoCAD main frame. After picking our point, we go
through the process of showing our dialog, setting focus back to our dialog, disabling
the AutoCAD main frame window, and finally enabling our dialog once again. Why
do we have the double call to GetParent()? Wel, we have to call GetParent() to get
back to the AutoCAD main frame. Remember that the parent of each tab is the
CAcUiTabMainDialog, not AutoCAD itself The single call o GeeParent() is where
‘we want to get back to the parent of the CAcUiTabChildDialog, which is
‘CAcUiTabMainDialog. As you can see, it is relatively straightforward to hide the
CAcUiTabMainDialog dialog.
Hiding a property sheet is no different from hiding a modal dialog. The only thing
that we have to take into consideration is that the parent of the property page is the
property sheet and the parent of the property sheet is AutoCAD. Hence the calls to
the GetParent() function and also the double calls to GetParent() to get the
‘AutoCAD frame window.WIZARD-STYLE DIALOGS AND SAMPLE APPLICATION
CH7_5
This application is a pure MFC, ObjectARK 2000 application that demonstrates how
to use MFC wizard-style dialogs based on CPropertySheet and CPropertyPage. You
will be happy to know that there is hardly any difference between a tabbed-style dia~
log and a wizard-style dialog in MFC. In this application, the dialog and resources are
identical to that of the last application. The only exceptions are that the caption that
appears on each page is different from the previous application and that the border style
for each of the property pages is style thin as opposed to style none in the resource
editor for this application. Here are the dialogs IDs and associated captions:
IDD_PAGE_WINDTYPE ‘Step 1 of 3 - Window Type’
IDD_PAGE_WINDSPECS ‘Step 2 of 3 - Specifications’
IDD_PAGE_WINDSTRTPT ‘Step 3 of 3 - Start Point’
Figure 7.12 shows the wizard-style dialog for this application.
ee
Figure 7.12 Wizard-Style Dialog
‘The main difference between a tabbed-style dialog and a wizard-style dialog is the
method in which they are launched. Here is a listing for the windo() function for this
application, which is located in the C7_SCommands.cpp file:MBC Dialogs and ObjetARXs UI Extensions 463
$$ $$
MALT
// ObjectARX defined ccmmands
fHinclude “StdAfx.h”
fHinclude “StdArx.h”
finclude “WindSpecsPg.h”
finclude “WindStartPtPg. h”
#include “WindTypePg.h”
// This is command ‘WINDO"
void windo()
{
// TODO: Implement the command
CAcModuleResourceOverride resOverride:
CPropertySheet propSht(IDS_TITLE,
acedGetAcadFrame());
CWindTypePg — windPg;
CWindSpecsPg specPg:
CWindStartPtPg startPtPg:
windPg.m_psp.dwFlags &= ~PSP_HASHELP;
specPg.m_psp.dwFlags &= ~PSP_HASHELP;
startPtPg.m_psp.dwFlags &= ~PSP_HASHELP;
propSht.m_psh.dwFlags & ~PSH_HASHELP:
propSht .AddPage(&wind?g) ;
propSht .AddPage(&spec?g);
propSht .AddPage(&startPtPg) ;
propSht. SetWizardMode( );
if(propSht.DoModal() == ID_WIZFINISH )
{
windInfo.m_dWindHt = specPg.m_dHei ght:
windInfo.m_dWindWt = specPg.m_dWidth;
windInfo.m_nCols = specPg.m_nCols;windInfo.m_nRows = specPg.m_nRows;
windInfo.m_dXVal = startPtPg.mdXVal;
WindInfo.m_dYVal = startPtPg.m_dYVal;
windInfo.mnWindType = windPg.m_nWindType:
switch(windInfo.m_nWindType)
(
case 0: // Rect
AfxMessageBox(“Here is where we would haved
drawn an Rect window.”);
break:
case 1: // Arch
AfxMessageBox("Here is where we would haved
drawn an Arch window.");
break;
case 2: // Apex
AfxMessageBox("Here is where we would haved
drawn an Apex window.”):
break:
}
}
)
Property pages belong to a property sheet. After setting our resource handle using
CacResourceModuleOverride, we declare a CPropertySheet in propSht. IDS_TITLE
is the ttle of the property sheet and it is located in the String Table. The property sheet
belongs to the AutoCAD frame window. We then create three property pages. I will
discuss the property page classes in detail later. Following this is some curious bit
‘manipulation. By default, a property sheet has a Help button; here we change the style
of the property sheet by removing the Help button, because we are not going to imple
‘ment a Help facility. Here is the trick to removing the Help button from the prop-
erty sheet: You have to remove the “style” bit from the underlying property sheet
PROPSHEETHEADER structure. The member variable m_psh points to this PROP-
SHEETHEADER structure. Using this member variable, we can remove the style bit
PSH_HASHELP as follows:
PropSht.m_psh.dwFlags & ~PSH_HASHELP;
However, that is not the whole story: if tis is to work, you also have to do the same
for each of the property pages. The property page contains a member variable m_pspMPC Dialogs and ObjetARX's UI Extensions 465
$$
that points to a PROPSHEETPAGE structure. Ifyou were to add an extra page to the
property sheet, you would have to make another bit removal call on the new page to
Temove its PSP_HASHELP style bit. Now if you created the new page but forgot to
add the call to remove the PSP_HASHELP style bit, the Help button on the prop-
exty sheet would appear disabled. When yeu eventually navigated to your new page,
the Help button would then appear enabled. So if you want a Help button enabled
only on certain pages, do not remove the PSP_HASHELP style bit for the page you
are interested in. See the MFC documentation for more information.
Add property sheet pages to the property sheet in the order that you want them to
appear when the tabbed dialog appears on your screen. To turn this into a wizard-style
dialog as opposed to a tabbed-style dialog, call propSht.SetWizardMode() prior to
calling DoModalQ. Look at how we call DoMedal() in this application:
if(propSht.DoModal() == ID_WIZFINISH )
C
/IMore stuff here
)
Here we compare the propSht. DoModal() to ID_WIZFINISH 2s opposed to IDOK.
As you navigate your way through the dialog, on the fist page the Back button is dis-
abled and the Next button is enabled. On the second page both the Back and Next
buttons are enabled. Finally, on the last page we change the caption of the Next but-
ton to Finish. We used ClassWizard to add a handler for the virtual function
‘OnSetActive() on each of the pages in our property sheet. This is where we control
the enabling/disabling and captions of our buttons. Here are the OnSetActive()
functions for each of the classes, where you will see how the buttons are controlled in
‘each of the pages:
BOOL CWindTypeP.
{
CPropertySheet* pParent;
pParent = (CPropertySheet*) GetParent();
pParent->SetWizardButtons(PSWIZB_NEXT);
OnSetactive()
return CPropertyPage: :OnSetActive();
BOOL CWindSpecsPg: :OnSetActive()
(
CPropertySheet* pParent;
pParent = (CPropertySheet*) GetParent();
pParent->SethizardButtons (PSWIZB_BACK |&PSWIZB_NEXT) ;
return CPropertyPage::OnSetActive();
)
BOOL CWindStartPtPg: :OnSetActive()
(
CPropertySheet* pParent;
pParent = (CPropertySheet*) GetParent():
pParent->SethizardButtons(PSWIZB_BACK |o
PSWIZB_FINISH) ;
return CPropertyPage::0nSetActive();
)
(On the final page (CWindStarePePg in our case), we used Class Wizard to add a han-
dler for OnWizardFinish(), which is called when the user selects the Finish button.
Here isthe listing for that function:
BOOL CWindStartPtPg: :OnWizardFinish()
{
UpdateData (TRUE) ;
return CPropertyPage::OnWizardFinish():
)
Let's look at the one of the property pages; here is the listing for WindStartPtPg.b:
Hite
defined (AFX_WINDSTARTPTPG_H__2A92A2A3_37A0_1102_@
A2B9_0080C7D122A2__INCLUDED_)
Hdefined
AFX_WINDSTARTPTPG_H_2A32A2A3_37AD_11D2_A2B9_¢
0080¢70122A2_INCLUDED_
#if _MSC_VER >= 1000
pragma once
#endif // _MSC_VER >= 1000
// WindStartPtPg.h : header file
“ws
finclude “Resource.h”‘MFC Dialogs and ObjctARX'+ UT Extensions 467
$$$
MTT ATLL
TULA
// CWindStartPtPg dialog
class CWindStartPtPg : public CPropertyPage
{
DECLARE_DYNCREATE(CWindStartPtPg)
// Construction
public:
CWindStartPtpg();
~CWindStartPtPa();
// Dialog Data
J/{(AFX_DATA(CMi ndStartPtPg)
enum { 10D = IDD_PAGE_WINOSTRTPT };
double mdXVal;
double m_dYVal;
J) VAFX_DATA
/1 Overrides
// ClassWizard generate virtual function overrides
7/{{AFX_VIRTUAL(CWindStartPtPg)
public:
virtual BOOL OnSetactive();
virtual BOOL OnhizardFinish();
protected:
Virtual void DoDataexchange(CDataexchanger?
POX); // DDX/DDV support
11) )AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
7/(AFX_MSG(CHindStartPtPg)
afx.msg void OnBtnPickPt();
TVYAFX_MSG
DECLARE_MESSAGE_MAP()
ds
/((AFX_INSERT_LOCATION) )// Microsoft Developer Studio will insert additional
declarations immediately before the previous line.
endif //
def ined(AFX_WINDSTARTPTPG_H__2A92A2A3_37AD_11D2_A2B9
0080C7D122A2__INCLUDED_)
Here is the implementation of the WindStartPt.opp file:
J/ WindStartPtPg.cpp : implementation file
WW
finclude “stdafx.h”
#include “StdArx.h”
finclude “WindStartPtPc.h”
ifinclude “gepnt2d.h
ifinclude “gepnt3d.h
#include “adslib.h”
iHinclude “geassign.h”
iHinclude “acedads.h”
fifdef _DEBUG
fidefine new DEBUG_NEW
fundet THIS_FILE
static char THIS_FILEL] = _FILE_;
Hendif
MITTAL LLL
IATL
// CWindStarteteg property page
IMPLEMENT_DYNCREATE(CWindStartPtPg, CPropertyPage)
CWindStartPtPg: :CWindStartPtPg() :¢
CPropertyPage(CWindStartPtPg: : 100)
(
// (AFX_DATA_INIT(CWindStartPtPg)
m_dXVal = windInfo.m_dxval;
m_dYVal = windInfo.m_dYVal;[MBC Dialogs and ObjetARX% UL Extentions 469
$$$
JA) )AFX_DATA_INIT
)
CWindStartPtpg: :~CWindStartPtPg()
(
)
void CWindStartPtPg: :DcDataExchange(CDataExchange*?
pox)
i
CPropertyPage: :DoDataExchange(pDX) ;
//{(AFX_DATA_MAP(CWindStartPtPg)
DDX_Text(pDX, IDC_EDIT_XVAL, m_dXVal);
DDX_Text(pDX, IDC_EDIT_YVAL, m_dYVal);
17) YAFX_DATA_MAP
BEGIN_MESSAGE_MAP(CWindStartPtPg, CPropertyPage)
//({AFX_MSG_MAP(CWindStartPtPg)
ON_BN_CLICKED(IDC_BTN_PICKPT, OnBtnPickPt)
1) )APX_MSG_MAP
END_MESSAGE_MAP()
TTL TTL
IATL
/1 CWindStartPtPg messege handlers
void CWindStartPtPg: :OrBtnPickPt()
(
// Here we hide our modal dialog property sheet
// to allow the user to pick a point
AcGePoint3d pkPt:
int retCode:
GetParent()->GetParent ()->EnableWindow( TRUE) ;
// Enable the parent window
JJ which in our case is the
71 AutoCAD main window.
GetParent()->ShowWindow(SW_HIDE); // Hide oure’
dialog
GetParent()->GetParent()->SetFocus(); // GivedAutoCAD the focus.
acedInitGet(NULL, NULL);
retCode = acedGetPoint (NULL, “\nPick lower lefté
corner of window: “, asDblArray(pkPt));
switch(retCode)
{
case RTCAN
case RTNONE :
pkPt.set(0.0, 0.0, 0.0);
break;
case RTNORM =
break;
)
mdXVal = pkPt.x:
m_dYVal = pkPt.y:
GetParent()->ShowWindow(Sh_SHOW): // Display ourg
dialog again
GetParent()->SetFocus(); // Reset the focus back?
to ourselves
GetParent()->GetParent()->EnableWindow( FALSE); //#
Disable AutoCAD window
GetParent()->EnableWindow(TRUE); // Enable oure
dialog
// Transfer the data values from the memberd
variables
// to the dialog.
UpdateData( FALSE);‘MEG Dialogs and ObjetARX's UI Extensions
$$$.
OnSetActive()
BOOL CWindStartPtPs
{
// TODO: Add your specialized code here and/ore
call the base class
CPropertySheet* pParent;
pParent = (CPropertySheet*) GetParent():
pParent->SetWizardButtons(PSWIZB_BACK |@
PSWIZB_FINISH’
return CPropertyPage::OnSetActive():
)
BOOL CWindStartPtP:
{
OnWizardFinish()
J TODO: Add your specialized code here and/ore
call the base class
UpdateData( TRUE);
return CPropertyPage::OnWizardFinish(
)
As you can see, there is not too much difference between the OnBtnPickPt( func-
tion of this application and the last application.
Before we move on to our next application, le’s look atthe issue of Windows 95 and
ObjectARX with reference to MFC-style property pages. There is a problem and here
is my limited understanding of what happeas in Windows 95 (please don't quote me
‘on this). In early versions of Windows 95, when property pages from DLLs were used
(ObjectARX applications are DLLs), the resources were read only and when a prop-
erty page wanted to access a resource. An exception would be thrown, leaving you
ting on the desktop or rebooting you computer. This was fixed in later versions of
‘Windows 95 and was never a problem in Windows NT: To get around this problem,
you have to copy the resources into memory and access them from there. This
applies to every property page that your property sheet uses. Let us illustrate this with
some sample code:
CWindStartPtPg::CWindStartPtPg() : CPropertyPage ()
(
//(AFX_DATA_INIT(CCustomi zeUi Page)
m_dXVal = windInfo.startPt.x;
m_dYVal = windInfo.startPt.y:
77) VAFX_DATALINITHRSRC_hResInfo = FindResource(_hd11 Instance, &
MAKEINTRESOURCE( TDD), RT_DIALOG) :
HGLOBAL hRes = LoadResource(_hdl1 Instance, @
hRes Info);
LPVOID pResource = LockResource(hRes);
WORD size = SizeofResource(_hdIlInstance,
hResInfo);
LPCDLGTEMPLATE pResourceCopy =<
(LPCDLGTEMPLATE)new BYTE [size]:
memcpy((void *)pResourceCopy, pResource, size):
InitModa1 Indirect (rResourceCopy);
mpsp.dwFlags |= PSP_DLGINDIREC
m_psp.pResource = pResourceCopy:
Ignore what is in the AFX_DATA_INIT comments; that was placed there by the
Class Wizard. The code that follows it must be placed in the constructor of each prop-
erty page that you plan to use. I did not writ this code—my knowledge of Windows
does not go that deep. Essentially, we are copying our resources and loading them into
locked global memory. When we create our property page indirectly, we get the
resources from memory. So now we dont have to worry about the read only problem.
This also works in Windows NT. If this is « concern to you, make sure that this code
is added to the constructor of every property page that you intend to use.
‘The ObjectARX documentation discusses this problem in more detail and it shows
a class derived from CPropertyPage called IndirectPage. So if you are to use this
method, derive your classes from IndirectPage as oppoved to CPropertyPage.
However, you will have to include the header and implementation files in your pro-
ject if you want to use this method. Both methods work—I prefer using the first
method.
In the next application we will demonstrate how to use a €TooIBar control in com-
bination with a modeless dialog and also a CTreeView control.
COMMON CONTROLS AND SAM
CH7_6
In this application we demonstrate how to use a toolbar (ToolBar) and a tree view
control (CTreeCtrl), The toolbar is launched from a modeless dialog and the tree view
control is launched from the toolbar. The tree view control is embedded inside a modal
dialog. We will start out by listing the Ch7_6Commands.pp file, which is where we
launch our modeless dialog CWindoTypeDlg. Here is the listing:
E APPLICATIONMPC Dialogs and ObjecARX's UI Extensions 473
$$.
TTT TLL TTD
// ObjectARX defined commands
ifinclude “StdAfx.h”
diinclude “StdArx.h”
extern CWindoTypeD1g *c_pWindoTypeDlg:
extern CAcToolBar *g_pAcToolBar;
“1 This is command *WINDO”
void windo()
(
// TODO: Implement the command
CAcModuleResourceOverride resOverride;
g_pWindoTypeDlg = new
CWindoTypeD1g(acedGetAcadFrame());
9_pWindoTypeD1g->Create( ID_WINDOTYPE, &
acedGetAcadFrame());
g_pWindoTypeD1g->CenterWindow():
g_pWindoTypeD1g->Showilindow(SH_SHOW) :
if(g_pAcToolBar != NULL && g_pAcTool Bare
—>IsWindowVisible())
(
((CButton*) g_pWindoTypeDlge
—>GetD] gl tem( IDC_CHK_VISTB))->SetCheck(1):
)
‘When we first run the application, our toolbar will not be visible and therefore the
check box will not have a check mark. Here we are checking to see if our toolbar point
et is not NULL and also ifthe toolbar window visible; this could be the case when we
run the application again later. If so, we turn on the check button in the modeless dia-
log (we will see this in a while).
Here is the listing for the Ch7_6.™
HINSTANCE _hd11Instance =NULL ;
// This command registers an ARX command.
void AddCommand(const char* cmdGroup, const char*#
cmdint, const char* cmcLoc,
const int cmdFlags, const AcRxFunctionPtre)
cmdProc, const int idlecal = -1);
/1 NOTE: DO NOT edit the following lines.
TH ((AFX_ARK_MSG:
void InitApplication();
void Unloadapplication();
TT) YAPX_ARX_MSG
// NOTE: D0 NOT edit the following lines.
7 (AFX_ARX_ADDIN_FUNCS
7/)) AFX_ARX_ADDIN_FUNCS
CWindoTypeD1g* g_pWindoTypeD1g = NULL;
CAcToolBar* —g_pAcToolBar = NULL:
CTBGenWind* — g_pTBGenWnd = NULL:
MALT TLL
ITLL
WV
// Define the sole extension module object.
‘AC_IMPLEMENT_EXTENSION_MODULE(Ch7_6DLL);
// Now you can use the CAcModuleResourceOverrided
class in
// your application to switch to the correcte
resource instance.
// Please see the ObjectARX Documentation for mored
details
MALTA TTL
TTT
77 DLL Entry Point
extern “C”(MBC Dialogs and ObjectARXs UI Extensions 475
$$
BOOL WINAPI D11Main(HINSTANCE hinstance, DWORD®
dwReason, LPVOID /*1pReserved*/)
fl
if (dwReason == DLL_PROCESS_ATTACH)
{
hdl lInstance = hinstance;
// Extension DLL one time initialization
Ch7_6DLL.AttachInstance(hInstance);
InitacUiDLLO;
} else if (dwReason = DLL_PROCESS_DETACH) {
// Terminate the library before destructors®
are called
Ch7_6DLL.DetachInstance();
)
return TRUE; // ok
)
UMMM
TATTLE
// ObjectARK EntryPoint
extern “C” AcRx: :AppRetCode
acrxEntryPoint(AcRx: :AppMsgCode msg, void* pkt)
{
switch (msg) {
case AcRx: :kInitappMsg:
11 Comment out the following line if your
// application shold be locked into memory
acrxDynami cLinker->unlockApplication(pkt);
acrxDynamicLinker->registerAppMDIAware(pkt) ;
Initapplication():
break:
case ACRx::kUn1oadAppls
UnloadApplication();
break:
}
return AcRx: :kRetOK:
)
// Init this application. Register your
// commands, reactors...
void InitApplication()6
(
// NOTE: DO NOT edit the following lines.
J/((APX_ARX_INIT
AddCommand(“CH7_APPS”, “WINDO”,
ACRX_CMD_MODAL, windo);
1 D)\APX_ARX_INIT
// T0D0: add your initialization functions
acutPrintf(“\nType \"NINDO\” to execute”
}
JI Unload this application. Unregister a11 objects
11 registered in InitAppl ication.
void UnloadApplication()
(
// NOTE: DO NOT edit the following lines.
I ({APX_ARX_EXIT
acedRegCnds->removeGraup( “CH7_APPS”)
71) YAFX_ARX_EXIT
// TODO: clean up your application
}
JJ This function registers an ARX command.
// It can be used to read the localized command name
// from a string table stored in the resources.
void AddCommand(const char* cmdGroup, const char+o
cmdInt, const char* cmdLoc,
const int cmdFlags, const AcRxFunctionPtr
cmdProc, const int idlccal)
(
char cmdLocRes(65];
// If idlocal is not -1, it’s treated as an ID for
// a string stored in the resources.
if Cidlocal I= -1) [
J/ Load strings from the string table ande
register the command.
oadString(_hdilInstance, idlocal, cmdLocRes,@
acedRegCmds->addConmand(cmdGroup, cmdint, &
cmdLocRes, cmdFlags, cndProc) ;(MBC Dialogs and ObjectARX's UI Extensions 477
$$
} else
J/ idlocal is -1, so the ‘hard coded’
1/ Vocalized function name is used.
acedRegCmds->addConmand(cmdGroup, cndInt,&
cmdLoc, cmdFlags, cmdProc);
)
void drawWindo()
(
switch(windInfo.mnWindType)
(
case 0:
AfxMessageBox(“Here I would have drawn a Rect
window.”);
break:
case 1:
AfxMessageBox(“Here I would have drawn a Arch
window."):
break:
case 2:
AfxMessageBox(“Here I would have drawn @ Apexe
window.”
break;
)
)
‘The only elements in this listing that I want to draw your attention to are the glob-
al pointers to some of the windows that our application implements; we will discuss
those later.
CWindoTypeDig* g_pWindoTypeDlg = NULL;
CAcToolBar* — g_pAcToolBar = = NULL
CTBGenwind* 9_pTBGenWind = NULL:
Briefly, the CWindoTypeDig is the modeless dialog we have seen before, with just
some slight modifications. The CAcTooIBar is a class that is derived from the base
class CToolBar and represents our application’s toolbar. The CTBGenWned class is
derived from the genetic CWnd class. This window will be invisible in our applica-
tion; its sole purpose is to process the messages coming from the toolbar (more on that
later).478
Figure 7.13 shows the modeless dialog for this application.
comes
Cer
Figure 7.13 Modeless Dialog with Spin Controls
Look closely at the Columns and Rows edit boxes: they have spin controls (CSpinCtri)
attached to them. The spin controls are initialized in the OnInitDialog() function of
the CWindoTypeDig clas. Table 7.7 shows the settings and resource IDs for the mod-
cless dialog that is represented by the CWindoTypeDlg class.
Resource Name
and Type
Resource ID
Resource Settings
Dialog -“Window
Parameters - Modeless”
IDD_WINDOTYPE
Style = Popup’, Border =
‘Dialog Frame’, Title Ba,
System menu
Radio button ~ “Rect”
IDC_RB_TYPERECT
Visible, Group, Tab Stop
and Auto. Caption =
‘BeRect?
IDC_RB_TYPEARCH
Visible, Tab Stop and
Auto.
Caption = '8&Arch’
‘Table 7.7 Resource IDs for the Dialog Controls of Figure 7.13MRC Dialogs and Objet ARX's UI Extensions 479
$$$
Type”
era ece Resource ID Resource Settings
Radio button="apeX” | IDC_RB_TYPEAPEX | Visible, Tab Stop and
Auto,
Caption = ‘apeBex’
Group Bax—"Window | IDC_STATIC Visible, Group
Caption ="Window Type’
Bait box “Height” IDC_EDITHEIGHT | Visible, Tab Stop, Auto
HScroll, Border
Edit box— "Width? IDC_EDIT.WIDTH __ | Visible, Tab Stop, Auto
HScroll, Border.
Bai box ~ "Columns" IDC_EDIT_COLS Visible, Tab Stop, Auto
HScroll, Border.
Spin Control IDC_SPIN_COLS. Visible, Auto Buddy, Set
buddy integer, Arcow
keys, Alignment « Right’.
Edit bor ~ “Rows” IDC_EDIT.ROWS | Visible, Tb Stop, Auto
HScrol, Border.
Spin Control IDC_SPIN ROWS Visible, Auto Buddy, See
buddy integer, Arrow
keys, Alignment = ‘Right
Push button ~“« Pick”
IDC_BTN_PICKPT
Visible, Tab Stop
Editbox-“8&X Value” | IDC_EDIT_XVAL Visible, Tab Stop, Auto
HScroll, Border.
Edit box-"&Y Value” | IDC_EDIT_YVAL Visible, Tab Stop, Auto
HScroll, Border.
(Check Box - ‘Show 1DC_CHK_VISTB Visible, Tab Stop, Auto.
Toolbar’
All other elements IDC_STATIC Default properties see
illustration,
‘Table 7.7 Resource IDs for the Dialog Controls of Figure 7.13 (continued)This modeless dialog is similar to the modeless dialog of the (b7_3.arx application
with the exception of the handler for the radio button and the addition of spin con-
trols, which are initialized in the OntnitDialog( function. Instead of listing the
header and implementation files, I will list only the OntnitDialog() and the
OnChkViewToolBar() functions. Here is the listing for the OntnitDialog() function:
BOOL CWindoTypeD1g: :OnInitDialog()
{
CDialog: :OnInitDialoc();
CSpinButtonctr1* pspin;
pSpin = (CSpinButtonctri*)o
GetD1 gI tem( IDC_SPIN_COLS);
pSpin->SetRange(1, 10);
pSpin = (CSpinButtonCtri*)o
GetDigItem(1DC_SPIN_ROWS);
pSpin->SetRange(1, 10);
return TRUE: // return TRUE unless you set the
focus to a control
71 EXCEPTION: OCX Property Pages?
should return FALSE
}
This is pretty straightforward; we are just initializing the range of our spin controls.
‘When the Columns and Rows edit boxes are active, you can also use the arrow
keys on the keyboard to adjust the values of the edit control.
Here is the listing for the OnChkViewToolBar( function, which is called asa result
of selecting the check box to display the toolbar:
void CWindoTypeD1g: :OnChkViewToolBar()
{
CMDIFrameWnd *pAcadFrame = acedGetAcadFrame();
if(g_pAcToolBar != NULL && g_pAcTool Bare
—>IsWindowVisible())
(
((CButton*) GetD1gitem( 1DC_CHK_VISTB))
=>SetCheck(0);
pAcadFrame->ShowControlBar(g_pAcToolBar, FALSE, #
FALSE);
GetO1gI tem( 1DC_8TN_HIDEDLG)->EnableWindow( FALSE) ;
}