How do I access this property

I have a ContainerControl which has a number of controls and some properties. Let’s say that a property is called myProp. Several of the controls are a class which is a subclass of TextArea, call it myTextarea, which has some methods. So there are several instances of myTextarea.

Now, myProp is some read-only data which will be needed by all the instances of myTextarea, specifically by their KeyDown event handlers. And because all the KeyDown event handlers are identical, I’ve put their code in a myTextarea method, to be called by the actual KeyDown event handlers.

The difficulty I have is how to refer to myProp from within this method. Just using its name gives “not found”, prefixing with me or self just gives that myTextarea has no such property.

How should I be doing this?

ContainerControl1.myProp ?

Mmmm yes, but I though one was supposed to avoid that sort of thing, on the grounds that if I rename the CC, I then have to fiddle inside a class to fix things up.

Nope. Xojo automatically renames the Super in all Classes which have this Con.tControl as a Super. :slight_smile:

You are correct, Tim. Calling up the chain is not the proper way. In myTextArea create a new Event Definition (I.e. HandleKey). In the KeyDown event of myTextArea, put
RaiseEvent HandleKey
Now go to the container control find the instance, or control set, in question and add the event HandleKey. From that event call whatever methods you wish. In this way the super, in this case the container control, handles calling the method. This will work no matter what super you place your class on.

Rogers idea is a sound way to do it.

One caveat though is if you have lots of myTextAreas, then you have to go through all of them and add HandleKey, each with the same bit of code to pass the myProp data back.

This could be alleviated with an Interface that returns the myProp data. The myTextArea Class code would get the ContainerControl it’s on, cast it to the Interface and get the data. To use, just add the Interface to your ContainerControl and drop myTextAreas on it, they will automatically find the data.

[code]Interface iCustomData
Sub getPropData() As String
End Interface

Class myContainerControl Implements iCustomData

Property myProp As String = “okay”

Sub getPropData() As String
//implements interface
return myProp
End Sub

End Class

Class myTextArea

Sub test()

if self.Window IsA iCustomData then    
  
  MsgBox iCustomData( self.Window ).getPropData

end

End Sub

End Class[/code]

This Interface idea is only applicable if all myTextArea instances will use the same data. If some will need to get different datas then use Rogers idea.

OK - I’m defining that as HandleKey (key as string) as boolean because that’s what KeyDown uses.

In that KeyDown event I seem to have ended up with:

dim result As Boolean result = RaiseEvent HandleKey (key) return result

because otherwise the compiler complains.

Now in there, presumably I need:

dim result As Boolean result = me.keydownBody (key) return result

where keydownBody is a method inside the myTextArea class to do the actual work. But I still have the issue I started with: how to access myProp from this method.

Going out to dinner now, back later and I’ll take a look at Will’s post.

Pass key as string to your event but no need to return a value.

[quote]dim result As Boolean
result = RaiseEvent HandleKey (key)
return result[/quote]
All you need is RaiseEvent HandleKey(Key)

[quote]Now in there, presumably I need:

dim result As Boolean
result = me.keydownBody (key)
return result[/quote]

No. In there you call this method to handle the key passing key as string to that method. No other work is needed because that method sits in the container control which is doing the calling.

[quote=285704:@Roger Clary]Pass key as string to your event but no need to return a value.

All you need is RaiseEvent HandleKey(Key)[/quote]
If I do that the compiler says I need to use the value returned by the method. The KeyDown event has to return a boolean, the value of which controls what happens to the key character that the user typed. At the bottom I have a method that processes the key and decides whether to return true or false. That boolean needs to be passed back via the HandleKey event, and via the event definition in the myTextArea subclass, to the actual KeyDown handler in that subclass, which returns it so that the key typed by the user is either processed further or not.

But if that method sits in the container control, how does it access the class methods and properties (and it does need to). If it sits in the myTextArea subclass, how does it access the container control’s properties.

I seem to be missing something fundamental here.

