B4J Question [ABM] - Click event firing early

Rob White

Member
Licensed User
Hi all,

I have a page (home page) on which there will be 8 to 10 buttons to take you to various pages.

Some thing like this (but only 2 buttons)
B4X:
Sub ButClicked(Dest As String)
    ABMShared.NavigateToPage(ws,Dest,$"../${Dest}/"$)
End Sub

public Sub ConnectPage()           
    '    connecting the navigation bar
    ' ABMShared.ConnectNavigationBar(page)
    
    Dim But1 As ABMButton
    But1.InitializeRaised(page,"But1","","","To page 1","mytheme")
    But1.EventHandler = ButClicked("Page1")
    page.Cell(1,1).AddComponent(But1)
    
    Dim But2 As ABMButton
    But2.InitializeRaised(page,"But2","","","To page 2","mytheme")
    But2.EventHandler = ButClicked("Page2")
    page.Cell(1,1).AddComponent(But2)
    
    ' More buts here with similar definitions
    
    ' refresh the page
    page.Refresh
    
    ' Tell the browser we finished loading
    page.FinishedLoading
    ' restoring the navigation bar position
    'page.RestoreNavigationBarPosition   
End Sub

Problem is the ButClick sub is executed for each button when ConnectPage is called.
Is there a better way to set this up?
I can post a full working example if needed.
 

MichalK73

Well-Known Member
Licensed User
Longtime User
What do you want this for?
B4X:
But1.EventHandler = ButClicked("Page1")
B4X:
But2.EventHandler = ButClicked("Page2")
Where do you have the press call functions?

You set the EventHandle to procure call? Every time a page is loaded (e.g. Refesh) some button event probably occurs. Find out what's going on in
B4X:
Sub Page_ParseEvent(Params as Map)
 
Upvote 0

Rob White

Member
Licensed User
Thanks MichalK73,

I was hoping to be able to write a single handler for all the buttons. I now see that I can do this within
Sub Page_ParseEvent(Params As Map)
.
If I may ask what is the function of the method xxxx.EventHandler = and where do I find this sort of documentation?
 
Upvote 0

alwaysbusy

Expert
Licensed User
Longtime User
This is indeed a construction I didn't even know would compile:

B4X:
But1.EventHandler = ButClicked("Page1")

EventHandler should be a Class instance. By default, all events are raised in the Page. But sometimes, you may want to make a reusable Class InputExecutorsB where you for example create a container, and you want all events to be handled by that class instead of on the main page. Then you can use EventHandler.

Main Page:
B4X:
' Global
Dim page As ABMPage
Dim Input1 As InputExecutorsB
...
'Connect
Page.Cell(1,1).AddComponent(myInput.Initialize("executors", Me, page))
...

' the Main's class ParseEvent, that can forward the events to our InputExecutorsB Class to be handled
Sub Page_ParseEvent(Params As Map)
    Dim eventName As String = Params.Get("eventname")
    Dim eventParams() As String = Regex.Split(",",Params.Get("eventparams"))
    If eventName = "beforeunload" Then
        Log("preparing for url refresh")
        ABM.RemoveMeFromCache(ABMShared.CachedPages, ABMPageId)
        Return
    End If
    Dim caller As Object = page.GetEventHandler(Me, eventName)
    If caller = Me Then
        If SubExists(Me, eventName) Then
            Params.Remove("eventname")
            Params.Remove("eventparams")
            ' BEGIN NEW DRAGDROP
            If eventName = "page_dropped" Then
                page.ProcessDroppedEvent(Params)
            End If
            ' END NEW DRAGDROP
            Select Case Params.Size
                Case 0
                    CallSub(Me, eventName)
                Case 1
                    CallSub2(Me, eventName, Params.Get(eventParams(0)))
                Case 2
                    If Params.get(eventParams(0)) = "abmistable" Then
                        Dim PassedTables As List = ABM.ProcessTablesFromTargetName(Params.get(eventParams(1)))
                        CallSub2(Me, eventName, PassedTables)
                    Else
                        CallSub3(Me, eventName, Params.Get(eventParams(0)), Params.Get(eventParams(1)))
                    End If
                Case Else
                    ' cannot be called directly, to many param
                    CallSub2(Me, eventName, Params)
            End Select
        End If
    Else
        CallSubDelayed2(caller, "ParseEvent", Params) 'ignore
    End If
End Sub

' some method we want to be able to call from the InputExecutorsB class
Sub CONTExecutors_DoSomething(txt as String)
     log(txt)
End Sub

