Using Introspection to dynamically load DesktopContainers at run-time

Hello:

I am currently experimenting with Introspection and it’s quite nice. I could use a bit of guidance, however as I’ve run into a bit of an issue.

Here’s the scenario:

I created a subclass of a DesktopContainer and added a Property to it. I then created two more containers and set their Super to be that of my subclassed container. So far so good.

On a Window, I placed a DesktopSegmentedButton control with two buttons inside that control.

I figured out how to get the correct container (OneContainer and TwoContainer) to be visible depending on which segmentedbutton is clicked (it works fine). Here’s the code:

for each dc as DesktopContainer in Containers
  if dc IsA OneContainer then
    if OneContainer(dc).ContainerID = sbSelection.SelectedSegmentIndex then
      if OneContainer(dc).isLoaded = false then 
        OneContainer(dc).EmbedWithin(self, sbSelection.Left, sbSelection.Top + 30, 300, 300)
        OneContainer(dc).isLoaded = true
      end if
      
      OneContainer(dc).Visible = true
    else 
      dc.Visible = false
    end if
  elseif dc IsA TwoContainer then
    if TwoContainer(dc).ContainerID = sbSelection.SelectedSegmentIndex then
      if TwoContainer(dc).isLoaded = false then
        TwoContainer(dc).EmbedWithin(self, sbSelection.Left, sbSelection.Top + 110, 300, 300)
        TwoContainer(dc).isLoaded = true
      end if
      
      TwoContainer(dc).Visible = true
      
      var t as Introspection.TypeInfo = Introspection.GetType(dc)
      
      for each prop as Introspection.PropertyInfo in t.GetProperties
        if prop.Name.Lowercase = "containerid" then
          var i as variant = prop.Value(dc)
        end if
      next
    else
      dc.Visible = false
    end if
  end if
next

Now, here’s what I’m “hoping” to do to make this even more flexible, however I’m likely overthinking things. Ultimately, I would like to be able to create even more of these subclassed containers (one for each new segmentedbutton I add) and not have to explicitly reference the concrete container object as I do in the above code example.

Here’s how far I’ve gotten thus far when execution reaches the line that reads: method.invoke(dc, p), I receive an illegalCastException and I cannot figure out which parameter is causing it or “if” I’m even on the right trajectory with this (see code below).

Once I can get this figured out, I’ll create a Xojo Library out of it and share it with any other projects that need to dynamically load, hide and/or display containers at run-time.

I’m close and on to something here. I can feel it!

var containerid as int32 = -1
var isLoaded as Boolean = false

for each dc as DesktopContainer in Containers
  for each prop as Introspection.PropertyInfo in Introspection.GetType(dc).GetProperties
    select case prop.Name.Lowercase
    case "containerid" 
      containerid = prop.Value(dc).Int32Value
    case "isloaded"
      isLoaded = prop.Value(dc).BooleanValue
    end select
    
    if isLoaded = false then 
      if containerid = sbSelection.SelectedSegmentIndex then
        var methods() as Introspection.MethodInfo = Introspection.GetType(dc).GetMethods
        
        for each method as Introspection.MethodInfo in methods
          if method.Name.Lowercase = "embedwithin" then
            var p() as Variant 
            
            p.Add(dc)
            p.Add(20)
            p.Add(20)
            p.Add(300)
            p.Add(300)
            
            method.invoke(dc, p)
          end if
          
          //CustomDesktopContainer(dc).isLoaded = true
          //CustomDesktopContainer(dc).Visible = true
        next
      end if
    end if
  next
next

There is more than one method of DesktopContainer named “embedwithin”, and you’re calling the first one you find, which is not necessarily the one you wanted.

When I run similar code the first one I get is:

EmbedWithin(DesktopUIControl, Integer, Integer, Integer, Integer)

But DesktopContainer is not a subclass of DesktopUIControl, it’s a subclass of DesktopWindow so an IllegalCastException gets raised. The method you want is actually:

EmbedWithin(DesktopWindow, Integer, Integer, Integer, Integer)

Which gets listed second for me when I run this code. But this isn’t guaranteed to always be true, so to find the one you want you’ll have to look at the ParameterInfo for each matching method:

if method.Name.Lowercase = "embedwithin" then
// it's the right name, but check the parameters
Dim params() As Introspection.ParameterInfo = method.GetParameters()
If params(0).ParameterType.FullName = "DesktopWindow" Then
  var p() as Variant 
  
  p.Add(dc)
  p.Add(20)
  p.Add(20)
  p.Add(300)
  p.Add(300)
  
  method.invoke(dc, p)
end if

You could do this with Introspection, but using the Introspection system is usually a strong signal that you shouldn’t be using the Introspection system. :slight_smile: It’s an indicator that you are trying to circumvent the Xojo type system – which, while sometimes necessary, should only be done as a last resort. Using Xojo’s type checking will save you a lot of frustration in the long term, and this is a problem that can be neatly solved using it.

I’ve attached a project containing a segmented button controller that does what you need: it scans the controls of the window it is embedded into for any containers that are of a specific base type (SegementedContainerBase). These containers are then linked to buttons in the segmented button control and their visibility is turned on and off as the buttons in the control are pressed.

You can also create new controls in code and add them; the segmented button control takes care of embedded them into the window. Check the code in MainWindow.Opening.

This project is a cute little example of how you can use Xojo’s class hierarchy to group functionality and enforce requirements on subclasses (check out the code in SegmentedContainerBase.DisplayName, for example), as well as extend the functionality of the built-in classes. Take a look and let me know if you have any questions. The design is intended to be robust and flexible, making it easy to integrate new containers as needed.

Segment Container Controller.zip (21.0 KB)

2 Likes

Great! I’ll take a look. As I stated, it’s purely experimental.

I see Introspection as a combination of two thoughts:

  1. The Art of the Possible

  2. To paraphrase what Jeff Goldblum’s character (Ian Malcolm) said in the original Jurassic Park and what he says about the science of genetics, I say about Introspection:

Genetic power is the most awesome force that the planet has ever seen. You wield it like a kid that’s found his dad’s gun. I’ll tell you the problem with the science you used here. It didn’t require and discipline to obtain it. You read what others have done and you took the next step…Your scientists were so preoccupied with whether or not they “could”, they didn’t stop to think whether or not they “should”.

I LOVE that scene! Great Movie!

Introspection is like a light saber. Use it only when you must.

thanks!!!

Eureka! A T-Rex is born! My experiment is a success!

With great introspection comes great responsibility (and flexibility). :grinning_face_with_smiling_eyes:

I shall use this only when absolutely called-for. Thanks again for all of your inputs.

1 Like