You need to create an Event Definition for your class. I have a feeling from your description that you have done something else.
I’m somewhat confused as to what you are trying to do. In your original post I understood that “myProp” is a property of the CC. If that is the case, then creating the event definition will allow the CC to refer to its own property as I described.

If, on the other hand, you are trying to access a method which belongs to your myTextArea, then just call that method directly from any of the instances of myTextArea. If myProp is only used my the myTextArea controls, then make it a property of your subclass and dispense with all of the other stuff.
MyTextArea1.myProp

[quote=285626:@Tim Streater]I have a ContainerControl which has a number of controls and some properties. Let’s say that a property is called myProp. Several of the controls are a class which is a subclass of TextArea, call it myTextarea, which has some methods. So there are several instances of myTextarea.

Now, myProp is some read-only data which will be needed by all the instances of myTextarea, specifically by their KeyDown event handlers. And because all the KeyDown event handlers are identical, I’ve put their code in a myTextarea method, to be called by the actual KeyDown event handlers.

The difficulty I have is how to refer to myProp from within this method. Just using its name gives “not found”, prefixing with me or self just gives that myTextarea has no such property.[/quote]
I understand your setup as follows – please correct me if I’m wrong:

Class MyContainerControl Properties: MyProp // The MyTextArea class needs to get this value Controls: textArea1 As MyTextArea textArea2 As MyTextArea ...

First their is no need for a separate event handler and/or method like this:

...because all the KeyDown event handlers are identical, I've put their code in a myTextarea method, ...

As the KeyDown event of MyTextArea is identical for all instances you will create of MyTextArea, just put the code into the this KeyDown event and raise a newly added KeyDown event definition:

[code]// Add a new KeyDown event definition to MyTextArea
Function KeyDown(key As String) As Boolean

// Implement the KeyDown event of MyTextArea and raise the above event definition
Function KeyDown(key As String) As Boolean
If RaiseEvent KeyDown(key) Then
Return True
End
// Query the container control here
End[/code]

Secondly I think you stumbled across the EmbeddedWindowControl vs ContainerControl issue, as you want to access ContainerControl at runtime, which is not easily possible. A ContainerControl at runtime is a Canvas subclass called EmbeddedWindowControl. You can unfortunately not cast an EmbeddedWindowControl to a ContainerControl as the latter is a subclass of Window. This is a an extension function on EmbeddedWindowControl which returns itself as ContainerControl:

[code]Module EmbeddedWindowControlExtensions

Static dct As New Dictionary()

Dim ewcHandle As Integer = ewc.Handle

Dim retval As ContainerControl = Nil

If dct.HasKey(ewcHandle) Then
Dim wr As WeakRef = dct.Value(ewcHandle)
If wr.Value IsA ContainerControl Then
retval = ContainerControl(wr.Value)
End
End

If retval Is Nil Then
Dim iterator As Runtime.ObjectIterator = Runtime.IterateObjects()
Do Until Not iterator.MoveNext()
If iterator.Current IsA ContainerControl Then
Dim cc As ContainerControl = ContainerControl(iterator.Current)
If cc.Handle = ewc.Handle Then
retval = cc
Exit
End
End
Loop
dct.Value(ewcHandle) = New WeakRef(retval)
End

Return retval

End Module[/code]

With this you can now replace the part // Query the container control here in the KeyDown event of MyTextArea with something like this:

... Dim parent As EmbeddedWindowControl = EmbeddedWindowControl(Parent) Dim cc As ContainerControl = parent.ContainerControl Dim myCC As MyContainerControl = MyContainerControl(cc) Dim myPropValue As String = myCC.MyProp ... Return False

There is a ton of request to introduce a ContainerControl property on EmbeddedWindowControl, a request which Xojo hasn’t addressed in the last 10 years. It makes ContainerControls less valuable. I think that technically it should be easy for Xojo to implement that, but they just don’t care – all request are either reviewed and then forgotten or they have never been reviewed at all. Here are a few of them:
2722 - ContainerControl and IsA incompatibility
4491 - Suggestion for getting ContainerControl references via Window.Control
6027 - Traverse Controls on Dynamic ContainerControls
23030 - Add EmbeddedWindowControl.Container property

