WebContainer.Close: Runtime Error

Hi everyone.

I’m working on a Xojo Web app in which I’m trying to simulate a table using the “embedded containers” technique. One container acts as the table, and another is used to add rows into it. The “row container” has a WebCanvas control on it, and a label. The WebCanvas control has a Paint event on it.

I can add row containers to the table container (using the EmbedWithin method). That’s working perfectly.

However, if I attempt to remove a row container (using the Close method), then this runtime exception is thrown:

“The event handler you are trying to remove has not been added with AddHandler.”

If I comment out the WebCanvas control’s Paint event, then attempts to remove the row container work perfectly - and no error occurs. Also, if I delete a row container before the WebCanvas control’s Paint event has finished drawing an image, then the error doesn’t occur.

I have a simple Xojo project that demonstrates this problem. If you’d like to see it, please let me know.

I’m wondering if anyone else has run into this, and if so, how you resolved it. At this point, I’m stumped.

Please File a bug report so I can look at that.

@Greg O’Lone: I’ve just filed <https://xojo.com/issue/45753>.

My workaround for this is to replace the WebCanvas control with a WebImageView.

Thanks.

Ok, so I tracked this down (and I’ll put these notes on the case as well). Your removal code looks like this:

[code]Public Sub RemoveRows()
// We need to remove the first control, but do it “RowCount” times!
For r As Integer = RowCount - 1 DownTo 0

// Get the row control.

Dim Row As Auto = ControlAtIndex(r)
Dim RowX As RowControl = Row
  
  // Close (remove) the row control.
  // If the control's Canvas1 control has a Paint event on it, 
  // then the app will crash here.
  RowX.Close
end if

Next

// Reset the table’s rowcount.
RowCount = 0
End Sub[/code]

There’s one really big problem with this… you’re assuming that everything returned from the ControlAtIndex method is going to be a RowControl. What appears to be happening is that you’re casting some other object to be a RowControl which is causing an error in the framework down the line.

Instead, I can suggest two other ways to do this:

Using a method similar to yours, first check to see if the control you’re operating on is in fact a RowControl before you cast it…

[code]Public Sub RemoveRows()
// We need to remove the first control, but do it “RowCount” times!
For r As Integer = RowCount - 1 DownTo 0

// Get the row control.

dim row as WebObject = ControlAtIndex(r) // ControlAtIndex returns a WebObject
if row isa RowControl then // Check that it's actually a RowControl
  
  // Close (remove) the row control.
  RowControl(Row).Close // Cast it to be a RowControl
end if

Next

// Reset the table’s rowcount.
RowCount = 0
End Sub[/code]

An even better solution would be to keep your own references to the rows as you add them. Just add them to an array on the container and then you can access them by index from that array… you can even use this technique to move all of the remaining rows upward if the row you’re removing is somewhere in the middle.

@Greg O’Lone: Thanks for looking into this, and for the quick response.

I tried using the code that you provided, to ensure that I’m really only trying to close RowControls, and I’m still getting the same error (“The event handler you are trying to remove has not been added with AddHandler.”).

The only type of controls that I’m embedding into the TableControl are RowControls. Adding the test to see confirm that the object returned by ControlAtIndex is a RowControl before trying to close it is a good idea, but it isn’t stopping the runtime exception.

I still think that the problem has to do with the use of a WebCanvas - and specifically one that has a Paint event on it - in the RowControl. If you remove that, then closing the container works as expected. Similarly, if you try to close a container before the WebCanvas Paint event has actually finished, then it works. The exception only occurs if the WebCanvas actually has an image loaded into it.

That leads me to believe that the “event handler” that the error is referring to is actually one that is associated with the WebCanvas. It’s as if there is something about an image being loaded into the WebCanvas that prevents the container from being able to close.

Again, my workaround for this has been to simply replace the WebCanvas with a WebImageView. That’s not ideal, but it does work.

I hope this helps. If you need additional info, or need me to file another bug report, please let me know.

Is this actually crashing your app though? All I could see was that it would break into the debugger and pressing run would just continue.

@Greg O’Lone: Oddly enough, it does not crash when it’s compiled. I’m testing it as a CGI here .

I’ve never run into this before - where an app will appear to crash in the debugger, but run fine when it’s compiled.

Thanks again for your help.

Ok, well what you are seeing is an exception in the Xojo framework that’s actually being caught in a try/catch. That I can fix :slight_smile:

I’m running into this as well. Although the feedback case is fixed, this still seems present in 2016r4.1?

My workaround for this is the following:

I created a custom class with Super ‘WebCanvas’ and in the ‘Open’ event add:

AddHandler Me.Paint, AddressOf paint

Now create a ‘paint’ method in the class to handle the event. Closing the container control will not throw the exception anymore.

The scenario that was outlined in the case was fixed. If you think you’ve found another way to trigger it, please file a new case.

[quote=305666:@Robert Everts]My workaround for this is the following:

I created a custom class with Super ‘WebCanvas’ and in the ‘Open’ event add:

AddHandler Me.Paint, AddressOf paint

Now create a ‘paint’ method in the class to handle the event. Closing the container control will not throw the exception anymore.[/quote]
What this means is that you’re now leaking WebCanvases because you’re creating a circular reference.

I will create a new feedback case.

@Greg O’Lone

I get it that normally you would add a handler for events of a class inside the creator of the class instance.

I thought by using Me.Paint and handling that with a method inside the class, no new instance of the class would be created.

Feedback case #46446

[quote=306000:@Robert Everts]I will create a new feedback case.

@Greg O’Lone

I get it that normally you would add a handler for events of a class inside the creator of the class instance.

I thought by using Me.Paint and handling that with a method inside the class, no new instance of the class would be created.[/quote]
It’s not, but the AddHandler creates a reference which will prevent it from ever being destroyed.

Would it be an option to add a custom destructor that removes the handler with RemoveHandler?

No. The Destructor will never be called because they are only raised when all of the references go away.

Now what you could do is override the Close method and in there call RemoveHandler. Just make sure you call Super.Close when you are done.