Please reload

 

alpha360 User Interface [v2]

All alpha360 projects share the same UI Framework - a simple, user friendly, stable and beautiful UI.

It is a multi tabbed, flat UI, based on the "classic" browse/form paradigm and in v2 it comes with many new features - among them is the inclusion of automatic access control at the "heart of the UI".

 

See the introduction video for more information.

 

Foundations

If you are a WINDEV programmer, you probably already know and understand the "classic" way of working with windows and forms in WD (or WEBDEV or WD Mobile).

 

Create a window, copy fields from your analysis, write some code to retrieve your data and add code for your CRUDE operations inside the window - of course there are many variations to the above, but this is the main idea. If your projects are rather small, this will work just fine.

 

But if your projects are growing larger and larger every day and you need to target many different platforms, this is not the way to do things.

And if you want to use Dynamic TABs in your apps [available from WD v20], you have to do a lot of work - we know it, we have been through it.

We introduced v1 of the UI - now called alpha360 UI Framework - with v1 of the alpha360 ERPs.

Because we started developing it with WD v20 - where Dynamic TABs where rather "buggy and incomplete" - it took us a lot of time to "work around bugs - or what we thought was bugs" and didn't have the time to complete all the desired features.

The UI framework that is included in alpha360 Retail [v2] is now in its second release - it is stable, it has many-many new features and the code is simple, easy to understand and above all VERY EASY to use. If you set aside, a few lines of difficult code, that you can ignore and treat as a "black box", everything else is as simple as the "classic" Open() code in WD.

And all the above with 90% LESS CODE than v1 ...

 

Introduction to the UI

From a "functional" view, there are 2 basic types of "windows" in the UI - the Browse and the Form.

 

A classic paradigm for a browse is a window that shows Product records.

From this window we can query or search the products database, add a new record or select and edit a product record. The editing is done inside the browse [with the help of a table control] _OR_ it can be done in separate windows of the Form type.

This "paradigm" is nothing new - it has been around for a long time - and many classic RAD tools, like Clarion, have used it with great success.

Now all alpha360 UI windows, Browses or Forms, are always:

  • WD Internal Windows - autonomus, with their own buffers [independent HFSQL context]

  • embedded inside dynamic tabs

  • controlled by just 2 UI procedures

  • they NEVER have ANY kind of CRUDE operations inside them

  • and ALL of them are procedures with just one parameter of type ST_a360_DynamicWindow

All Browse windows have the same "visual design", the same set of local procedures and the same "behaviour and workflow", without the use of templates. This is true for the Form windows also.

The only windows - inside the UI - that are NOT internal windows are a few lookup windows.

 

Calling a "window" - Open() vs a360_OpenWindow()

The "classic" way to call or open a simple window with WINDEV is to use Open(WindowName, Parameter1, Parameter2 , ...) or use OpenChild etc ...

But to use the new Dynamic TAB control, you must use internal windows and the TabOpen function [or one of its variations].

 

The a360_OpenWindow() procedure - part of our new UI framework - plays the role of the classic Open() function. It embeds an internal window inside a Dynamic TAB, implements access control _AND_ takes care of the "little but important details" in this area.

How it does it, is not realy important right now - you can think of it as a "black box" and use it immediatelly. When you have the time, you can study the code and see how it is implemented ...

 

All a360_OpenWindow() calls have ONE and ONLY one parameter - a ST_a360_DynamicWindow variable and nothing else - all the information needed is "inside" this structured variable.

Lets give a small paradigm, to clear things ...

  1. We create an internal window from the WD IDE and we name it say, IWB_Paradigm.

  2. In the Global Declaration of IWB_Paradigm, we give it a proper name and one and only parameter, like this: PROCEDURE IWB_Paradigm(LOCAL p_OpenWindow is ST_a360_DynamicWindow)

  3. Set Independent HFSQL Context to true

  4. The functionality of the above "window" is NOT important right now - its just a "window" we want to embed inside a Dynamic TAB.

Now here is the code we use to "call/embed" the above internal window:

e_OpenWindow is ST_a360_DynamicWindow

// Initialize the structure to pass to a360_OpenWindow

e_OpenWindow.sDynamicInternalWindow="IWB_Paradigm"        
// Now Open the Window with the UI ......

a360_OpenWindow(e_OpenWindow)

The ST_a360_DynamicWindow structure has many preset members (or variables) that you can use to pass data or funcionality to your internal windows. Primary and Foreign Keys, Window titles, the ability to embed multiple copies of the internal windows and many more features are included.

You can find the complete list of preset members at the Project code area _AND_ you can add your own members to the structure, without affecting the way your apps work.

 

Closing a "window" - Close() vs a360_CloseWindow()

The "classic" way to close a simple window with WINDEV is to use Close(WindowName, Returned value1) etc ...

But with internal windows, embedded inside Dynamic TABs, this does not work anymore.

You can find more information in PCSoft's help, about the right way to do it, but it will take you some time to understand it and clear up the "little but important" details.

The a360_CloseWindow() procedure, takes care of all these "closing issues" in a simple and elegant way. Just use the following code, inside your internal windows:

// Remove the Internal Window from the DynamicTAB
a360_CloseWindow(p_OpenWindow.sDynamicInternalWindowKey,...

                 p_OpenWindow.sDynamicInternalWindowID, ...

                 p_OpenWindow.sReturnTOWindow,...

                               sReturnThis)

In the alpha360 projects, this code is always in a procedure, local to the internal window, and named UI_Close.

Here is a short explanation of the variables used in the above procedure:

  • .sDynamicInternalWindowKey and .sDynamicInternalWindowID come from the structure we passed when "opening the window" and are automatically constructed and handled by the UI.

  • .sReturnTOWindow - if used, tells the UI to which "internal window" it must "try" to give focus after "closing" the "window". We say "try", because this "internal window" may have been closed by the user.

  • .sReturnThis - this value is "returned" to the above sReturnTOWindow and the variable must be declared in the Global Area of each internal window. 

 

Note: When returning or giving focus to another "embedded internal window" the UI tries to run a local procedure named UI_RefreshRequest and pass to it the .sReturnThis variable.

Using the UI

We are going to take a closer look at the implementation of a Browse/Form "paradigm", included in both the Retail and the ERPs 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_Retail" 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 iw's, 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. It is then done automatically by the UI - it uses the local procedure, UI_RefreshRequest, to implement it.

Call the Form

There are basically 3 reasons to "call" a Form from a Browse:

  • To add an Entities Record(s) -> we call UI_Add

  • To edit an Entities Record(s) -> we call UI_Edit

  • To delete an Entities Record(s) -> we call UI_Delete

We use the word records instead of record, because an Entity may be constructed by one or more different type of file records - here the Product entity is constructed by gProduct_Class and gProduct records.

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

 

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 operation, with p_OpenWindow.nAction=2[or 3], 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 and WEBDEV configurations. The various record values are passed to BPs by variable record structures and arrays. They are enclosed in a Transaction Frame, they return a True or False and they "return" errors by a passed 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 succesfull 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 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.

 

computerplus

Leoforos Dodonis 43,  45221

IOANNINA - GREECE

Registered VAT ID: EL084190121

sales@computerplus.gr