B4J Question How not to fire RangeSlider_ValueChange?

Mashiane

Expert
Licensed User
Longtime User
Hi there..

I am using multiple CLV to load the same layout, 5 of them. The layout used has a rangeslider and there could be 5,10,20 items in the different CLVs.

Each and everytime the value of the rangeslider changes I want to update some values in a label. This works perfectly. The problem is that when loading each of the CLV items, the rangeslider_valuechange event is being fired, I guess due to the loadlayout method.

How do I NOT fire this event on creation of the CLV items and only fire it when a user actually changes the rangeslider value?

Thanks..

This code works well for doing the updates but fires everytime the CLV item is added with the range slider...

B4X:
Sub aSlider_ValueChange (LowValue As Double, HighValue As Double)
    If bLoading = True Then Return
    Dim slider As RangeSlider = Sender
    Dim q As Map = slider.Tag
    Dim key As String = q.getdefault("key","")
    Dim prefix As String = jMash.MvField(key,1,".")
    Dim learner As String = q.GetDefault("learner","")
    Select Case prefix
        Case "1"
            SeLabel(lstActivity1,learner,key,Round2(HighValue,0))
        Case "2"
            SeLabel(lstActivity2,learner,key,Round2(HighValue,0))
        Case "3"
            SeLabel(lstActivity3,learner,key,Round2(HighValue,0))
        Case "4"
            SeLabel(lstActivity4,learner,key,Round2(HighValue,0))
        Case "5"
            SeLabel(lstActivity5,learner,key,Round2(HighValue,0))
        Case "c"
            SeLabel(lstCumulative,learner,key,Round2(HighValue,0))
    End Select
End Sub

I have tried to set a boolean value to true on load and then false after the load, does nothing..

This is the code to load each CLV item...

B4X:
Sub CreateQuestion(q As Map) As B4XView
    'get marks for learner
    Dim learnerm As String = q.GetDefault("learner","")
    Dim key As String = q.GetDefault("key","")
    key = learnerm & key
    Dim marks As String = kvs.GetSimple(key)
    If marks = "" Then marks = "0"
    q.Put("achieved", marks)
    Dim p As Pane
    p.Initialize("")
    p.LoadLayout("vQuestion")
    lblQuestion.Text = " " & q.GetDefault("q","")
    aSlider.MaxValue = q.GetDefault("max",5)
    aSlider.MinValue = q.GetDefault("min",0)
    aSlider.HighValue = q.GetDefault("achieved",0)
    aSlider.LowValue = q.GetDefault("achieved",0)
    aSlider.MajorTickUnit = q.GetDefault("tick",1)
    lblAchieved.Text = q.GetDefault("achieved",0)
    lblAchieved.Tag = q.GetDefault("key","")
    lblMark.Text = q.GetDefault("max",5)
    chkNone.Checked = q.GetDefault("none",False)
    If lblQuestion.text = " Totals" Or lblQuestion.Text.StartsWith(" Activity") Then
        p.Enabled = False
        aSlider.Visible = False
    Else
        p.Enabled = True
        aSlider.Visible = True
    End If
    p.Tag = q
    aSlider.Tag = q
    Return p
End Sub
 

Daestrum

Expert
Licensed User
Longtime User
The only way I could get it to work properly using a Boolean flag was by inserting a sleep(1) after a value change
ie
B4X:
Dim noFlag as Boolean = false
....
 noFlag = True
 RangeSlider1.LowValue = 10 ; set the values here
 Sleep(1)
 RangeSlider1.HighValue = 50
 Sleep(1)
 noFlag = False
....

'then in _ValueChange ( ...)

if noFlag then return
...

It appears it needs a miniscule delay for it to be able to see the Boolean flag in the ValueChange sub. (maybe a different thread??)
 
Upvote 0

EnriqueGonzalez

Well-Known Member
Licensed User
Longtime User
Hi!

It is not about threading, it is about Events queue, each event will be triggered after the ui thread is finished working that is why having a flag is not useful.

the best approach to the events queue is to have a property change, for example: enabled.

Pseudo code.
B4X:
rgSlider.enabled  = false 
rgslider.value = 5

Private sub rangeslider_valuechange
if rgSlider.enabled then
'Here is where you prevent your code of running for the first time and it is queue friendly.
rgslider.enabled = true
else
'Your normal code
end if
end sub
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Sub to ignore change events that happen up to 100ms after the layout is loaded (change the interval as needed):
B4X:
Sub IgnoreValueChangedEvents
   Dim start As Long = DateTime.Now
   Dim interval As Int = 100
   Do While True
     Wait For aSlider_ValueChange (LowValue As Double, HighValue As Double)
     If DateTime.Now < start + interval Then
       Log("Ignored")
     Else
       aSlider_ValueChange (LowValue, HighValue)
       Exit
     End If
   Loop