You must select the correct level of encapsulation. Since the textfield needs a property of the container, you must move encapsulation to the container level and not try to slavishly adhere to encapsulation at the level of the control. Move the method that handles the key to the container and pass it a reference to “me” (the control).

Ah, I was seeing it like this

[code]Class MyContainerControl Inherits ContainerControl

Property myProp As String

Control myTextArea1 As myTextArea
Control myTextArea2 As myTextArea

End Class

Class myTextArea Inherits TextArea

Event Function KeyDown(Key As String) As Boolean

…code to process key
…in here you want to get or set myProp on the CC this instance is on

return something
End Event

End Class[/code]

What you described makes me think of this

[code]Class MyContainerControl Inherits ContainerControl

Property myProp As String

Control myTextArea1 As myTextArea
Event Function KeyDown(Key As String) As Boolean
return me.keydownBody(Key)
End Event

Control myTextArea2 As myTextArea
Event Function KeyDown(Key As String) As Boolean
return me.keydownBody(Key)
End Event

End Class

Class myTextArea Inherits TextArea

Function keydownBody(Key As String) As Boolean

…code to process key
…in here you want to get or set myProp on the CC this instance is on

return something
End Event

End Class[/code]

Is this necessary when getting the CC an instance is on? I tested with casting self.Window to an Interface or the CC type and it worked. I have little experience with .Window so I’m not sure it always works or always gives the right CC.

[code]Class MyContainerControl Inherits ContainerControl

Property myProp As String

Control myTextArea1 As myTextArea
Control myTextArea2 As myTextArea

End Class

Class myTextArea Inherits TextArea

Event Function KeyDown(Key As String) As Boolean

dim localMyProp As String

if self.Window IsA MyContainerControl then
  localMyProp = MyContainerControl( self.Window ).myProp
else
  //not on the valid CC
end

return something

End Event

End Class[/code]

This is tied to MyContainerControl. Use an Interface to support multiple CC types.

Me too. Don’t know why you think I see it differently.

Didn’t think of the Window property. Of course this will work.

It was Rogers quote you highlighted and uncertainties about keydownBody() that I imagined the other scenario :slight_smile:

[quote=285747:@Roger Clary]You need to create an Event Definition for your class. I have a feeling from your description that you have done something else.
I’m somewhat confused as to what you are trying to do. In your original post I understood that “myProp” is a property of the CC. If that is the case, then creating the event definition will allow the CC to refer to its own property as I described.

If, on the other hand, you are trying to access a method which belongs to your myTextArea, then just call that method directly from any of the instances of myTextArea. If myProp is only used my the myTextArea controls, then make it a property of your subclass and dispense with all of the other stuff.
MyTextArea1.myProp[/quote]

What I have is a container with three TextAreas that operate identically. The user types into these, and what is typed may just be passed through the KeyDown handler or in some cases (user types tab or right-arrow, say) I need to highlight or replace some of the text already in the TextArea. So the KeyDown handler is about 100 lines long and calls a couple of other methods here and there. It also needs to store some state about what is going on in a particular TextArea, and consult some data common for all three.

This suggested to me a common method that would be called from the three KeyDown handlers. 100 lines is not a lot of code so the saving would not be immense, but having just the one copy is clearly better from a maintenance perspective. Also, it seemed obvious to make a subclass of TextArea that would hold the state info about each TextArea. This would allow the user to switch to another TextArea and then back again, with the expectation of being able to continue as if he had not switched away.

For development, I was just working with one TextArea, but as soon as everything worked to a satisfactory level, I tried to implement my subclass. The common data should be a property of the container so that there is only one copy, initialised once. The specific data, OTOH, would be in the subclass. That’s when I started getting into trouble and posted here.

For the moment I’ve given up on the subclass, and just put everything in the container. I’ll use LostFocus to tidy up when the user switches away from a TextArea. Looking at the use case, that may suffice.