Sign in to follow this  
Followers 0
tonedeaf

Creating/Managing multiple forms in AutoIt - Part II

2 posts in this topic

#1 ·  Posted (edited)

This discussion follows Part I of following previous post:

Creating/Managing Multiple Forms in AutoIt - Part I

AutoIt by default does not create forms in a specific structure. All forms as well as the controls on the form are declared as global variables.

This situation becomes worse if we have multiple forms with many controls. We then have to come up with unique names for each and every control so that control names do not clash between different forms. Moreover, so many global variables become difficult to manage after the code becomes complex.

I'm trying to address some common issues by putting solutions which I think are flexible enough to handle such situations. Of course, these are always open to discussion/improvements.

In this part, the following issues are discussed:

> Trapping control events of form data types.

> Handling parent/child windows.

In my previous post I've discussed creating form data structures using arrays in AutoIt. We created a dialog box which is supposed to allow user to reset some password. In this part I'll build upon the application.

The Reset Password form is a subform for an application which allows you to reset Local Computer User account passwords (for Windows 2000/XP). Here is the screenshot of the main form (frmUserManager) and the subform (frmResetPassword).

Posted Image Posted Image

In Part I, we created a form data type for frmResetPassword. The code is reproduced here:

Func CreateForm_frmResetPassword(ByRef $afrmNew)
    Dim $afrmNew[5][2]

    $afrmNew[0][0] = "$frmResetPassword"
    $afrmNew[0][1] = GUICreate("Reset Password", 318, 111, 390, 279)
    
    GUICtrlCreateLabel("New passsword:", 11, 13, 131, 13)
    $afrmNew[1][0] = "$txtNewPwd"
    $afrmNew[1][1] = GUICtrlCreateInput("", 143, 11, 165, 23)
    GUICtrlCreateLabel("Confirm new passsword:", 11, 44, 131, 13)
    $afrmNew[2][0] = "$txtConfirmPwd"
    $afrmNew[2][1] = GUICtrlCreateInput("", 143, 41, 165, 23)

    $afrmNew[3][0] = "$cmdOK"
    $afrmNew[3][1] = GUICtrlCreateButton("OK", 152, 75, 75, 23)
    $afrmNew[4][0] = "$cmdCancel"
    $afrmNew[4][1] = GUICtrlCreateButton("Cancel", 233, 75, 75, 23)
EndFunc

Since frmResetPassword is a child window to frmUserManager window, we make some modifications in the above code:

Func CreateForm_frmResetPassword(ByRef $afrmNew, $frmParent)
    Dim $afrmNew[6][2]

    $afrmNew[0][0] = "$frmResetPassword"
    $afrmNew[0][1] = GUICreate("Reset Password", 318, 107, (@DesktopWidth-100)/2, (@DesktopHeight-115)/2, _
                    $WS_CAPTION, -1, $frmParent)
    
    $afrmNew[1][0] = "$frmParent"
    $afrmNew[1][1] = $frmParent
    
    GUICtrlCreateLabel("New passsword:", 11, 13, 131, 13)
    $afrmNew[2][0] = "$txtNewPwd"
    $afrmNew[2][1] = GUICtrlCreateInput("", 143, 11, 165, 23, $ES_PASSWORD)
    GUICtrlCreateLabel("Confirm new passsword:", 11, 44, 131, 13)
    $afrmNew[3][0] = "$txtConfirmPwd"
    $afrmNew[3][1] = GUICtrlCreateInput("", 143, 41, 165, 23, $ES_PASSWORD)

    $afrmNew[4][0] = "$cmdOK"
    $afrmNew[4][1] = GUICtrlCreateButton("OK", 152, 75, 75, 23)
    $afrmNew[5][0] = "$cmdCancel"
    $afrmNew[5][1] = GUICtrlCreateButton("Cancel", 233, 75, 75, 23, $BS_DEFPUSHBUTTON)
EndFunc

Modifications:

1. The CreateForm_frmResetPassword() function now takes a parameter to $frmParent. This parameter is used to create this form as a child window in the GuiCreate() function on the 3rd line.

2. There is an extra variable in the form array - $frmParent which stores the parent window handle.

3. The style $ES_PASSWORD is added to both text boxes to make them behave as password text boxes.

