Using the tabbed UI
Here we are going to take a closer look at the implementation of a Browse/Form "paradigm", included in the alpha360 projects:
- Browse - an internal window that contains our Products - IWB_Products
- Form - an internal window that contains ONE Entity Object from our Products - IWF_Product
Browse - IWB_Products [internal window or IW]
This IW contains a table control, that is populated by a query, Query_Products.
At the Global Area of the IW, we have the following:
PROCEDURE IWB_Products(LOCAL p_OpenWindow is ST_a360_DynamicWindow)
and at the End of Initialization ... :
UI_Format()
This is a local procedure and "formats" the various data, depending on our configuration - take a look at it for more information.
Call the Browse
We can call this IW from ANYWHERE in our application and it will be embedded (by default) in our standard Dynamic TAB - "TAB_Dynamic" in the "MNU_ERP" window.
To "call" it, we just need to use the following syntax:
a360_OpenWindow_Menu("TAB_Dynamic", "IWB_Products", 100, "","",False)
The a360_OpenWindow_Menu()is a simple function, designed to quickly call Browse type IW.
Behind the scenes, it calls a360_OpenWindow - see the code for more information.
Note, that when "calling" Browse IWs, we don't generally need to pass any data - like PKs, FKs etc.
The only time data may be passed to a Browse is when a Form is closed.
The close is then done automatically by the UI - it uses the local procedure, UI_RefreshRequest to implement it.
Call the Form
There are basically 4 reasons to "call" a Form from a Browse:
- To add an entity record -> we call UI_Add
- To edit an entity record -> we call UI_Edit
- To delete an entity record -> we call UI_Delete
- To view an entity record -> we call UI_View
The UI_Add() local procedure
What we conceptually do is:
- create a ST_a360_DynamicWindow variable
- assign the .nAction member the value of 1 [meaning add/insert]
- assign the .sDynamicInternalWindow member the value of the IW Form
- assign the .sReturnTOWindow member the value of the IW to return to - after the user closes the Form
- call a360_OpenWindow(e_OpenWindow)to embed the window in the default TAB
Here is the code:
e_OpenWindow is ST_a360_DynamicWindow
// Initialize the structure to pass to a360_OpenWindow .........
e_OpenWindow.nAction=1 // Insert
e_OpenWindow.sDynamicInternalWindow="IWF_Product"
e_OpenWindow.sReturnTOWindow="IWB_Products" // The name of the IW to Focus after closing
// Now Open the Window with the UI ......
a360_OpenWindow(e_OpenWindow)
The UI_Edit() local procedure
What we conceptually do is:
- create a ST_a360_DynamicWindow variable
- check to make sure the user has selected a "record" from the table
- assign the .nAction member the value of 2 [meaning edit]
- assign the .sPK member the value of the selected PK
- assign the .sDynamicInternalWindow member the value of the iw Form
- assign the .sDynamicInternalWindowID member the value of the iw Form plus the selectedPK, because we wan't to allow simultaneous editing of different records
- assign the .sReturnTOWindow member the value of the iw to return to, after the user closes the Form
- call a360_OpenWindow(e_OpenWindow)to embed the window in the default TAB
Here is the code:
selectedPK is string
e_OpenWindow is ST_a360_DynamicWindow
// Check to see if a record is selected in the Browse ...
IF TableSelect(Table_Query_Products) =-1 THEN RETURN
selectedPK=Table_Query_Products.sProductPK// Remember this is the PK
// Initialize the structure to pass to a360_OpenWindow ...
e_OpenWindow.nAction=2 // Edit
e_OpenWindow.sPK=selectedPK // Primary Key [when Changing, Deleting or Viewing]
e_OpenWindow.sDynamicInternalWindow="IWF_Product"
e_OpenWindow.sDynamicInternalWindowID=e_OpenWindow.sDynamicInternalWindow+selectedPK
e_OpenWindow.sReturnTOWindow="IWB_Products" // The name of the IW to Focus after closing
// Now Open the Window with the UI ......
a360_OpenWindow(e_OpenWindow)
The UI_Delete() local procedure
We do the same coding as when editing, but we pass to the .nAction member the value of 3:
e_OpenWindow.nAction=3 // Delete
The UI_View() local procedure
We do the same coding as when editing, but we pass to the .nAction member the value of 4:
e_OpenWindow.nAction=4 // View
Form - IWF_Product [internal window or IW]
This IW contains - screen fields, linked to local record variables.
In more complicated entities - like Invoices - forms can also contain table controls.
Note: the forms that handle tables with ONLY one record - like the gConfiguration table - are linked directly to table buffers and not to local record variables.
At the Global Area of the IW, we have the following:
PROCEDURE IWF_Product(LOCAL p_OpenWindow is ST_a360_DynamicWindow)
// Return this for Searches, filters etc
// to the p_OpenWindow.sReturnTOWindow
sReturnThis is string = ""
// Critical Errors ...
w_bCriticalError is boolean=False
w_bCriticalErrorMessage is string = ""
// Main Record Variables ...
r_gProduct is Record of gProduct
r_gProduct_Class is Record of gProduct_Class
// A Copy of the Record Variables ...
rc_gProduct is Record of gProduct
rc_gProduct_Class is Record of gProduct_Class
- We have a variable sReturnThis, to return values
- Variables to catch and show messages for Critical Errors (more on this below)
- and double Record structures for all files in this entity
and at the End of Initialization ... :
UI_Format()
This is a local procedure that "formats" the various data, depending on our configuration - take a look at the code for more information.
UI_Init()
This is a local procedure that Initializes the screen variables.
UI_Init
This procedure initializes the record variables and various screen elements, depending on the reason the Form was called.
- If it was called for an add/insert operation, with p_OpenWindow.nAction=1, it resets the record variables and it assigns to them, "default values".
- If it was called for an edit or delete or View operation, with p_OpenWindow.nAction=2[or 3 or 4], it retrieves the various records of the entity, and it copies them to the record variables.
- at the end it uses SourceToScreen() to copy everything to the screen elements.
*Remember: all screen elements are "linked" to the main variable record structures, defined at the global area of the Form and NOT to file variables.
We can now go on with our "data entry" and - at the end - select one of the following actions :
- Click the <Cancel Button> to run the local procedure, UI_Cancel().
This procedure calls the UI_Close() local procedure and it "removes" the Form from the Dynamic TAB.
- or Click the <Save Button> to run the local procedure, UI_Save().
This procedure calls the UI_Save() local procedure that tries to "save" our data changes.
The UI_Save() procedure checks for required fields and double entries, before calling the BPs.
Follow the code and it's documentation for more information.
*BPs: Business Processes are alpha360s procedures that handle adds, modifies, deletes, posts etc for the various data entities and are shared between WINDEV, WEBDEV and WINDEV Mobile apps.
The various record values are passed to BPs by variable record structures and arrays and
they are enclosed in a Transaction Frame and return a True or False and
they "return" error, through a NON Local string variable - usually named p_sErrorMessage.
You can find more information about the BPs in the Business Processes area.
Return value(s) after closing the Form
After a successful Add/Insert or Edit, the sReturnThis variable has the "PK of the entity" or here, the PK of the gProduct record.
The global procedure a360_CloseWindow(), called from inside UI_Close(), does the following:
- removes the Form from the Dynamic TAB
- gives FOCUS to the IW, p_OpenWindow.sReturnTOWindow
- runs the local procedure, p_OpenWindow.sReturnTOWindow.UI_RefreshRequest, passing to it the sReturnThis variable
If any of the above "fail", either because a user has "closed a window" or a developer has NOT added the functionality, it "fails silently", without any errors.
ScreenToSource()
You must add ScreenToSource(), to every screen data elements, after you do any edits [always inside the elements code].
This will "preserve" the values of the screen elements, when users navigate between the different IWs in the Dynamic TAB.
The Form's Flow
When embedding a IW in a Dynamic TAB, you are never 100% sure when everything has been completed.
This makes it very difficult to catch errors, in say UI_Init(), and thus close the IW from inside UI_Init().
To work around this issue, we have an "error mechanism" inside these "dangerous areas".
All errors found, say in UI_Init(,) are reported through a call to RequestUpdateUI().
RequestUpdateUI() then calls a360RefreshDisplayOfWindow(), that takes care of the errors.
Read PCSofts RequestUpdateUI() documentation for more information.