I’m attempting to call a method of each container control on a window by accessing them via the controls array. For example, if I put the following code in a PushButton action method on the parent window:
For Each oControl As Control In Self.Controls
If oControl IsA MyCC Then
MyCC( oControl ).SaveData
End If
Next
It fails, as it never finds a control that IsA MyCC. Looking at the array and other posts I find that I can do the following instead:
For Each oControl As Control In Self.Controls
If oControl IsA EmbeddedWindowControl Then
MyCC( oControl ).SaveData
End If
Next
I now spot the container controls but I get an IllegalCastException on the MyCC( oControl ), even though the container is of that type.
So, thinking laterally I tried creating a Class Interface and assigning it to the container control and casting to the interface and calling an interface method as follows:
For Each oControl As Control In Self.Controls
If oControl IsA EmbeddedWindowControl Then
SaveDataInterface( oControl ).SaveData
End If
Next
Still no joy, the same illegalCastException arises.
So, what do I have to do to allow me to call a method of the Container Control from this Controls() reference.
The window in the example consists of two items. The ContainerControl and PushButton. When I stop the debugger in the PushButton Action event the Window.controls() array has two elements:
PushButton
EmbeddedWindowControl
However, when I look at the Controls link in the Debugger it has three elements:
Clicking on the _Window1._wrapper_ContainerControl11 yields the object I want to deal with, however, that doesn’t show up in the iteration of the Controls() array. The Name of the EmbeddedWindowControl is also the _Window1… element. So it would seem that the Debugger is somehow using this to access the ‘Real container’. How can I emulate this.
If I understand what you’re trying to do, then it’s the same as when I want to embed a container control in a window and then use it by referencing controls and methods in the container.
For starters, you have to embed the container and create a reference to it. So, I start by embedding a container from my project Nav panel (cntMainData in my case) into the MainWindow and calling it “MainData”. I then place MainData in an array (dataContainers) and create a reference to it in another array (dataContainerIndex) so I can reference it later.
// Add the "MainData" container to the MainWindow
MainData = New cntMainData
MainData.EmbedWithin(MainWindow, 5, 20)
dataContainers.Append MainData // place container in array for future control
dataContainerIndex.Append("MainData") // add ability to ID each container in the array inContainers later
Now, when I need to reference any control, method, etc. from the embedded container (such as your saveData method), I use:
for ndx As Integer = 0 to Ubound(dataContainerIndex) // point @ MainData container control
if dataContainerIndex(ndx) = "MainData" then
cntMainData(dataContainers(ndx)).saveData
// anything else you want to do with an item in the container
end if
next
Not sure if that’s the same as you’re asking, but thought I’d share it just in case.
The problem of converting an EmbeddedWindowControl to a ContainerControl has been discussed a few times before. I know a few of us worked the problem a while back, and here’s code from one of my projects.
I put this in a module, as it extends EmbeddedWindowControl:
Public Function getContainer(extends ewc as EmbeddedWindowControl) as ContainerControl
if ewc = nil then Return Nil
if ewc.Handle = 0 then Return Nil
var objects as Runtime.ObjectIterator = Runtime.IterateObjects
var currentObject as Object
var currentType as Introspection.TypeInfo
var currentProps() as Introspection.PropertyInfo
while Objects.MoveNext
currentObject = Objects.Current
if currentObject isa ContainerControl then
currentType = Introspection.GetType(currentObject)
currentProps = currentType.GetProperties
for each prop as Introspection.PropertyInfo in currentProps
if prop.Name = "handle" and prop.Value(currentObject) = ewc.Handle then Return ContainerControl( currentObject )
next
end if
wend
End Function
So, when iterating over the controls, I have this code:
for each currentControl as Control in self.Controls
if currentControl isa EmbeddedWindowControl then
var currentEWC as EmbeddedWindowControl = EmbeddedWindowControl( currentControl )
var currentContainer as ContainerControl = currentEWC.getContainer()
if currentContainer <> nil then
currentContainer.SomeMethod()
end if
end if
next
Dim oCC As ContainerControl
For Each oControl As Control In Self.Controls
If oControl IsA EmbeddedWindowControl Then
oCC = ContainerControl( oControl.Window )
End If
Next
@Anthony_G_Cyphers
So, looking at your function it appears to be iterating across every item that is created within the entire application. Wow, talk about using a sledgehammer to crack a walnut. If that’s what’s required I suppose it will be the answer. I suspect that’s what the debugger is doing, or something similar, to present the extra item in the controls view!
Yeah, you basically have to. EmbeddedWindowControl(and thus ContainerControl) is a very special control. It really is, internally, like a window object that’s been embedded into another view.
You have to wonder why they didn’t simply add a ContainerInstance property to the EmbeddedWindowControl class that would simply return the container control to you. Sure you would still need the secret sauce but at least it would be nice simple sauce.
Nice. Adding my voice to this. It should be a simple addition, but without knowing the internals I’m probably shooting myself in the foot by saying that.
We’ve handled this in a different way with great success.
Create a common super for your Windows.
Create a common super for your ContainerControls.
In the Window super, add methods to RegisterContainerControl, UnregisterContainerControl, and a function RegisteredContainerControls. Register and Unregister will record and remove WeakRefs from a private array, and the function will translate those WeakRefs back to ContainerControl in an array.
In the CC super, implement the Open event and have it check the class of TrueWindow. If it’s your Window super, call Register on itself. On close, call Unregister.
Now you will have easy access to your Window’s CC’s, and all you have to do is set the supers of the CC and Window respectively.
I’ve switched to the Registration method. I’ve a array of container controls on the parent window and a method to allow the containers to register themselves. Then you can loop through the array rather than all controls on the window.
The cost of disentangling the control from it’s EmbeddedWindowControl was enormous, especially if you take into account that every variant is an element in the runtime. It was taking 12 full seconds to find which control I had, in extreme circumstances.
Private Property Panels() as LoadSaveDataInterface
Sub RegisterPanel(CC as LoadSaveDataInterface)
Panels.Add CC
End Sub
In each panels open event:
WindowClass( Window ).RegisterPanel( Self )
You can then do things like this in the parent window.
Public Sub PerformValidation(lblError as Label, BtnAction0 as PushButton, BtnAction1 as PushButton)
'
' Trigger each panels ValidData method
'
For Each Panel As LoadSaveDataInterface In Panels
Panel.SomeMothod
Next
End Sub
By using an array of Interface (rather than class names or CustomControl) you get the protection that you can’t register an control that doesn’t support the interface and also you don’t have to perform the cast to that interface when calling it