Similarily we create the Form Data Type for frmUserManager which is the main window of the application.

Func CreateForm_frmUserManager(ByRef $afrmNew)
    
    Dim $afrmNew[4][2]

    $afrmNew[0][0] = "$frmUserManager"
    $afrmNew[0][1] = GUICreate("User Accounts", 396, 274, (@DesktopWidth-396)/2, (@DesktopHeight-274)/2)
    GUICtrlCreateGroup("User Accounts:", 8, 8, 380, 73)
    $afrmNew[1][0] = "$icoUsers"
    $afrmNew[1][1] = GUICtrlCreateIcon("shell32.dll", 50, 22, 32)
    GUICtrlCreateLabel("Select a local user account and click Reset Password. " & _
                    "You must have the required priviledges to reset passwords.", 70, 33, 301, 31)
    GUICtrlCreateLabel("Users for this computer:", 8, 90, 115, 17)
    $afrmNew[2][0] = "$lvwUserAccounts"
    $afrmNew[2][1] = GUICtrlCreateListView("Name            |Full Name", 8, 110, 380, 121, _
                    BitOR($LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_NOSORTHEADER), BitOR($LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE))
    $afrmNew[3][0] = "$cmdResetPwd"
    $afrmNew[3][1] = GUICtrlCreateButton("Reset &Password...", 273, 240, 115, 25)

EndFunc

Since this is the main form, the function to create this form does not require a parameter of a parent window.

We also have these two functions from Part I:

Func _GetCtrl($sCtrlName, $afrmArray)   
    For $i = 0 to UBound($afrmArray, 1) - 1
        If $afrmArray[$i][0] = $sCtrlName Then
            Return $afrmArray[$i][1]
        EndIf
    Next
    
; Error Handler
    If $i > UBound($afrmArray, 1) - 1 Then
        UserManager_ErrorHandler("Control reference invalid ", _
                                "Control Name: " & $sCtrlName  & @CRLF & _
                                "Form Name: " & $afrmArray[0][0], "Fatal")
        SetError(1)
    EndIf
EndFunc

; Application wide central message handler.
; Displays an error msg and exits the application if the error is fatal.
Func UserManager_ErrorHandler($sError, $sSolution, $sImpact)
    If $sImpact = "Non-Fatal" Then
        MsgBox(8256, "User Manager", $sError & @CRLF & @CRLF & $sSolution)
    ElseIf $sImpact = "Fatal" Then  
        MsgBox(8208, "User Manager", $sError & @CRLF & @CRLF & $sSolution)
        Exit
    EndIf
EndFunc

The only thing which has changed is that that Application_ErrorHandler() function name is replaced with UserManager_ErrorHandler(), since this application is about managing user accounts.

Continuing on the same lines as Part I discussion, lets make the code functional:

#include <GUIConstants.au3>

Global $afrmUserManager
Global $afrmResetPassword

CreateForm_frmUserManager($afrmUserManager)
CreateForm_frmResetPassword($afrmResetPassword, $afrmUserManager[0][1])

GUISetState(@SW_SHOW, $afrmUserManager[0][1])

While 1
    $msg = GuiGetMsg(1)
    
    Select

    Case $msg[1] = $afrmUserManager[0][1]
        Select
        Case $msg[0] = $GUI_EVENT_CLOSE
            GUIDelete($afrmUserManager[0][1])
            GUIDelete($afrmResetPassword[0][1])
        Exit
        
        Case $msg[0] = _GetCtrl("$cmdResetPwd", $afrmUserManager)
; Show frmResetPassword
            GUISwitch($afrmUserManager[0][1])
            GUISetState(@SW_DISABLE)
            GUISwitch($afrmResetPassword[0][1])
            GUISetState(@SW_SHOW)
        EndSelect
        
    Case $msg[1] = $afrmResetPassword[0][1]
        Select  
        Case $msg[0] = _GetCtrl("$cmdCancel", $afrmResetPassword)
            GUISwitch(_GetCtrl("$frmParent", $afrmResetPassword))
            GUISetState(@SW_ENABLE)
            GUISwitch($afrmResetPassword[0][1])
            GUISetState(@SW_HIDE)
            
        EndSelect

    EndSelect
Wend

Exit


