B4J Tutorial [ABMaterial] Better Delegating the creation of complex components

Discussion in 'B4J Tutorials' started by Cableguy, Sep 14, 2017.

?

Should the "Composite" or "Complex" component Class be implemented in v3.75?

  1. YES!

    11 vote(s)
    91.7%
  2. NO!

    0 vote(s)
    0.0%
  3. What For?

    1 vote(s)
    8.3%
  1. Cableguy

    Cableguy Expert Licensed User

    [EDIT: This thread started as my approach to delegating complex components creation, including events.
    @alwaysbusy then proposed a better system, that he needs us to say if we want it implemented on the upcoming release of v 3.75, SO let's create a Poll!
    Hi Gurus...

    This time I come Not with questions, but with MY answer to one of my biggest problems with this amazing framework : ABMaterial!

    And to me, the main problem is, a simple webapp can get quite complex very quickly UI wise, so maintainability can be a very hard business...

    So I thought... what if I could divide my code into "complex components" and treat them as a unit, instead of going through all of a pages lines to find a prop to change, and risk breaking the code beyond repair!
    So I obviously thought of a ClassModule and treat it like a custom view... and it almost worked...
    I only had 2 issues with it... both related to theming...
    Going the Class route I just couldn't figure out how to theme the "complex component", since a class needs to be first initialized, and the theme must be created BEFORE!
    Then, as I typed this question I started thinking about a CodeModule...
    Main advantages : doesn't need declaring nor initializing!

    So, my basic steps were:
    Create a Code Module, in my case, I called it "TopBar2"

    then declared all that was needed to my Complex Component
    Code:
    Sub Process_Globals
        
    Private ABM As ABMaterial 'ignore
        Private MyTheme As ABMTheme

        
    Private TargetPage As ABMPage
        
    Private ObjectId As String
        
    Private LogoFilename As String
        
    Private TopBarThemeName As String

        
    Private TopBarContainer As ABMContainer
        
    Private TopBarSubContainer As ABMContainer
        
    Private TopBarLogo As ABMImage
        
    Private TopBarSearch As ABMInput
        
    Private TopBarBtn1 As ABMButton
        
    Private TopBarBtn2 As ABMButton
        
    Private TopBarBtn3 As ABMButton

        
    Private LogoTheme As String
        
    Private SearchTheme As String
        
    Private BtnTheme As String
        
    Private Btn3Theme As String
    End Sub
    Then the "real construction work" takes place at the "CreateTopBar" sub:
    Code:
    Public Sub CreateTopBar(page As ABMPage, id As String, filename As String, Theme As StringAs ABMContainer
        TargetPage = 
    page
        ObjectId = 
    id
        LogoFilename = filename
        TopBarThemeName = Theme
        LogoTheme = TopBarThemeName & 
    "Logo"
        SearchTheme = TopBarThemeName & 
    "Search"
        BtnTheme = TopBarThemeName & 
    "Btn"
        Btn3Theme = TopBarThemeName & 
    "Btn3"

        TopBarContainer.Initialize ( TargetPage, ObjectId, 
    "" )
        TopBarSubContainer.Initialize ( TargetPage, ObjectId & 
    "SubCntr""")
        TopBarLogo.Initialize ( TargetPage, ObjectId & 
    "Logo", LogoFilename, 1 )
        TopBarSearch.Initialize ( TargetPage, ObjectId & 
    "Search", ABM.INPUT_SEARCH, "Pesquizar"False, SearchTheme )
        TopBarBtn1.InitializeRaised ( TargetPage, ObjectId & 
    "Btn1""""""Registar", BtnTheme )
        TopBarBtn2.InitializeRaised( TargetPage, ObjectId & 
    "Btn2""""""Login", BtnTheme)
        TopBarBtn3.InitializeFlat ( TargetPage, ObjectId & 
    "Btn3""settings", ABM.ICONALIGN_CENTER, "", Btn3Theme)

        LogError (
    "LogoTheme : " & LogoTheme)
        LogError (
    "SearchTheme : " & SearchTheme)
        LogError (
    "BtnTheme : " & BtnTheme)
        LogError (
    "Btn3Theme : " & Btn3Theme)

        
    'then we build the grid

        TopBarContainer.AddRowsMV(
    1False,7,7, ABM.VISIBILITY_SHOW_ON_LARGE_ONLY,"").AddCellsOS(1,0,0,0,2,2,2,"").AddCellsOS(1,0,0,0,7,7,7,"").AddCellsOS(1,0,0,0,3,3,3,"")
        TopBarContainer.BuildGrid 
    ' IMPORTANT!

        TopBarSubContainer.AddRowsMV(
    1False,0,0, ABM.VISIBILITY_SHOW_ON_LARGE_ONLY,"").AddCellsOSMP(1,0,0,0,5,5,5,0,0,0,5,"").AddCellsOSMP(1,0,0,0,5,5,5,0,0,5,0,"").AddCellsOS(1,0,0,0,1,1,1,"")
        TopBarSubContainer.BuildGrid 
    ' IMPORTANT!

        
    'and we set the cell height
        TopBarContainer.Row(1).SetFixedHeight(70False)
    '    TopBarSubContainer.Row(1).SetFixedHeight(60, False)

        
    'we then add the picture Logo and set its theme
        TopBarContainer.Cell(1,1).AddComponent(TopBarLogo)
        TopBarContainer.Cell(
    1,1).UseTheme(LogoTheme)

        
    'Now we add the search field and set its theme
        TopBarSearch.Narrow = True
        TopBarContainer.Cell(
    1,2).AddComponent(TopBarSearch)
        TopBarContainer.Cell(
    1,2).UseTheme(SearchTheme)

        
    'now we add the SubContainer for Buttons 1 & 2
        TopBarContainer.Cell(1,3).AddComponent(TopBarSubContainer)

        
    'finaly we start adding the buttons
        TopBarBtn1.UseFullCellWidth =True
        TopBarSubContainer.Cell(
    1,1).AddComponent(TopBarBtn1)

        TopBarBtn2.UseFullCellWidth =
    True
        TopBarSubContainer.Cell(
    1,2).AddComponent(TopBarBtn2)
        TopBarContainer.Cell(
    1,3).UseTheme(BtnTheme)

        TopBarSubContainer.Cell(
    1,3).AddComponent(TopBarBtn3)

        
    Return TopBarContainer
    End Sub
    (yeah, I know, not the best coding technique, but I'm getting there!)
    The more aware of you will notice that I have 2 containers...
    The main Container has 3 cells, and inside the 3rd cell we have a SubContainer with another 3 cells...
    I did it this way to arrange the positioning of the components more efficiently (in other words, to my liking)
    Then we do what we always do, assemble the LEGO! and we return the main Container to the calling sub!

    the call resembles something like this:
    Code:
    Page.Cell(1,1).AddComponent(TopBar2.CreateTopBar(AppPage,"TopBar","../web/images/topbarlogo60.gif","TopBar"))
    The way I solved my theming issue was, I first created a "BuildTheme" public sub:
    Code:
    Public Sub BuildTheme
        MyTheme =ABMShared.MyTheme

        MyTheme.AddCellTheme(
    "TopBarLogo")
        MyTheme.Cell(
    "TopBarLogo").Align = ABM.CELL_ALIGN_CENTER
        MyTheme.Cell(
    "TopBarLogo").VerticalAlign = True

        MyTheme.AddInputTheme(
    "TopBarSearch")
        MyTheme.Input(
    "TopBarSearch").BackColor = ABM.COLOR_GREY
        MyTheme.Input(
    "TopBarSearch").BackColorIntensity = ABM.INTENSITY_LIGHTEN2
        MyTheme.Input(
    "TopBarSearch").ForeColor = ABM.COLOR_BLUE
        MyTheme.Input(
    "TopBarSearch").ForeColorIntensity = ABM.INTENSITY_DARKEN4
        MyTheme.Input(
    "TopBarSearch").FocusForeColor = ABM.COLOR_BLUE
        MyTheme.Input(
    "TopBarSearch").FocusForeColorIntensity = ABM.INTENSITY_DARKEN4
        MyTheme.Input(
    "TopBarSearch").AutoCompleteBackColor = ABM.COLOR_DEEPPURPLE
        MyTheme.AddCellTheme(
    "TopBarSearch")
        MyTheme.Cell(
    "TopBarSearch").Align = ABM.CELL_ALIGN_CENTER
        MyTheme.Cell(
    "TopBarSearch").VerticalAlign = True

        MyTheme.AddCellTheme(
    "TopBarBtn")

        MyTheme.Cell(
    "TopBarBtn").Align = ABM.CELL_ALIGN_CENTER
        MyTheme.Cell(
    "TopBarBtn").VerticalAlign = True
        MyTheme.AddButtonTheme(
    "TopBarBtn")
        MyTheme.Button(
    "TopBarBtn").WavesCircle = True

        MyTheme.AddCellTheme(
    "TopBarBtn3")
        MyTheme.Cell(
    "TopBarBtn3").Align = ABM.CELL_ALIGN_CENTER
        MyTheme.Cell(
    "TopBarBtn3").VerticalAlign = True
        MyTheme.AddButtonTheme(
    "TopBarBtn3")
        MyTheme.Button(
    "TopBarBtn3").BackColor = ABM.COLOR_GREY
        MyTheme.Button(
    "TopBarBtn3").BackColorIntensity = ABM.INTENSITY_LIGHTEN2
        MyTheme.Button(
    "TopBarBtn3").WavesCircle = False
        MyTheme.Button(
    "TopBarBtn3").WavesEffect = ABM.WAVESEFFECT_NONE
        MyTheme.Button(
    "TopBarBtn3").ForeColor = ABM.COLOR_BLUE
        MyTheme.Button(
    "TopBarBtn3").ForeColorIntensity = ABM.INTENSITY_DARKEN4
    End Sub
    But the real magic in this was the very first line... that extended the existing theme into my Code Module
    All I had the to do was call this sub from within the Shared CodeModule BuildTheme sub.
    The only issue I still have is, I can't figure out how to theme the subcontainer.

    Then, to make things a bit cleaner on the events side...
    Code:
    Sub Page_ParseEvent(Params As Map)
        
    Dim eventName As String = Params.Get("eventname")
        
    Dim eventParams() As String = Regex.Split(",",Params.Get("eventparams"))
    ...
        
    If eventName = "topbarbtn1_clicked" Then
            
    CallSub2 (myTopBar, "TopBarBtn1_Clicked",Params.Get(eventParams(0)))
        
    End If
    This way we can have the events declared inside our CodeModule keeping it all nice and neat!

    Hope this helps somebody!
     
    Last edited: Sep 15, 2017
    Mashiane, Erel, alwaysbusy and 2 others like this.
  2. Harris

    Harris Well-Known Member Licensed User

    Got a simple picture of what this looks like?

    If eventName = "topbarbtn1_clicked"ThenCallSub2 (myTopBar, "TopBarBtn1_Clicked",Params.Get(eventParams(0)))EndIf

    This is interesting... Something like "if it is a table".... Events in the CM, nice. I have not taken the time to strip the build theme and stick it in a code module (like shared). I really don't see the need for each page to have a buildtheme since since each new page is a repeat of the previous construction. At times, one gets lost in what NEEDS to be with the page and what can be shared... So, I go with status quo... and "rinse and repeat"...

    Thanks
     
  3. alwaysbusy

    alwaysbusy Expert Licensed User

    It is 4am and I really should get some sleep, but your approach gave me an idea I'm going to test out. Report back soon...
     
    inakigarm likes this.
  4. Harris

    Harris Well-Known Member Licensed User

    4 am, don't you work at 6?
    Your over active creative mind keeps us all up.
    We all know how much more productive we could be if sleep was not an imperative.

    In your case (and mine), give a dog a bone - then try and take it away... I feel your ambition. That is what I admire.
    @Cableguy has been quite busy questioning / testing and exploring above and beyond the fundamentals. Excellent.

    Without this feedback, your ideas cannot foster alone. That is why, i suspect, he is ranked as "expert", rightfully so.

    You once advised me to shut it down and reconsider ( complicated matter) - which I did and after reconsideration, all was clear.

    It - ABM only gets better....
     
  5. alwaysbusy

    alwaysbusy Expert Licensed User

    It is going to be an all nighter I think. But it is weekend after tomorrow (today?)!

    I know! But I'm not known to take my own advice...;)

    But if I don't look look into it right away, I get the weirdest dreams. Once woke up in sweat as I was searching for an algorithm to zip refugees so they could fit on my USB stick. Took me a whole minute to figure out humans could not be compressed at all :confused:
     
    Mashiane likes this.
  6. Harris

    Harris Well-Known Member Licensed User

    Sheat.. I get these weird programming dreams as well. Not as off as yours, but difficult tasks that could be resolve thru dreams... Weird shit... The mind just won't shut down and let us play normal.

    Take a break....
     
    Mashiane and alwaysbusy like this.
  7. mindful

    mindful Active Member Licensed User

    I think your solution would not work properly because you created a code module. Take the following scenario:

    You get one connection to your page. -> the page calls the TopBar2.CreateTopBar which saves references as private in the code module for the Target page and returns your top bar.

    You get another connection to your page -> the page calls the TopBar2.CreateTopBar which replaces all the private references saved with the above connection.

    So as a fix I suggest to declare your variables inside the method. And also if withing your events (TopBarBtn1_Clicked) declared in the code module you access private or public variables declared in the code module you should replace CallSub with CallSubDelayed because the pages that call the subs in the code module run on different thread.

    This is just a sugestion as I don't know what you are trying to acomplish ... and maybe in your situation your code will work.
     
    Cableguy likes this.
  8. Cableguy

    Cableguy Expert Licensed User

    I have tried connecting to the webapp from several browsers and had no issues with it...
    This particular component will only be used in this one page (my webapp will have only this one page), and, as I stated, this is MY solution to MY particular problem, and it works as I expected (except for the subcontainer theme thing)

    Treat this as a proof of concept, anyone that uses this concept will need to adapt it to his particular needs
     
    alwaysbusy likes this.
  9. alwaysbusy

    alwaysbusy Expert Licensed User

    Here is my suggestion on how we could do this. It has limitations: it is not possible to use AddArrayComponent() in the class, nor can the resulting component be added with AddArrayComponent()! I'm not saying it will never be possible, however building this for AddArrayComponent WILL break existing code heavely!

    Ok, here we go with my proposed solution:

    I created a Class (not a module, so we can re-use this component). As Mindful pointed out, in a module, all properties in Global_ are shared with all the logged in users (I would be very surprised if they are not). I used Cableguys example for this.
    Code:
    'Class module
    Sub Class_Globals
       
    Private ABM As ABMaterial 'ignore
     
       
    Private TargetPage As ABMPage
       
    Private ObjectId As String
       
    Private LogoFilename As String
       
    Private TopBarThemeName As String

       
    Public TopBarContainer As ABMContainer ' has to be public, so we can use it in the AddComponent() method
       Private TopBarSubContainer As ABMContainer
       
    Private TopBarLogo As ABMImage
       
    Private TopBarSearch As ABMInput
       
    Private TopBarBtn1 As ABMButton
       
    Private TopBarBtn2 As ABMButton
       
    Private TopBarBtn3 As ABMButton

       
    Private LogoTheme As String
       
    Private SearchTheme As String
       
    Private BtnTheme As String
       
    Private Btn3Theme As String
    End Sub

    ' new part, we give the class it's own ParseEvent method
    public Sub ParseEvent(params As Map)
       
    Dim eventName As String = params.Get("eventname")
       
    Dim eventParams() As String = Regex.Split(",",params.Get("eventparams"))

       
    ' Important! Here we remove the 'parent' part, so we can re-use this component, as the events will have the same name
       ' e.g. myTopBar2aBtn1_Clicked will become Btn1_Clicked
       eventName = eventName.SubString(ObjectId.Length)

       
    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

    'Initializes the object. You can add parameters to this method if needed.
    Public Sub Initialize(page As ABMPage, id As String, filename As String, Theme As String)
       TargetPage = 
    page
       ObjectId = 
    id
       LogoFilename = filename
       TopBarThemeName = Theme
       LogoTheme = TopBarThemeName & 
    "Logo"
       SearchTheme = TopBarThemeName & 
    "Search"
       BtnTheme = TopBarThemeName & 
    "Btn"
       Btn3Theme = TopBarThemeName & 
    "Btn3"

       TopBarContainer.Initialize ( TargetPage, ObjectId, 
    "" )
       TopBarSubContainer.Initialize ( TargetPage, ObjectId & 
    "SubCntr""")
       TopBarLogo.Initialize ( TargetPage, ObjectId & 
    "Logo", LogoFilename, 1 )
       TopBarSearch.Initialize ( TargetPage, ObjectId & 
    "Search", ABM.INPUT_SEARCH, "Pesquizar"False, SearchTheme )
       TopBarBtn1.InitializeRaised ( TargetPage, ObjectId & 
    "Btn1""""""Registar", BtnTheme )
       TopBarBtn2.InitializeRaised( TargetPage, ObjectId & 
    "Btn2""""""Login", BtnTheme)
       TopBarBtn3.InitializeFlat ( TargetPage, ObjectId & 
    "Btn3""settings", ABM.ICONALIGN_CENTER, "", Btn3Theme)

       
    ' NEW PART: we set for each component that we want the handler of the events here to this class
       TopBarContainer.EventHandler = Me
       TopBarSubContainer.EventHandler = Me
       TopBarLogo.EventHandler = Me
       TopBarSearch.EventHandler = Me
       TopBarBtn1.EventHandler = Me
       TopBarBtn2.EventHandler = Me
       TopBarBtn3.EventHandler = Me

       
    'then we build the grid
       TopBarContainer.AddRowsMV(1False,7,7, ABM.VISIBILITY_SHOW_ON_LARGE_ONLY,"").AddCellsOS(1,0,0,0,2,2,2,"").AddCellsOS(1,0,0,0,7,7,7,"").AddCellsOS(1,0,0,0,3,3,3,"")
       TopBarContainer.BuildGrid 
    ' IMPORTANT!

       TopBarSubContainer.AddRowsMV(
    1False,0,0, ABM.VISIBILITY_SHOW_ON_LARGE_ONLY,"").AddCellsOSMP(1,0,0,0,5,5,5,0,0,0,5,"").AddCellsOSMP(1,0,0,0,5,5,5,0,0,5,0,"").AddCellsOS(1,0,0,0,1,1,1,"")
       TopBarSubContainer.BuildGrid 
    ' IMPORTANT!

       
    'and we set the cell height
       TopBarContainer.Row(1).SetFixedHeight(70False)
       
    '  TopBarSubContainer.Row(1).SetFixedHeight(60, False)

       
    'we then add the picture Logo and set its theme
       TopBarContainer.Cell(1,1).AddComponent(TopBarLogo)
       TopBarContainer.Cell(
    1,1).UseTheme(LogoTheme)

       
    'Now we add the search field and set its theme
       TopBarSearch.Narrow = True
       TopBarContainer.Cell(
    1,2).AddComponent(TopBarSearch)
       TopBarContainer.Cell(
    1,2).UseTheme(SearchTheme)

       
    'now we add the SubContainer for Buttons 1 & 2
       TopBarContainer.Cell(1,3).AddComponent(TopBarSubContainer)

       
    'finaly we start adding the buttons
       TopBarBtn1.UseFullCellWidth =True
       TopBarSubContainer.Cell(
    1,1).AddComponent(TopBarBtn1)

       TopBarBtn2.UseFullCellWidth =
    True
       TopBarSubContainer.Cell(
    1,2).AddComponent(TopBarBtn2)
       TopBarContainer.Cell(
    1,3).UseTheme(BtnTheme)

       TopBarSubContainer.Cell(
    1,3).AddComponent(TopBarBtn3)

       TopBarSubContainer.Row(
    1).SetFixedHeight(70False' I'm guessing here for the SubContainer theme problem. You want to center it with the Search component?
       TopBarSubContainer.Cell(1,1).UseTheme(BtnTheme)
       TopBarSubContainer.Cell(
    1,1).MarginTop = "20px"
       TopBarSubContainer.Cell(
    1,2).UseTheme(BtnTheme)
       TopBarSubContainer.Cell(
    1,2).MarginTop = "20px"
       TopBarSubContainer.Cell(
    1,3).UseTheme(BtnTheme)
       TopBarSubContainer.Cell(
    1,3).MarginTop = "20px"

    End Sub

    Sub Btn1_Clicked(Target As String' note we use Btn1_Clicked, NOT objectID & Btn1_Clicked (which is impossible anyway)
       Log("You clicked Btn1 from " & ObjectId)
    End Sub
    So this is basically the same code with a new one: its own ParseEvent() method. Note one BIG difference in this method:
    Code:
    eventName = eventName.SubString(ObjectId.Length)
    By doing this, be can re-use this component all we want, as the event names can be a fixed name. For both myTopBar2A and myTopBar2B (see further), they can use Btn1, Btn2, etc for the event name.
    Also, in the Initialize, each component would get a property EventHandler which points to this class. Check out the comments in the code, ask away if in doubt.

    And now we can create our Btn1_Clicked() event in the class instead of in the main page class.

    In the Page, a small modification is made to the Page_ParseEvent() method:
    Code:
    Sub Page_ParseEvent(Params As Map)
       
    Dim eventName As String = Params.Get("eventname")
       
    Dim eventParams() As String = Regex.Split(",",Params.Get("eventparams"))
       
    'Log(eventName)
       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) ' NEW
       If caller = Me Then ' NEW
         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)  ' NEW
       End If
    End Sub
    So new is now the page.GetEventHandler() method. It retrieves the Class you've set with the component.EventHandler property before.

    Creating and adding components to the page is similar as we're used to:
    Code:
    Dim myTopBar2A As TopBar2
    myTopBar2A.Initialize(
    page"myTopBar2A""../images/batman.png""TopBar")
    page.Cell(2,1).AddComponent(myTopBar2A.TopBarContainer)

    Dim myTopBar2B As TopBar2
    myTopBar2B.Initialize(
    page"myTopBar2B""../images/superman.png""TopBar")
    page.Cell(3,1).AddComponent(myTopBar2B.TopBarContainer)
    Note: As this is now a class, BuildTheme for this component cannot be set in the class itself. It needs to be in the page (or in a module, using Cableguys system). This is because the CSS needs to generated BEFORE the page is loaded.

    In my example, I did it in the page:
    Code:
    public Sub BuildTheme()
       
    ' start with the base theme defined in ABMShared
       theme.Initialize("pagetheme")
       theme.AddABMTheme(ABMShared.MyTheme)

       BuildThemeForTopBar
    End Sub

    Public Sub BuildThemeForTopBar()
       theme.AddCellTheme(
    "TopBarLogo")
       theme.Cell(
    "TopBarLogo").Align = ABM.CELL_ALIGN_CENTER
       theme.Cell(
    "TopBarLogo").VerticalAlign = True
       ...
    End Sub
    The result for this system is that now the events for e.g. Btn1 are raised in the class:
    [​IMG]


    If you guys like this, I can start building it for version 3.75 this weekend.

    Also @Cableguy: The sub container Theme looks like it is working to me, you just didn't use it. Or what I am missing?

    Alwaysbusy
     
    Last edited: Sep 15, 2017
    Mashiane, incendio and Cableguy like this.
  10. alwaysbusy

    alwaysbusy Expert Licensed User

    Also this does not break your existing code, as the page.GetEventHandler() method will return Me and act as usual.
     
  11. Cableguy

    Cableguy Expert Licensed User

    I will give it a better look tonight, maybe, as often, something tiny is eluding me!

    Thanks for the changes proposed, you're the second greatest!
     
    alwaysbusy likes this.
  12. alwaysbusy

    alwaysbusy Expert Licensed User

    @Cableguy No hurry, I just value your inputs as this is your library too.
     
  13. incendio

    incendio Well-Known Member Licensed User

    I have not much experience with ABMaterial, - still in learning process.
    Till now, never use AddArrayComponent(), so unable to use this on class have no effect on me.

    I do have a class, made from ABMInput & ABMTable.

    For ABMInput's class, didn't have any problem at all, though class couldn't catch any events, but all events still catch at page level where I can & intended to put my codes for events on page level.

    For ABMTable's class, I do have a problem, which is events were not catch on class level and on page level. I do have a solution for this problem, but it was kind of tricky.

    So, I am eagerly waiting for your proposed solution to be implemented on new version of ABMaterial :)
     
    alwaysbusy likes this.
  14. OliverA

    OliverA Well-Known Member Licensed User

    Disclaimer: Have not really used ABM yet, I'm just chiming in to see if the code consolidation can be taken even further.

    How about moving the Page_ParsEvent code to the shared module and only having a stub that calls this code in the page?
    In the actual page class have:
    Code:
    Sub Page_ParseEvent(Params as Map)
       
    ' Note: I'm using "SharedModule" here, but this needs
       '    to be the name of the actual shared module
        CallSub3(SharedModule, "Page_ParseEvent", CreateMap("pageid" : ABMPageId, "page":Page"caller":Me), Params)
    Sub
    In the shared module have this:
    Code:
    ' The word "Modded" indicates lines previously posted that have been, well, modded
    ' The words "NEW NEW" indicate lines that have been added
    Sub Page_ParseEvent(CallerInfo As Map, Params As Map' Modded
       Dim eventName As String = Params.Get("eventname")
       
    Dim eventParams() As String = Regex.Split(",",Params.Get("eventparams"))
       
    Dim pageID As String = CallerInfo.Get("pageid"' NEW NEW
       'Log(eventName)
       If eventName = "beforeunload" Then
         
    Log("preparing for url refresh")
         ABM.RemoveMeFromCache(ABMShared.CachedPages, pageID) 
    ' Modded
         Return
       
    End If
       
    Dim pageObject As ABMPAge = CallerInfo.Get("page"' NEW NEW
       Dim callerObject As Object = CallerInfo.Get("caller"' NEW NEW
       Dim caller As Object = page.GetEventHandler(callerObject, eventName) ' NEW Modded
       If caller = callerObject Then ' NEW Modded
         If SubExists(caller, eventName) Then ' Modded
           Params.Remove("eventname")
           Params.Remove(
    "eventparams")
           
    ' BEGIN NEW DRAGDROP
           If eventName = "page_dropped" Then
             pageObject.ProcessDroppedEvent(Params) 
    ' Modded
           End If
           
    ' END NEW DRAGDROP
           Select Case Params.Size
             
    Case 0
               
    CallSub(caller, eventName) ' Modded
             Case 1
               
    CallSub2(caller, eventName, Params.Get(eventParams(0))) ' Modded
             Case 2
               
    If Params.get(eventParams(0)) = "abmistable" Then
                 
    Dim PassedTables As List = ABM.ProcessTablesFromTargetName(Params.get(eventParams(1)))
                 
    CallSub2(caller, eventName, PassedTables) ' Modded
               Else
                 
    CallSub3(caller, eventName, Params.Get(eventParams(0)), Params.Get(eventParams(1))) ' Modded
               End If
             
    Case Else
               
    ' cannot be called directly, to many param
               CallSub2(caller, eventName, Params) ' Modded
           End Select
         
    End If
       
    Else
         CallSubDelayed2(caller, 
    "ParseEvent", Params)  ' NEW
       End If
    End Sub
    Please note this is untested code and may have typos, logic errors, and/or syntax errors. I also don't know if this would have some sort of impact on threading and therefore may not be a suitable way of doing things (if it is suitable to begin with).

    Why did I create a CallerInfo map? Because I don't know how to pass on a pages class module and then still get to ABMPageID and Page variables (since each page's class module has a different class name). So there may even be a cleaner way to call the shared method that just escapes my limited experience with/knowledge of B4J.
     
  15. Cableguy

    Cableguy Expert Licensed User

    Hi Alain, I'm trying to test out your proposed solution, but it seems that, in ver 3.50, "GetEventHandler" does not exist yet...
     
  16. alwaysbusy

    alwaysbusy Expert Licensed User

    No it does not. It is only if you guys approve this system, I will build it in 3.75. This is just a proof of concept I've written. Making this work for real is a lot more work.
     
  17. Cableguy

    Cableguy Expert Licensed User

    I've just created a poll to encourage @alwaysbusy to continue his highly appreciated hard and good work, and to gently ask him to add this new feature to the upcoming v3.75
     
    Last edited: Sep 15, 2017
    alwaysbusy likes this.
  18. mindful

    mindful Active Member Licensed User

    @alwaysbusy so if you do implement it this way will it be necessary to set .EventHandler for every component we create even if we do not wish to delegate ?

    Maybe it can be Null by default and in parseevents of the page it can be if caller = Null or caller = Me then .... else ...

    Just a thought ;)
     
  19. alwaysbusy

    alwaysbusy Expert Licensed User

    @mindful this indeed how it will work (see post #10). If you do not want to delegate, you don't have to do anything. That way it will not break existing projects too. By passing me in the GetEventHandler, it sort of acts like a map.GetDefault method.
     
    mindful likes this.
  20. alwaysbusy

    alwaysbusy Expert Licensed User

    @OliverA I think that may be a bit overkill. Sometimes readability of the code may take precedence over writing less code.
     
    OliverA and Cableguy like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice