Example 5: Dynamically Create Buttons

There are many programming situations where an action requires the creation and population of a collection, passing the collection to another component for processing and finally destroying the collection.

This example shows how to dynamically create a keyed collection of buttons, work with them, and how to then destroy all the buttons from memory:

A Dynamic Collection

This example first creates a dynamic collection of buttons:

 

DEFINE_COM class(#PRIM_KCOL<#prim_phbn #STD_NUM>) name(#BUTTON_COLLECTION) reference(*DYNAMIC)  STYLE(Collection)

 

By specifying reference(*DYNAMIC) you are telling LANSA not to create the collection when the form is loaded (as happens with an ordinary DEFINE_COM statement), but to create it only when a reference is explicitly made to it. The benefit of explicitly creating the collection is that later on you will be able to destroy it at any given time by setting the reference to it to *NULL.

Collection and Buttons Are Created at Run-Time

At run-time, when the user enters the number of buttons to be created and clicks the Create Buttons button and if the collection does not already exist, it is created using the SET_REF command:

 

IF_REF com(#BUTTON_COLLECTION) is(*null)

SET_REF com(#BUTTON_COLLECTION) to(*Create_as #PRIM_KCOL<#prim_phbn #STD_NUM>)

ENDIF

And then the number of buttons specified in the #STD_NUM field are created in the collection:

 

BEGIN_LOOP to(#STD_NUM)

CHANGE field(#BUTTINDEX) to('#ButtIndex + 1')

USE builtin(BCONCAT) with_args('Push Button' #BUTTCHAR) to_get(#STD_TEXTL)

SET_REF com(#Button_collection<#ButtIndex>) to(*CREATE_AS #PRIM_PHBN)

SET com(#BUTTON_COLLECTION<#ButtIndex>) PARENT(#Button_Panel) WIDTH(120) CAPTION(#Std_TextL)

END_LOOP

Lastly they are realized:

 

* Realize all the buttons just created. You can do this button by button

* but it is more efficient to do them all in one go by (re)realizing their parent ....

INVOKE method(#Button_Panel.Realize)

Destroy the Collection

All buttons in the collection are be destroyed when the user clicks on the Destroy all Buttons button which sets the reference to the entire collection to *NULL:

 

EVTROUTINE handling(#DESTROY_BUTTON.Click)

SET_REF com(#BUTTON_COLLECTION) to(*null)

ENDROUTINE

Source Code for Collection Example 5

FUNCTION options(*DIRECT)

BEGIN_COM ROLE(*EXTENDS #PRIM_FORM) CAPTION('Setting References') HEIGHT(324) LEFT(353) TOP(146) VISUALSTYLE(#VS_NORM) WIDTH(643)

* Form layout and button details

DEFINE_COM CLASS(#PRIM_PANL) NAME(#BUTTON_PANEL) DISPLAYPOSITION(1) HEIGHT(194) LAYOUTMANAGER(#BUTTON_PANEL_FLOW) LEFT(32) PARENT(#COM_OWNER) TABPOSITION(1) TABSTOP(False) TOP(72) WIDTH(569)

DEFINE_COM CLASS(#PRIM_FWLM) NAME(#BUTTON_PANEL_FLOW) FLOWOPERATION(Center) FLOWOPERATIONHOR(Spread)

DEFINE_COM CLASS(#STD_NUM.Visual) NAME(#STD_NUM) CAPTION('Number of buttons to Create') DISPLAYPOSITION(2) HEIGHT(19) LABELTYPE(Caption) LEFT(24) PARENT(#COM_OWNER) TABPOSITION(2) TOP(16) WIDTH(193)

DEFINE_COM CLASS(#PRIM_PHBN) NAME(#CREATE_BUTTON) CAPTION('Create Buttons') DISPLAYPOSITION(3) LEFT(232) PARENT(#COM_OWNER) TABPOSITION(3) TOP(16)

DEFINE_COM CLASS(#PRIM_PHBN) NAME(#DESTROY_BUTTON) CAPTION('Destroy all Buttons') DISPLAYPOSITION(4) LEFT(318) PARENT(#COM_OWNER) TABPOSITION(4) TOP(16) WIDTH(97)

DEFINE_COM CLASS(#PRIM_PHBN) NAME(#ENABLE_BUTTON) CAPTION('Enable All Buttons') DISPLAYPOSITION(5) LEFT(422) PARENT(#COM_OWNER) TABPOSITION(5) TOP(16) WIDTH(97)

DEFINE_COM CLASS(#PRIM_PHBN) NAME(#DISABLE_BUTTON) CAPTION('Disable All Buttons') DISPLAYPOSITION(6) LEFT(526) PARENT(#COM_OWNER) TABPOSITION(6) TOP(16) WIDTH(96)

* Keep an index to the number of buttons created, also in character format so that it can be used in the button caption later

DEFINE field(#BUTTINDEX) length(7) decimals(0) reffld(#DATE) edit_code(4) default(0)

DEFINE field(#BUTTCHAR) type(*CHAR) length(7) to_overlay(#BUTTINDEX)

* Keyed collections required to track buttons and flow manager items dynamically created

DEFINE_COM class(#PRIM_KCOL<#prim_phbn #STD_NUM>) name(#BUTTON_COLLECTION) reference(*DYNAMIC)  STYLE(Collection)

DEFINE_COM class(#PRIM_KCOL<#prim_fwli #STD_NUM>) name(#FLOWITEM_COLLECTION) reference(*DYNAMIC) STYLE(Collection)

* =================================================

* Handle Form Create instance

* =================================================

EVTROUTINE handling(#Com_Owner.CreateInstance)

SET com(#Std_Num) VALUE(3)

ENDROUTINE

* ===============================================

* Handle number of buttons to create being set to silly values

* ===============================================

EVTROUTINE handling(#Std_Num.Changed)

CASE of_field(#STD_NUM)

WHEN value_is('< 0')

SET com(#Std_Num) VALUE(1)

WHEN value_is('> 50')

SET com(#Std_Num) VALUE(50)

ENDCASE

ENDROUTINE

* =================================================

* Handle click of the button asking to dynamically create buttons

* =================================================

EVTROUTINE handling(#CREATE_BUTTON.Click)

* If the required keyed collections do not currently exist then create them now

IF_REF com(#BUTTON_COLLECTION) is(*null)

SET_REF com(#BUTTON_COLLECTION) to(*Create_as #PRIM_KCOL<#prim_phbn #STD_NUM>)

SET_REF com(#FLOWITEM_COLLECTION) to(*Create_as #Prim_KCol<#PRIM_FWLI #STD_NUM>)

ENDIF

* Now create the requested number of buttons and keep track

* of them in the keyed collections ....

BEGIN_LOOP to(#STD_NUM)

CHANGE field(#BUTTINDEX) to('#ButtIndex + 1')

USE builtin(BCONCAT) with_args('Push Button' #BUTTCHAR) to_get(#STD_TEXTL)

SET_REF com(#Button_collection<#ButtIndex>) to(*CREATE_AS #PRIM_PHBN)

SET com(#BUTTON_COLLECTION<#ButtIndex>) PARENT(#Button_Panel) WIDTH(120) CAPTION(#Std_TextL)

* Dynamically create a new flow layout item for the button and set up its details.

* Keep a reference to it in the flow item keyed collection.

SET_REF com(#FlowItem_Collection<#ButtIndex>) to(*CREATE_AS #PRIM_FWLI)

SET com(#FlowItem_Collection<#ButtIndex>) PARENT(#Button_Panel_Flow) MANAGE(#BUTTON_COLLECTION<#ButtIndex>)

END_LOOP

* Realize all the buttons just created. You can do this button by button

* but it is more efficient to do them all in one go by (re)realizing their parent ....

INVOKE method(#Button_Panel.Realize)

ENDROUTINE

* =================================

* Handle request to destroy all buttons

* =================================

EVTROUTINE handling(#DESTROY_BUTTON.Click)

* Destroy the button and flow item collections which will cause

* all the referenced items in the collection to be destroyed as well

SET_REF com(#FLOWITEM_COLLECTION) to(*null)

SET_REF com(#BUTTON_COLLECTION) to(*null)

* Reset the button index to zero .....

CHANGE field(#BUTTINDEX) to(0)

ENDROUTINE

* =================================

* Handle request to enable all buttons

* =================================

EVTROUTINE handling(#ENABLE_BUTTON.Click)

* If a button collection currently exists enable all the button in it ...

IF_REF com(#BUTTON_COLLECTION) is_not(*null)

SET com(#BUTTON_COLLECTION<>) ENABLED(True)

ENDIF

ENDROUTINE

* =================================

* Handle request to enable all buttons

* =================================

EVTROUTINE handling(#DISABLE_BUTTON.Click)

* If a button collection currently exists disable all the button in it ...

IF_REF com(#BUTTON_COLLECTION) is_not(*null)

SET com(#BUTTON_COLLECTION<>) ENABLED(False)

ENDIF

ENDROUTINE

* =====================================================================

* Handle click of one of the created buttons by listening to the entire collection of buttons

* =====================================================================

EVTROUTINE handling(#BUTTON_COLLECTION<>.Click) com_sender(#ClickedButton)

USE builtin(BCONCAT) with_args('You just clicked' #CLICKEDBUTTON.CAPTION) to_get(#STD_TEXTL)

USE builtin(MESSAGE_BOX_SHOW) with_args(OK OK INFO *COMPONENT #STD_TEXTL)

ENDROUTINE

END_COM