Func CreateForm_frmUserManager(ByRef $afrmNew)
    
    Dim $afrmNew[4][2]

    $afrmNew[0][0] = "$frmUserManager"
    $afrmNew[0][1] = GUICreate("User Accounts", 396, 274, (@DesktopWidth-396)/2, (@DesktopHeight-274)/2)
    GUICtrlCreateGroup("User Accounts:", 8, 8, 380, 73)
    $afrmNew[1][0] = "$icoUsers"
    $afrmNew[1][1] = GUICtrlCreateIcon("shell32.dll", 50, 22, 32)
    GUICtrlCreateLabel("Select a local user account and click Reset Password. " & _
                    "You must have the required priviledges to reset passwords.", 70, 33, 301, 31)
    GUICtrlCreateLabel("Users for this computer:", 8, 90, 115, 17)
    $afrmNew[2][0] = "$lvwUserAccounts"
    $afrmNew[2][1] = GUICtrlCreateListView("Name            |Full Name", 8, 110, 380, 121, _
                    BitOR($LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_NOSORTHEADER), BitOR($LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE))
    $afrmNew[3][0] = "$cmdResetPwd"
    $afrmNew[3][1] = GUICtrlCreateButton("Reset &Password...", 273, 240, 115, 25)

EndFunc

Func CreateForm_frmResetPassword(ByRef $afrmNew, $frmParent)
    Dim $afrmNew[6][2]

    $afrmNew[0][0] = "$frmResetPassword"
    $afrmNew[0][1] = GUICreate("Reset Password", 318, 107, (@DesktopWidth-100)/2, (@DesktopHeight-115)/2, _
                    $WS_CAPTION, -1, $frmParent)
    
    $afrmNew[1][0] = "$frmParent"
    $afrmNew[1][1] = $frmParent
    
    GUICtrlCreateLabel("New passsword:", 11, 13, 131, 13)
    $afrmNew[2][0] = "$txtNewPwd"
    $afrmNew[2][1] = GUICtrlCreateInput("", 143, 11, 165, 23, $ES_PASSWORD)
    GUICtrlCreateLabel("Confirm new passsword:", 11, 44, 131, 13)
    $afrmNew[3][0] = "$txtConfirmPwd"
    $afrmNew[3][1] = GUICtrlCreateInput("", 143, 41, 165, 23, $ES_PASSWORD)

    $afrmNew[4][0] = "$cmdOK"
    $afrmNew[4][1] = GUICtrlCreateButton("OK", 152, 75, 75, 23)
    $afrmNew[5][0] = "$cmdCancel"
    $afrmNew[5][1] = GUICtrlCreateButton("Cancel", 233, 75, 75, 23, $BS_DEFPUSHBUTTON)
EndFunc

Func _GetCtrl($sCtrlName, $afrmArray)   
    For $i = 0 to UBound($afrmArray, 1) - 1
        If $afrmArray[$i][0] = $sCtrlName Then
            Return $afrmArray[$i][1]
        EndIf
    Next
    
; Error Handler
    If $i > UBound($afrmArray, 1) - 1 Then
        UserManager_ErrorHandler("Control reference invalid ", _
                                "Control Name: " & $sCtrlName  & @CRLF & _
                                "Form Name: " & $afrmArray[0][0], "Fatal")
        SetError(1)
    EndIf
EndFunc

; Application wide central message handler.
; Displays an error msg and exits the application if the error is fatal.
Func UserManager_ErrorHandler($sError, $sSolution, $sImpact)
    If $sImpact = "Non-Fatal" Then
        MsgBox(8256, "User Manager", $sError & @CRLF & @CRLF & $sSolution)
    ElseIf $sImpact = "Fatal" Then  
        MsgBox(8208, "User Manager", $sError & @CRLF & @CRLF & $sSolution)
        Exit
    EndIf
EndFunc

Output:

frmUserManager:

Posted Image

frmResetPassword:

Posted Image

Clicking on the "Reset Password..." button shows frmResetPassword as a modal window and clicking the "Cancel" button hides the frmResetPassword and enables frmUserManager

The application does not look like the finished application yet. In Part III of this discussion the application will be completely functional.

Code review:

The important thing to keep in mind is that the form handles are always stored as the Array[0][1] element of the Form Datatype array.

