API2 Method for Iterating Over an Embedded Control's Peers

I’m in the process of updating a very large project to API 2.0, which has become more necessary now that in 2024 things like Popovers exist and they can only be opened by DesktopUIControls and not the legacy RectControls.

The structure of my project is a series of various UI classes that are EmbeddedContainerControls. The view hierarchy sometimes is 3 or 4 levels deep of embedded containers inside of embedded containers, etc.

In API 1.0, the code in a RectControl could refer to the Window property to get the container that contains the RectControl. The control could then easily iterate over the Controls list and do things with its peer RectControls. If the RectControl needed the top-level window it belongs to, it could use TrueWindow. Pretty straightforward.

In API 2.0, the code in a DesktopUIControl can get the top-level window it belongs to using Window instead. This appears to simply be a renaming of the old TrueWindow. But how do we get the window that our DesktopUIControl belongs to? It seemed obvious that the Parent property would do this, but alas, it appears that Parent points to the object one level up the the hierarchy that owns the embedded window, and not the embedded window itself.

In other words, my question is – How does a DesktopUIControl that is in an embedded window get a pointer to the embedded window itself so it can iterate over the list of Controls that reside in the same container? The Parent property does not point to the embedded window. Instead it points to an object in the view hierarchy one above the DesktopUIControl, which isn’t particularly useful, as there appears to be no elegant way to get back to the actual embedded window that API 1.0 used to simply provide as Window.

Would Self possibly work?

In a sample project, I have the following Objects:

  1. a DesktopContainer, Smallbox (With a button, BtnAction)
  2. a DesktopContainer, Mediumbox (With a SmallBox)
  3. a DesktopWindow (Window1) (With a MediumBox)

In the SmallBox.BtnAction Pressed Event:

Dim MyParent1 As Object

MyParent1 = Self

When I examine MyParent1 in the Debugger, it shows MediumBox.SmallBox1, and if I add logic to see the ControlCount, it is correct.

As DesktopContainer inherits from DesktopWindow, this is actually pretty easy. I made this short extension method that you can toss in a module:

Public Function ParentView(extends control as DesktopUIControl) As Object
  var parent as Object = control.Parent
  while parent isa DesktopUIControl
    parent = DesktopUIControl(parent).Parent
  wend
  
  Return parent
End Function

Then call like this, in a DesktopButton.Pressed event, for example:

var parent as Object = me.ParentView
if parent isa DesktopContainer then
  MessageBox( "I'M TRAPPED IN A CONTAINER!" )
elseif parent isa DesktopWindow then
  MessageBox( "I'M TRAPPED IN A WINDOW!" )
end if

From there, you can cast parent to its appropriate type – DesktopContainer or DesktopWindow – and iterate over the controls.

It’s important to note that since DesktopContainer inherits from DesktopWindow, the order in which you check the value is important if you want to distinguish between the two. If you don’t care that it’s a DesktopContainer or DesktopWindow then you can just cast the result of the method to DesktopWindow then iterate the contained controls:

var parent as Object = me.ParentView
if parent = nil then Return '// Sanity check
var parentW as DesktopWindow  = DesktopWindow( parent )

for each c as Object in parentW.Controls
  break
next
1 Like

Here’s a version that extends DesktopContainer:

Public Function ParentView(extends container as DesktopContainer) As Object
  var parent as Object = container.Parent
  while parent isa DesktopUIControl
    parent = DesktopUIControl(parent).Parent
  wend
  
  Return parent
End Function

And here’s a couple of methods to get Siblings:

Public Function Siblings(extends container as DesktopContainer) As Object()
  var parent as Object = container.ParentView
  
  if parent <> nil then
    var result() as Object
    
    var parentWindow as DesktopWindow = DesktopWindow( parent )
    for each o as Object in parentWindow.Controls
      if o <> container then result.Add( o )
    next
    Return result
  end if
End Function
Public Function Siblings(extends control as DesktopUIControl) As Object()
  var parent as Object = control.ParentView
  
  if parent <> nil then
    var result() as Object
    
    var parentWindow as DesktopWindow = DesktopWindow( parent )
    for each o as Object in parentWindow.Controls
      if o <> control then result.Add( o )
    next
    Return result
  end if
End Function
1 Like

After building a simple example project to demonstrate the issue I’m having, I have discovered that it is possible to easily iterate through the peer objects of an API 2.0 control when embedded in a container control. You just need the entire view hierarchy to be built on API 2.0 objects.

The problem I was having is that in the process of trying to “slowly” migrate a large project from API 1.0 to API 2.0, I have a “mixed” project with lots of crossover between the two APIs. I’ve got a 1.0 ContainerControl that includes both 1.0 and 2.0 objects. Although this compiles and runs just fine, the API combination appears to create a few holes.

If I’m understanding the situation I’ve created, a 1.0 control in a 1.0 container uses the Control iterators in the Window object. A 2.0 control in a 2.0 container uses the container’s Control iterators. But if a 2.0 control is in a 1.0 container the control doesn’t have a pointer to the container and the Window is the TrueWindow and doesn’t have the correct control list. (Yes, this is confusing)

Bottom line appears to be that I need to push through more significant changes to my project before things start working better. There appears to be no issue other than the realization that transitioning from 1.0 to 2.0 is a lot more complicated for large projects than it initially appeared.

Yes, you should tackle objects as a whole whenever possible. While you can mix Desktop API 1.0 and 2.0 in the same project, there will be problems if you attempt to do some things in a mixed view, as you’ve just learned.

Once you start a window/container, it’s best to just push on through and finish it.

@Anthony_G_Cyphers Great job on the solutions you’ve suggested. My problem is that my DesktopUIControl is contained in a legacy ContainerControl. The API mismatch appears to prevent being able to get a list of siblings to the DesktopUIControl.

I’m just glad you’ve found a solution to your problem.