Android Question B4A: How to create, raise, and handle custom event on class

Andrew King

Member
Licensed User
In B4A how does one declare and define an event with parameters in a class and in a sub of that class raise the event and pass it parameters?
Then how do you handle that event on say Main from a sub?
 

klaus

Expert
Licensed User
Longtime User
You may have a look at the B4X CustomViews Booklet, it contains examples with source code.

Class code:
B4X:
Private Sub xpnlCursor_Touch (Action As Int, X As Float, Y As Float)
  If Graph.ChartType = "YX_CHART" Then
    If (Action = 0 Or Action = 2) And X > Graph.Left And X < Graph.Right And Y > Graph.Top And Y < Graph.Bottom  And xui.SubExists(mCallBack, mEventName & "_Touch", 2) Then
      If xui.SubExists(mCallBack, mEventName & "_Touch", 2) Then
        CallSubDelayed3(mCallBack, mEventName & "_Touch", Scale(sX).MinVal + (X - Graph.Left) / Scale(sX).Scale, Scale(sY).MaxVal - (Y - Graph.Top) / Scale(sY).Scale)    'in screen coordonates
      End If
    End If
  End If

And Main code:
B4X:
Private YXChart1 As xChart


Private Sub YXChart1_Touch(X As  Double, Y As Double)
  Log(X & " / " & Y)
End Sub

The example uses the XUI library.
 
Upvote 0

Andrew King

Member
Licensed User
You may have a look at the B4X CustomViews Booklet, it contains examples with source code.

Class code:
B4X:
Private Sub xpnlCursor_Touch (Action As Int, X As Float, Y As Float)
  If Graph.ChartType = "YX_CHART" Then
    If (Action = 0 Or Action = 2) And X > Graph.Left And X < Graph.Right And Y > Graph.Top And Y < Graph.Bottom  And xui.SubExists(mCallBack, mEventName & "_Touch", 2) Then
      If xui.SubExists(mCallBack, mEventName & "_Touch", 2) Then
        CallSubDelayed3(mCallBack, mEventName & "_Touch", Scale(sX).MinVal + (X - Graph.Left) / Scale(sX).Scale, Scale(sY).MaxVal - (Y - Graph.Top) / Scale(sY).Scale)    'in screen coordonates
      End If
    End If
  End If

And Main code:
B4X:
Private YXChart1 As xChart


Private Sub YXChart1_Touch(X As  Double, Y As Double)
  Log(X & " / " & Y)
End Sub

The example uses the XUI library.

Ok, but if I'm understanding you correctly this involves user interface events. Is there a way for a module or class (non ui or view related) to have events defined and raised, and then handled by another class or module?
 
Upvote 0

Andrew King

Member
Licensed User
Haha. I figured I would get this response. But I did search the forum and the only event examples I see have to do with user interface and views. I'm talking about class and module events. For example this VB.NET example I am wondering how to do in b4A as in the ide there are no event, or raise event keywords.

Public Class Person

Public Property Age

Public Event Died

Public Sub StartLife()
Age = 0
Do Until Age >= 85
Age += 1
Loop
RaiseEvent Died
End Sub

End Class Person


Public Module World
Public Person as Person

Public Sub Main()
Person = new Person
Person.StartLife()
End Sub

Public Sub PersonDied handles Person.Died()
msgbox("Person is dead.")
EndSimulation()
End Sub

Public Sub EndSimulation()
End()
End Sub

End World
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
To get more 'precise' answers , you should ask more precice questions.
In your case these are not 'events' but routine calls.
The principle is the same as the example in post#2.
Depending on what you want to do you may consider CallSubDelayed insted of CallSub.
 
Upvote 0

Cableguy

Expert
Licensed User
Longtime User
If you had done a simple search and read some of the results, you would most certainly come across Klaus's most complete booklets, and they would have sent you in the right path.
As I have often pointed out... One does not go to a auto repair shop and say "my car does not work, what is wrong?", rather we try to give as much detail as we possibly can to help the mechanic find what is wrong the easiest and quickest way... Time is money.
 
Upvote 0

Andrew King

Member
Licensed User
To get more 'precise' answers , you should ask more precice questions.
In your case these are not 'events' but routine calls.
The principle is the same as the example in post#2.
Depending on what you want to do you may consider CallSubDelayed insted of CallSub.

Thank you Klaus for your answer and your book is very informative as Cableguy nicely pointed out.
And I have done a simple search and found nothing close to what I'm looking for. So I took to the forum to see if anybody could clarify.

YET after Reading through your book the closet thing I've found to what I'm looking for is:

  • CallSub (this appears to gives access to current module to call another module)
  • WaitFor (this can be used after calling a sub on an object that ultimately results in another event handler being called on that same object, where it will wait until completion and what it appears to be able to get parameters in the wait for)
While these offer somewhat of a solution, it is far from the ideal solution. The issues with the above are:
  • CallSub is hardcoded into the class that raises an event and handles the event itself and then this CallSub written inside the event handler can call an outside module sub such as Main, "_EventReceived" for example. What I want is the reverse of this.
  • WaitFor is called from Main and can receive parameters, but the parent sub that calls "WaitFor" must be called first.

