B4J Question Error when reloading CLV quickly when clv items contain B4XComboBox

Chris2

Active Member
Licensed User
Longtime User
For a while I've been noticing an error when I reload a CustomListView quite quickly:
Each CLV item contains a B4XCombobox and the error seems to be because I'm trying to use clv.GetItemFromView using the underlying combobox.

Attached is a small project showing the issue. If you run it and click the button quickly then you'll see in the logs:
B4X:
Error caught in cmb1_SelectedIndexChanged.
(Possibly occurs when quickly reloading the clv):
java.lang.RuntimeException: Object should first be initialized (B4XView).

Without the Try.... Catch the full error generated is:
B4X:
customlistview._getitemfromview (java line: 415)
java.lang.RuntimeException: Object should first be initialized (B4XView).
    at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:49)
    at b4j.example.customlistview._getitemfromview(customlistview.java:415)
    at b4j.example.main._cmb1_selectedindexchanged(main.java:97)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:111)
    at anywheresoftware.b4a.keywords.Common.CallSub4(Common.java:514)
    at anywheresoftware.b4a.keywords.Common.CallSubNew2(Common.java:469)
    at b4j.example.b4xcombobox$ResumableSub_RaiseEvent.resume(b4xcombobox.java:272)
    at anywheresoftware.b4a.keywords.Common$2$1.run(Common.java:1051)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:833)

The relevant code in the example app is
B4X:
Sub Button1_Click
    
    clv.Clear
    For i=0 To Rnd(3, 10)
        clv.Add(CreateclvItem(clv.AsView.Width, 40), i)
    Next
    
End Sub


Private Sub CreateclvItem(Width As Double, Height As Double) As B4XView

    Dim p As B4XView = xui.CreatePanel("")
    p.SetLayoutAnimated(0, 10, 0, Width, Height)
    p.LoadLayout("clvItem")
    
    txt.Text = "text here"

    cmb1.setItems(Array As String("item1", "item2", "item3", "item4", "item5"))
    cmb1.cmbBox.Value="item3"

    Return p

End Sub