End Sub

Usage:
B4X:
Activity.LoadLayout(...)
IgnoreValueChangedEvents
 
Upvote 0

Mashiane

Expert
Licensed User
Longtime User
The Sleep idea actually made me think ResumableSub, so I tested this, after I changed my "LoadActivity1..n" subs to them and to return true, I didnt need to test the return value. It seems to be working perfectly now, thanks guys...

B4X:
Sub LoadLearner(sLearner As String)
    'clear the accordion
    'load questions
    bLoading = True
    Accordion1.Panes.Clear
    Accordion1.LoadLayout("vActivity1","Formative Activity 1: " & sLearner)
    Accordion1.LoadLayout("vActivity2","Formative Activity 2: " & sLearner)
    Accordion1.LoadLayout("vActivity3","Formative Activity 3: " & sLearner)
    Accordion1.LoadLayout("vActivity4","Formative Activity 4: " & sLearner)
    Accordion1.LoadLayout("vActivity5","Formative Activity 5: " & sLearner)
    Accordion1.LoadLayout("vCumulative","Cumulative Formative: " & sLearner)
    Accordion1.LoadLayout("vSummative","Summative Activity: " & sLearner)   
    Wait For(LoadActivity1(sLearner)) Complete (Result As Boolean)
    Wait For(LoadActivity2(sLearner)) Complete (Result As Boolean)
    Wait For(LoadActivity3(sLearner)) Complete (Result As Boolean)
    Wait For(LoadActivity4(sLearner)) Complete (Result As Boolean)
    Wait For(LoadActivity5(sLearner)) Complete (Result As Boolean)
    Wait For(LoadCumulative(sLearner)) Complete (Result As Boolean)
    bLoading = False
End Sub
 
Upvote 0

Mashiane

Expert
Licensed User
Longtime User
This is an example of one of the subs...

B4X:
Sub LoadActivity3(sLearner As String) As ResumableSub
'define the question and mark
    Dim scores As Map
    scores.Initialize
    scores.Put("3.1","5")
    scores.Put("3.2","1")
    scores.Put("3.3","5")
    scores.Put("3.4","8")
    scores.Put("3.5","2")
    scores.Put("3.6","3")
    scores.put("3.7","3")
    scores.Put("3.8","2")
    scores.Put("3.9","3")
    scores.Put("3.10","3")
'clear the clv
    Dim q1 As Int
    lstActivity3.Clear
    For q1 = 1 To 10
        Dim key As String = "3." & q1
'add only questions in the scores, there might be jumps e.g. q3 not asked
        If scores.ContainsKey(key) Then AddQuestion(lstActivity3,"3",q1,q1,sLearner,scores)
    Next
'sum the map values ie.total marks
    Dim itv As Int = jMash.SumMapValues(scores)
'add each question with its mark for the learner
    AddQuestion(lstActivity3,"3","total",itv,sLearner,scores)
    'store totals for such
    kvs.PutSimple("3.totalo",itv)
    Return True
End Sub


Sub AddQuestion(clv As CustomListView, fa As String, qn As String, mark As String, slearner As String, scores As Map)
    Dim q As Map
    Dim p As B4XView
    Dim pfx As String
    If qn = "total" Then
        pfx = "Totals"
    Else
        pfx = "Question " & qn
    End If
    Dim key As String = $"${fa}.${qn}"$
    If scores.ContainsKey(key) Then mark = scores.Get(key)
    q = CreateMap("q":$"${pfx}"$,"max":$"${mark}"$,"key":$"${fa}.${qn}"$,"learner":slearner)
    p = CreateQuestion(clv,q): p.height = 60
    clv.Add(p,$"${fa}.${qn}"$)
End Sub
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Excellent @Erel, you could also use this technique when updating the values generally, such as loading a new set of data. if you don't want to call the update processing as it effectively consumes the update event. Another one for the trick box.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
The Sleep idea actually made me think ResumableSub, so I tested this, after I changed my "LoadActivity1..n" subs to them and to return true, I didnt need to test the return value. It seems to be working perfectly now, thanks guys...
Unless I'm missing something, there is nothing asynchronous in the LoadActivity sub. I don't see how using Wait For will change anything.
 
Upvote 0

Mashiane

Expert
Licensed User
Longtime User
Unless I'm missing something, there is nothing asynchronous in the LoadActivity sub. I don't see how using Wait For will change anything.
Yes, there is nothing asynchronous and strangely baffling it worked for some reason.

1. Without Wait For and logging bLoaded inside aslider_valuechange returned False and thus fired the event(this hanged my app)
2. With Wait For and logging bLoaded inside aslider_valuechange returned true and thus exited the sub. (this smoothed my app)

In these cases I havent used the code you provided but will, this was just a thought. #ICouldBeWrong.
 
Upvote 0
Top