I am talking about tying up EventHandlers on modules that contain references to a class event.
So that when an event is triggered on an instantiated object, many different subs on other modules can handle that event at anytime.

What I'm looking for is something similar to this:

Public WithEvents LazyPoster

Public Sub LazyPoster_PostedMessage (sender as object, e as PostEventArgs) Handles LazyPoster.Posted
If e.DidASimpleSearch = false then
Scold(Sender)
End If
End Sub
 
Upvote 0

Andrew King

Member
Licensed User
To get more 'precise' answers , you should ask more precice questions.
In your case these are not 'events' but routine calls.
The principle is the same as the example in post#2.
Depending on what you want to do you may consider CallSubDelayed insted of CallSub.

How is "Public Sub PersonDied handles Person.Died()" a subroutine call?
To me that looks exactly like a sub that handles an event, not a subroutine call.
 
Upvote 0

Cableguy

Expert
Licensed User
Longtime User
Yep. I'm thinking that you probably need to make yourself aware of how Android apps handle events.

Here's a link to get you started -> https://www.tutorialspoint.com/android/android_event_handling.htm

- Colin.

And also make yourself aware of Android Activity life cycle!
There is a very good reason why callsub works the way it works...
If you tie a class to an activity, that class is only "active" when the tied activity is too...
I guess you should also take a good look to code modules, which can be called from whichever class or activity you want, whenever you want!
 
Upvote 0

Andrew King

Member
Licensed User
Thank you now we are getting some good answers! I will read the material.

I am pretty green to Android. The only practice I had was in Android Studio which was so much trouble, I decided to go with B4A.
It also doesn't help that I have a deadline for a project.
Anyhow as I suspected, and as has been clarified from the posts here, Android deals with events differently due to the way Android applications are structured.
So a short answer to my question about trying to do an event the same way in .NET, is that it cannot be done that way, due to Android, and must be done a different way.

So with a workaround I think I have an idea for the closet way to do what I was trying to do:

I could make a class or structure called EventHandler containing two properties: object as obj, and methodname as string.
Basically I have a class with events that I want other objects to listen to and handle.
On that class I will put a public list of object in the Class Globals that I will call EventHandlers which will contain a list of EventHandler objects.
On each module that I want to listen or handle the events of my class I can reference the instantiated object and do Object.EventHandler.add(me, <name of sub>)
On the event handler of a java object, or a user control, or anything else within that same class, I want to relay it to every object listed in EventHandler.
I'd do that by cycling through the EventHandler list and execute callsub(eventhandler.object, eventhandler.name)
This would then call any object's sub listed in the EventHandler list.

Do you foresee any issues with this method?
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
It looks like what you are after replicating is not so much a .NET Event but the behaviour of the underlying MulticastDelegate type which neither Java nor B4X implement. You should be able to make your described approach should work fine within the limitations of which Subs are available to Callsub.
 
Upvote 0

Jeffrey Cameron

Well-Known Member
Licensed User
Longtime User
Sorry I'm late to this party, but I've been on Christmas Holiday ;)

Coming from a .NET background I think this type of prototype is what the OP is asking about:

I created a class module called "clsTest":
B4X:
#Event: PostMade(SendingClass As clsTest, EvArg as PostEventArgs)

Sub Class_Globals
    Type PostEventArgs(DidASimpleSearch As Boolean, PostCount As Int, MadeOn As Long)
    Private mbDidSimple As Boolean = False
    Private moCaller As Object
    Private msEvent As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(Caller As Object, BaseEvent As String)
    moCaller = Caller
    msEvent = BaseEvent
    mbDidSimple = False
End Sub

Public Sub getSimpleSearchMade As Boolean
    Return mbDidSimple
End Sub
Public Sub setSimpleSearchMade(value As Boolean)
    mbDidSimple = value
End Sub

Public Sub MakeAPost(DidSimple As Boolean)
    Dim poEv As PostEventArgs
    poEv.Initialize
    poEv.DidASimpleSearch = DidSimple
    mbDidSimple = DidSimple
    poEv.PostCount = 1
    poEv.MadeOn = DateTime.Now
    If moCaller <> Null Then
        If SubExists(moCaller, msEvent & "_PostMade") Then
            CallSubDelayed3(moCaller, msEvent & "_PostMade", Me, poEv)
        End If
    End If
End Sub

And here is the use of it in the main activity:
B4X:
Sub Process_Globals

End Sub

Sub Globals
    Private foTest As clsTest   
End Sub

Sub Activity_Create(FirstTime As Boolean)
    foTest.Initialize(Me, "TestHandler")
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Private Sub Button1_Click
    foTest.MakeAPost(True)
End Sub

Public Sub TestHandler_PostMade(SendingClass As clsTest, EvArg As PostEventArgs)
    ToastMessageShow("Post made, did search = " & EvArg.DidASimpleSearch, True)
End Sub

Note that I haven't tested this, just typed it in. Also note the "#Event:" tag will only work for Intellisense if you compile the class to a library.
 
Upvote 0
Top