InputExecutorsB:
B4X:
' globals
Private Page As ABMPage ' owner ABMPage of this class
Private Caller As Object ' Class that holds the ABMPage
Private Name as String
Private Cont as ABMContainer

...

Public Sub Initialize(ObjectName As String, TargetCaller As Object, TargetPage As ABMPage) As ABMContainer
    Page = TargetPage
    Caller = TargetCaller
    Name = ObjectName
   
    cont.Initialize(Page, "Cont" & Name, "")
    cont.AddRowsM(3, True,0,0,"").AddCellsOS(1,2,2,2,10,5,5,"").AddCellsOS(1,0,0,0,12,5,5, "ccenter")
    cont.AddRowsM(1, True,0,0,"").AddCellsOS(1,2,2,2,10,10,10,"")
    cont.AddRows(1,True,"").AddCells12(1,"")
    cont.BuildGrid
  
    Dim inpBarcode As ABMInput
    inpBarcode.Initialize(Page, Name & "inpBarcode", ABM.INPUT_TEXT, "barcode", False, "onetwo")
    cont.Cell(RowIndex,1).AddComponent(inpBarcode)

     ...
    
    cont.EventHandler = Me '<----------------- We want all events that happen to this container (and it's children) to be handled by this class, not the main page

    Return cont
End Sub

' this class has it's own ParseEvent method, and is called by the Main's ParseEvent (the else CallSubDelayed2 if Caller <> Me)
public Sub ParseEvent(params As Map)
    Dim eventName As String = params.Get("eventname")
    Dim eventParams() As String = Regex.Split(",",params.Get("eventparams"))
    If eventName.ToLowerCase.StartsWith(Name.ToLowerCase) Then
        eventName = eventName.SubString(Name.Length)
    End If

    If SubExists(Me, eventName) Then
        params.Remove("eventname")
        params.Remove("eventparams")
        Select Case params.Size
            Case 0
                CallSub(Me, eventName)
            Case 1
                CallSub2(Me, eventName, params.Get(eventParams(0)))
            Case 2
                If params.get(eventParams(0)) = "abmistable" Then
                    Dim PassedTables As List = ABM.ProcessTablesFromTargetName(params.get(eventParams(1)))
                    CallSub2(Me, eventName, PassedTables)
                Else
                    CallSub3(Me, eventName, params.Get(eventParams(0)), params.Get(eventParams(1)))
                End If
            Case Else
                ' cannot be called directly, to many param
                CallSub2(Me, eventName, params)
        End Select
    End If
End Sub

' handling the event here, in the Class itself, not in the main
Sub inpBarCode_LostFocus()
    Dim inpBarcode As ABMInput = cont.Component(Name & "inpBarcode")
    ' do something on the Main Page with it
    CallSub2(Caller, "CONT" & Name & "_DoSomething", inpBarcode.Text)
End Sub

As for your buttons, use AddArrayComponent instead of AddComponent. They will all raise the same Event and you can use Target to find out which one it is (may need some string parsing to find out exactly).

B4X:
Dim btn1 as ABMButton
btn1.InitializeFloating(page, "1", "fa fa-chain-broken", "onetwo") ' just use a number here for the ID, NOT a name. We will give it a name in AddArrayComponent!
Page.Cell(1,2).AddArrayComponent(btn1, "MyButtons") '<--- here we give it a name

Dim btn2 as ABMButton
btn2.InitializeFloating(page, "2", "fa fa-chain-broken", "onetwo") ' again just use a number here for the ID
Page.Cell(1,3).AddArrayComponent(btn2, "MyButtons") '<--- SAME name

...

' one click event to handle all the buttons with MyButtons as name
Sub MyButtons_Click(Target as String)
    Log(Target)
    Dim ID As String = Target.Replace("mybuttons", "")
    Select case ID
          case "1"
          
          case "2"
     
    End Select
End Sub


Alwaysbusy
 
Upvote 0

Rob White

Member
Licensed User
Thanks for that clarification.
Which is best practice? Use "AddArrayComponent" OR modify "Page_ParseEvent(Params As Map)" with something like

B4X:
    Dim Parts() As String = Regex.Split("_",Params.Get("eventname"))
    Act.Index=-1
    If Parts(1) = "clicked" Then
        Select Parts(0).Trim
            Case "sowbut"
                Act.Index=1
                
        End Select
        If Act.Index > -1 Then
            ABMShared.NavigateToPage(ws,"SelectPaddockPage","../SelectPaddockPage/")
        Else
            Log($"Click ignored! Button = ${Parts(0)} "$)
        End If

    End If
 
Upvote 0
Top