Autocad DCL Dialog Forms

Dialog Control Language – DCL – was introduced in its final form 25 years ago. If you have created forms with VBA, DCL has a tiny fraction of the capability. It uses archaic unfamiliar terms and it is evidently hard to describe as no one does it well. My advice is to find a template and create some working code as you try to read about it. The best template and teaching approach I have used is Jeffery Sanders website. He has a good basic template at the bottom of the very first page.

JefferySanders AutoLisp DCL Tutorial

There are two components – the DCL file and the Lisp file. The DCL file looks confusing at first but its not. The basic element is the Tile, in VBA that is called a Control. There are only a few Tiles that the user actually uses – textbox, button, listbox, popup list, radio button and toggle. There are several other column and row spacings which are technically tiles but you can leave them out of your first programs. Each Tile has attributes, in VBA called Properties. The most important attribute is called a Key, in VBA that is just the Name. The structure of tile and attributes is not hard to learn if you just start typing. Develop your own style of formatting and indenting. As you add rows and column objects, indent the Tiles so the Colon for each level is in a vertical line on the page.

Here is my style that i can read at a glance without counting brackets.

The visual lisp IDE has a DCL preview tool that is essential to get working. It is broken by default (or perhaps neglect). AfraLISP has clear instructions here to change permissions on your autocad installation folder.

AfraLisp Previewing DCL Files

Autodesk help has a LSP-DCL example overview.

Example: Quick Overview of Dialog Boxes (DCL)

DCL

hello  : dialog {
	          label = "sample box";
               	  : text { label = "Hello world"; }
       ok_only;
}

LSP

(defun C:HELLO ( / dcl_id )
  (setq dcl_id (load_dialog "hello.dcl")) ; Load the DCL file.
  (if (not (new_dialog "hello" dcl_id))   ; Initialize the dialog.
    (exit)                                ; Exit if this does not work.
  )
  (start_dialog)                          ; Display the dialog box.
  (unload_dialog dcl_id)                  ; Unload the DCL file.
 (princ)
)

Running this quickly shows it runs, displays and closes but it does not do anything. There is no input from the user. The mystery of these files remains – how is user input processed when they close?

start-dialog is running when the dialog is displayed, when the dialog is closed, start-dialog returns a status number. if the builtin ok_cancel buttons are used in the dcl file, the numbers are 0 for cancel and 1 for ok. that is what is happening behind the scenes in the program above, though there is no ok button.

You can start modifying the above program by adding an ok button, and capture the integer returned by start_dialog as it closes the dialog. The built-in ok_cancel buttons have built-in keys of “accept” and “cancel.” With that knowledge you can build an action_tile statement in your lisp program that is run when that button is pushed. Eventually we need to run our own program when the ok button is pushed.

(action_tile “accept” “(done_dialog 1)”) will cause the start_dialog exit and return the number 1 when the ok button is pushed. that happens to be the same number as the default, you could choose any number and start_dialog will return that number. The string of lisp commands is quoted so it does not evaluate when first loaded. This is why you just have to start running code because its so hard to explain. This area of how it closes and how user input is captured is the key part that is hardest to get from the manuals.

To make this overview program do something user related lets take this intermediate step where we add an edit_box, an ok_cancel assembly and examine the start_dialog return values. Adding some error checking to the dcl loading is straightforward.

DCL

hello2  : dialog {
           label = "hello2";

                  : edit_box {
                               label = "type in the box";
                                 key = "hellobox";
                                  value = "hello world";
                                  width = 50; }

ok_cancel;
}

LSP

(defun c:hello2 ( / dcl_id)

;'can the file be found and loaded
;'load_dialog returns positive integer when successful
;'returns a negative integer when not successful

  (setq dcl_id (load_dialog "c:\\lisp\\dcl\\hello.dcl"))
  (if (< dcl_id 0)
   (progn
      (alert "The Hello.DCL file could not be loaded.")
      (alert "Check DCL file location is in Search Path")
      (exit)))

;'(alert (rtos dcl_id))

 ;'new_dialog returns T or nil
 (if (not (new_dialog "hello2" dcl_id))
        (progn
        (alert "DCL file loaded but not definition, internal problem with files")
        ;'this might even be inconsistent capitalization of the dialog name
        (exit)))

;'as an experiment uncomment these lines
;'these numbers are returned by start_dialog
;'they mean whatever you want them to mean - they overwrite default 0 and 1
;'accept and cancel are the built-in key names you need to know for the ok_cancel sub-assembly
;'(action_tile "accept" "(done_dialog 3)")
;'(action_tile "cancel" "(done_dialog 12)")

;'by default start_dialog returns 0 if cancel, 1 if OK, -1 if all boxes term_dialog
;'alternatively, if done_dialog argument is an integer > 1
;'then start_dialog returns that value which is user-application defined

(setq d (start_dialog))
(unload_dialog dcl_id)

(alert (itoa d))
)

now maybe we have some understanding of how the built-in buttons pass values behind the scenes to the start-dialog, we can do the same thing with explicitly created buttons in the dcl. we don’t have to do that, but perhaps it is simpler to see how it works. The last most important piece of the puzzle, where and how do we save user input? i take this technique from the jefferysanders template above, create a (savevars) function and put it in the action_tile statement for the ok button. Test the number returned by start_dialog after exiting the dialog to see if it gets used. now the helloworld demo file returns whatever the user types in to the box.

DCL

hello3  : dialog {
           label = "hello3";

                  : edit_box {
                               label = "type in the box";
                                 key = "hellobox";
                                  value = "hello world";
                                  width = 50; }

                  : row {
              
                          : button {
                                     key = "accept";
                                   label = " Okay ";
                              is_default = true; }
                          : button {
                                     key = "cancel";
                                   label = " Cancel ";
                              is_default = false;
                               is_cancel = true; }
                        }
 }

LSP

(defun c:hello3 ( / dcl_id)

;'can the file be found and loaded
;'load_dialog returns positive integer when successful
;'returns a negative integer when not successful

  (setq dcl_id (load_dialog "c:\\lisp\\dcl\\hello.dcl"))
  (if (< dcl_id 0)
   (progn
      (alert "The Hello.DCL file could not be loaded.")
      (alert "Check DCL file location is in Search Path")
      (exit)))

 ;'new_dialog returns T or nil
 (if (not (new_dialog "hello3" dcl_id))
        (progn
        (alert "DCL file loaded but not definition, internal problem with files")
        ;'this might even be inconsistent capitalization of the dialog name
        (exit)))

;'now using explicitly created button tiles rather than built-in
;'but it is not necessary, same results are with hello2.dcl
(action_tile "cancel" "(done_dialog 0)")
(action_tile "accept" "(savevars) (done_dialog 1)")


;'by default start_dialog returns 0 if cancel, 1 if OK, -1 if all boxes term_dialog
(setq d (start_dialog))


(unload_dialog dcl_id)

;'pop up contents of edit_box if ok is pushed
(if (= d 1) (alert textval))
)


(defun savevars ()
(setq textval (get_tile "hellobox")))

Advertisements