Private Sub cmb1_SelectedIndexChanged (Index As Int)
    
    Dim cmb As B4XComboBox = Sender
    Dim cmbbox As B4XView = cmb.cmbBox
    
    'try...catch... added to preventjava.lang.RuntimeException: Object should first be initialized (B4XView) occuring at
    'Seems to occur when reloading the clv too quickly?
    Try
        Log(cmb.SelectedItem)
        Dim s As String = clv.GetValue(clv.GetItemFromView(cmbbox))    <----error here
        Log(s)
    
    Catch
        Log($"Error caught in cmb1_SelectedIndexChanged.
(Possibly occurs when quickly reloading the clv):
${LastException.Message}"$)

    End Try
    
End Sub

The try catch does its job and in the real app there don't seem to be any bad side effects, but can anyone tell me why this is occuring and perhaps if there's a better way to avoid the error than the Try...Catch?

Many thanks as always.
 

Attachments

  • CLVQuickLoad.zip
    4.3 KB · Views: 145

Mahares

Expert
Licensed User
Longtime User
better way to avoid the error than the Try...Catch?
I think the culprit is this line: cmb1.cmbBox.Value="item3" If you comment it, you will not experience any crash. At least for the many tests I made. I even increased the number of items tenfolde by running your app using :For i=0 To Rnd(30, 100) instead of: For i=0 To Rnd(3, 10)
 
Upvote 0

Chris2

Active Member
Licensed User
Longtime User
Well spotted, and thanks for looking into this @Mahares.

I've found also that using cmb1.SelectedIndex instead of cmb1.cmbBox.Value prevents the error.
So the CreateclvItem sub is now:
B4X:
Private Sub CreateclvItem(Width As Double, Height As Double) As B4XView

    Dim p As B4XView = xui.CreatePanel("")
    p.SetLayoutAnimated(0, 10, 0, Width, Height)
    p.LoadLayout("clvItem")
   
    txt.Text = "text here"

    cmb1.setItems(Array As String("item1", "item2", "item3", "item4", "item5"))
'    cmb1.cmbBox.Value="item3"
    cmb1.SelectedIndex=3


    Return p

End Sub

Interestingly, this code doesn't seem to raise the SelectedIndexChanged event at all which is where the error occured when using cmb1.cmbBox.Value.

So I can work arround the problem by using this new version of the sub, but if anyone can shed any light on why using cmb1.cmbBox.Value="item3" causes an error I'd be glad to hear it.
 
Upvote 0

Mahares

Expert
Licensed User
Longtime User
've found also that using cmb1.SelectedIndex
Yes, I was going to add it to the post later, but I wanted to wait till you react. Actually if you want to display Item3 when you populate the xClv, it should be: cmb1.SelectedIndex = 2 .
If you want to show no item selected when you first populate it, you can use: cmb1.SelectedIndex = -1.
I reworked your project using B4XPages and a B4X project and in B4A, the following line is not even supported: cmb1.cmbBox.Value
Here is how your SelectedIndexChanged event sub should look like:
B4X:
Private Sub cmb1_SelectedIndexChanged (Index As Int)
    Dim cmb As B4XComboBox = Sender
    Dim s As String = clv.GetValue(clv.GetItemFromView(cmb.cmbBox))  
    Log("clv value  " & s)    
    Log("cmb selected item method1 " & cmb.GetItem(Index))  
    Log("cmb selected item method2 " & cmb.SelectedItem)  
    Log("----------")
End Sub
 
Upvote 0

teddybear

Well-Known Member
Licensed User
cmb1.SelectedIndex=3


Interestingly, this code doesn't seem to raise the SelectedIndexChanged event at all which is where the error occured when using cmb1.cmbBox.Value.

So I can work arround the problem by using this new version of the sub, but if anyone can shed any light on why using cmb1.cmbBox.Value="item3" causes an error I'd be glad to hear it.
There are 2 issues in your code.
1. As you said,cmb1.SelectedIndex=3 this code doesn't seem to raise the SelectedIndexChanged event at all, you should use cmb1.cmbBox.SelectedIndex instead of cmb1.SelectedIndex.
2. The cause of the error it is not related to cmb1.cmbBox.Value="item3", it just raised SelectedIndexChanged event, the reason is that the interval time between multiple clicking on button1 is too short, the clv has not been clear and added yet
 
Last edited:
Upvote 0

Chris2

Active Member
Licensed User
Longtime User
2. The cause of the error it is not related to cmb1.cmbBox.Value="item3", it just raised SelectedIndexChanged event, the reason is that the interval time between multiple clicking on button1 is too short, the clv has not been clear and added yet
So how can I prevent this?

I've tried disabling and re-enabling the button on each click:
B4X:
Sub Button1_Click
    
    Button1.Enabled=False
    
    clv.Clear
    For i=0 To Rnd(3, 10)
        clv.Add(CreateclvItem(clv.AsView.Width, 40), i)
    Next
    
    Button1.Enabled=True
    
End Sub
But this does not seem to make any difference.

It feels almost like I need to add a 'Wait for...' somewhere but there are no resumable subs involved anywhere so I son't understand why that would be necessary.
 
Upvote 0

Chris2

Active Member
Licensed User
Longtime User
OK, so adding a sleep(1000) in the Button1_Click sub seems to be enough to prevent the user clicking the button too quickly :
B4X:
Sub Button1_Click
   
    Button1.Enabled=False
   
    clv.Clear
    For i=0 To Rnd(3, 10)
        clv.Add(CreateclvItem(clv.AsView.Width, 40), i)
    Next
    Sleep(1000)
    Button1.Enabled=True
   
End Sub
But it's a bit of a hack, is there a better way?
 
Last edited:
Upvote 0

Mahares

Expert
Licensed User
Longtime User
it's a bit of a hack, is there a better way?
What is the purpose of raising the cmb1_SelectedIndexChanged event when you are populating the xClv. Using: cmb1.SelectedIndex = 2 instead of cmb1.cmbBox.Value="item3" already sets the default selected item to Item3 and causes no crash.
But, if you insist it has to be that way, then you may want to use your sleep(1000) and also implement Lazy Loading in the event you have several hundred items to display, because without lazy loading you may still have that problem when you use a large xClv. If you have trouble with lazy loading, come back.
 
Upvote 0

Chris2

Active Member
Licensed User
Longtime User
What is the purpose of raising the cmb1_SelectedIndexChanged event when you are populating the xClv. Using: cmb1.SelectedIndex = 2 instead of cmb1.cmbBox.Value="item3" already sets the default selected item to Item3 and causes no crash.
Absolutely. Using cmb1.SelectedIndex= in place of cmb1.cmbBox.Value= solves my problem.

With the follow-up questions I was just trying to understand exactly why the error occured.
I had originally thought that because there were no async methods called and no resumable subs involved that each button click would be allowed to finish before the next one was allowed to start. That's clearly not the case.

Thanks for all the help.
 
Upvote 0
Top