Author Topic: Using modeless forms in MDI mode  (Read 8907 times)

Fred Tomke

  • OpenDCL Technician
  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2062
  • [ Mr. Bad Guy ]
    • Kommunale Entwicklungsplanung auf digitaler Basis - digital landscape and urban design
Using modeless forms in MDI mode
« on: December 11, 2009, 06:00:16 AM »
The advantage of a modeless form, a dockable form and a palette is to allow the user to work in the drawing as well as in the active form at the same time. If the multiple document interface is active (SDI=0), the user can also switch between drawings. In that case your task is to make sure that all events of the form and all the functions, which can be called by the events, and finally the variables, which are used by the events and functions, are defined in all drawings, even in the newly created or opened drawings.
You’ll need a concept to divide the variables which can only be used in the current drawing (lists of layernames, for instance) from those which are needed in all drawings (control settings or states). Variables with values, which are needed in all drawings can be addressed by the AutoCAD blackboard using the functions vl-bb-set and vl-bb-ref.

Change the dialog contents in case of switching between drawings
For the case the form shows document-related information, it is necessary to update the form after switching from one drawing to another. This can be solved by using the event OnDocActivated. Activate the form in the OpenDCL Studio and activate the event DocActivated in the events tab. Copy the function definition in your code and define the tasks AutoCAD has to do after switching.
In this sample the blockview control has to be cleaned up because the last selected block is not defined in the currently activated drawing. To avoid that the returning value of the Clear-method is given back to the command line, set a (princ) into the last line.
Code: (autolisp) [Select]
(defun c:blocklist_blocklist_OnDocActivated (/)
  (dcl_BlockView_Clear blocklist_blocklist_prv_block)
  (princ)
); c:blocklist_blocklist_OnDocActivated

Change the dialog contents in case of opening a new drawing
The OnDocActivated event will not released after creating a new drawing or opening an existing drawing. If it would be released, maybe the AutoLisp context does still not exist for the drawing until the opening progress has been finished. That’s why it has to be managed manually. Since the AutoLisp-file has to be loaded in every document, it is very easy to add a line in the end of the lisp file to check whether the form is open or not, and if so then you can call the OnDocActivated event manually.
In the end of the attached sample file there you can find the line
Code: (autolisp) [Select]
(blocklist_execute blocklist_blocklist 'c:blocklist_blocklist_OnDocActivated nil)The blocklist_execute function checks if the form blocklist_blocklist is active. If so, then the OnDocActivated event function will be called manually.
Code: (autolisp) [Select]
(defun blocklist_execute (oControl symFunc lstArg / uErg)
  (if (and oControl (dcl_Form_IsActive oControl))
    (if (dcl_Form_IsVisible oControl)
      (if symFunc (setq uErg (vl-catch-all-apply symFunc lstArg)))
      (dcl_Form_close oControl)
    ); if
  ); if
  uErg
); blocklist_execute

Handle the zero document state
It's up to the implementer of a modeless form to decide how it should behave when entering zero document state. The safest way to handle it is to just close the form. Otherwise, you have to ensure that the needed code and variables load again when a drawing is opened. Note that no events fire while there is no document open.
In the following example the form would be closed if the last drawing is going to be closed.
Code: (autolisp) [Select]
(defun c:blocklist_blocklist_OnEnteringNoDocState (/)
  (if (and blocklist_blocklist (dcl_form_IsActive blocklist_blocklist))
    (dcl_form_close blocklist_blocklist)
  ); if
); c:blocklist_blocklist_OnEnteringNoDocState

Loading the code in all drawings
The most important thing in using a modeless form is to make sure that all the events and functions are already loaded when the drawing is going to be activated. There are many ways to solve. I can give you a small list of ways to solve. Your choise depends on your application and your goals.

Loading AutoCAD using an external application
In the case you start AutoCAD from an external application, you have to make sure that after starting AutoCAD you have to send a command line string to the current drawing. It is recommended to use the VisualLisp-function vl-load-all to make sure, that the Lisp-file will be loaded in all currently and newly opened drawings.
Code: [Select]
Public Sub AcadStart()
  Dim oAcad As AcadApplication
 
  ' Is AutoCAD already loaded
  Set oAcad = GetObject(, "AutoCAD.Application")

  ' If not it shall be started
  If TypeName(oAcad) = "Empty" Then
    Set oAcad = CreateObject("AutoCAD.Application")
  End If

  oAcad.Visible = True
 
  oAcad.ActiveDocument.SendCommand ("(vl-load-com")
  oAcad.ActiveDocument.SendCommand ("(vl-load-all " & Chr(34) & "blocklist.lsp" & Chr(34) & ") ")
End Sub

Loading Lisp-File using acad.lsp
If AutoCAD starts it checks all the support paths for an existing acad.lsp. It will be loaded only into the first drawing if the value for the system variable ACADLSPASDOC is 0. If the value for ACADLSPASDOC is 1, the lisp file will be loaded into every single drawing.
If you want to load your application using acad.lsp you should add your application path to the AutoCAD support path. In your application path you have to create an ASCII file named “ACAD.LSP” with the following contents:
Code: (autolisp) [Select]
(vl-load-com)
(if (findfile "blocklist.lsp") (vl-load-all "blocklist.lsp"))

Loading Lisp-File using acaddoc.lsp
If a new drawing is being opened, AutoCAD checks all the support paths and the folder of the current drawing for an existing acaddoc.lsp and loads it for the current AutoLisp-context. Since a acaddoc.lsp will be loaded into each drawing there is no need to use vl-load-all so the following line does it for you.
Code: (autolisp) [Select]
(if (findfile "blocklist.lsp") (load "blocklist.lsp"))
Loading Lisp-File using *.mnl-file
If your application loads a menu files into AutoCAD, you can also load your AutoLisp-file from a mnl-file. When AutoCAD loads your menu (mns, mnu, cui, cuix) then it searches also for a mnl-file with the same name: Having a menu file named tools.cuix, the tools.mnl will be loaded. The mnl-file has the same structure as the acad.lsp file. It will be loaded only once at the point when AutoCAD is loading your menu. That’s why you have to use vl-load-all, to load it in all drawings.

Loading Lisp-File using APPLOAD startup folder
An AutoLisp-file which was added to the APPLOAD startup folder will be loaded into all drawings when they are opened or created. You can add you lisp-file by hand or by creating the registry key
Code: [Select]
HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\<YourProductRelease>\<YourProductKey>\Profiles\<YourProfile>\Dialogs\Appload\Startup
"1Startup"="D:\\blocklist\\blocklist.LSP"

This is not the final state of this topic. Let's complete it. Send me notes for improving and extending.
Fred
« Last Edit: December 21, 2009, 11:10:31 AM by Fred Tomke »
Fred Tomke
Dipl.-Ing. (FH) Landespflege

[ landscaper - landscape developer - digital landscape and urban design]