So to refer to the form handle for frmUserManager, I've used $afrmUserManager[0][1] and similarily for frmResetPassword, I've referred to $afrmResetPassword[0][1]

In the beginning of the script, a new global array variable is created for the form datatype of frmUserManager and the CreateForm_frmUserManager() function is used to populate this array with variable names and their corresponding control handles which mimics a FORM Datatype.

Global $afrmUserManager
Global $afrmResetPassword

CreateForm_frmUserManager($afrmUserManager)
CreateForm_frmResetPassword($afrmResetPassword, $afrmUserManager[0][1])

The important part in this code is that after creating the frmUserManager Form Array, the function CreateForm_frmResetPassword() is called with $afrmUserManager array variable in the second argument. This makes frmUserManager the parent window to frmResetPassword.

The main window frmUserManager is made visible by the following line:

GUISetState(@SW_SHOW, $afrmUserManager[0][1])

The rest of the code is the WHILE loop:

While 1
    $msg = GuiGetMsg(1)
    
    Select

    Case $msg[1] = $afrmUserManager[0][1]
        Select
        Case $msg[0] = $GUI_EVENT_CLOSE
            GUIDelete($afrmUserManager[0][1])
            GUIDelete($afrmResetPassword[0][1])
        Exit
        
        Case $msg[0] = _GetCtrl("$cmdResetPwd", $afrmUserManager)
; Show frmResetPassword
            GUISwitch($afrmUserManager[0][1])
            GUISetState(@SW_DISABLE)
            GUISwitch($afrmResetPassword[0][1])
            GUISetState(@SW_SHOW)
        EndSelect
        
    Case $msg[1] = $afrmResetPassword[0][1]
        Select  
        Case $msg[0] = _GetCtrl("$cmdCancel", $afrmResetPassword)
            GUISwitch(_GetCtrl("$frmParent", $afrmResetPassword))
            GUISetState(@SW_ENABLE)
            GUISwitch($afrmResetPassword[0][1])
            GUISetState(@SW_HIDE)
            
        EndSelect

    EndSelect
Wend

The WHILE loop starts with calling GuiGetMsg() in the advanced mode

$msg = GuiGetMsg(1)

which returns an array with the form handle as the Array[1] element.

This Array[1] element is compared against the form handles for frmUserManager and frmResetPassword.

Based on which form the event is coming from, the code branches to that SELECT CASE section which determines next the control on the form which has caused the event.

These events are:

Form: frmUserManager

Event: $GUI_EVENT_CLOSE

frmUserManager is the main form. A close event will exit the application. All the forms are deleted and the application exits.

Case $msg[0] = $GUI_EVENT_CLOSE
    GUIDelete($afrmUserManager[0][1])
    GUIDelete($afrmResetPassword[0][1])
    Exit

Form: frmUserManager

Event: $cmdResetPwd

User clicked the "Reset Password..." button which will show frmResetPwd window

Case $msg[0] = _GetCtrl("$cmdResetPwd", $afrmUserManager)
; Show frmResetPassword
    GUISwitch($afrmUserManager[0][1])
    GUISetState(@SW_DISABLE)
    GUISwitch($afrmResetPassword[0][1])
    GUISetState(@SW_SHOW)
To show frmResetPwd as a modal dialog window, its necessary to disable the parent window (frmUserManager first.

Form: frmResetPassword

Event: $cmdCancel

User clicked the "Cancel" button which hides the frmResetPassword window

Case $msg[0] = _GetCtrl("$cmdCancel", $afrmResetPassword)
    GUISwitch(_GetCtrl("$frmParent", $afrmResetPassword))
    GUISetState(@SW_ENABLE)
    GUISwitch($afrmResetPassword[0][1])
    GUISetState(@SW_HIDE)
Before hiding the frmResetPassword window, it's necessary to enable the parent window first. Otherwise the active window focus will move to some other window.

Although the code is more logically arranged, its already becoming large.

In Part III I'll suggest ways to split a large AutoIt project with multiple forms into separate .au3 files.

Any comments/suggestions/improvements are always welcome.

Edited by tonedeaf

Share this post


Link to post
Share on other sites



Whaou ! Interesting !

Thank you !

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0