Hi all, I can’t wrap my head around this.
I am trying to make my own popupmenu.
It consists of a containercontrol which is the menu. Inside that are containercontrols which are the menu list items.
All the list items are created at runtime.
Now when the user clicks the list item, I would like it to call the parent containercontrol. Is there away to do that?
I am thinking I should use “addhandler”, but I am not sure.
Raise an event and have the parent implement the event
You can return values and everything
[quote=347896:@Norman Palardy]Raise an event and have the parent implement the event
You can return values and everything[/quote]
Can it still be done that way if you are embedding the child containers at runtime?
Where would you implement the event if you don’t have an instance of the child container control to add a handler to?
there are at least a few ways to skin this cat
the lazy one (but I’d argue the wrong way) is to use addhandler
the reason I’d say wrong is that the unconstrained use of addhandler can lead to hard to find memory leaks because a control doesn’t close and that keeps a layout in memory etc
IF you go this route make sure that you have a matching REMOVE handler when the control is to be removed from your layout
the second is to do a bit more work and make this more automagical
- define an interface (which might be one with a single method or a bunch of methods)
- have the layout that is the parent BE an implementor of that interface
- make you containers to be embedded have a constructor that takes a parameter that is one of these interfaces
- the constructor sets a private property on the container that is a weakref <- this is important
- when you want to call a method of the parent (exposed by this interface) you
- check if the weakref is nil and if so do not call the method
- check if the weakref value is nil and if so do not call the method
- get the weakref.value , cast it to the interface type, and call the method
while more work initially the upside is that to extend this to more exposed methods you update the interface, and implement the method on the parent and the call
no need to add more addhandlers and matching remove handlers
and because you use a weak ref you do not increase the ref count of the parent and thereby maybe cause a memory leak like you can with addhandler
dont get me wrong, used as intended, addhandler can be very useful
its the “oh just addhandler for …” everything that causes problems because it does increase the ref count of whatever holds the method and that can lead to hard to track down memory leaks
less code is not always “better” code
I’ll post a sample of what the second way looks like
edit : turns out this is the second one I’ve posted
[quote=347900:@Norman Palardy]3) make you containers to be embedded have a constructor that takes a parameter that is one of these interfaces
I’m on the verge of grasping this, but there is one part that I don’t quite understand; sorry Alexander, I don’t mean to take over your thread.
In testing this, I have a container named ChildContainerControl with a constructor:
Public Sub Constructor(classInterface as TestInterface)
mInterface = new WeakRef(classInterface)
mInterface is a property of the ChildContainerControl with type WeakRef
What is don’t quite understand is how to get an instance of the interface which the ParentContainerControl is now implementing (I’ve added “TestInterface” to the project and have had ParentContainerControl implement that interface).
In ParentContainerControl’s open event I have:
[code]Sub Open() Handles Open
dim ti as TestInterface
dim child as new ChildContainerControl(ti)
This issue with that code is that “ti” is nil.
How do I access the instance of the interface that the ParentContainerControl is implementing, so that I can pass it to the constructor of the ChildContainerControl?
Thank you for the detailed reply, that’s what makes these forums amazing!
ah … you dont … you already have one … SELF
Sub Open() Handles Open
dim child as new ChildContainerControl(self)
because the window implements the interface a test like
if self isa TestInterface then
msgbox "you are a testinterface IMPLEMENTOR"
works just fine
and a class can implement many interfaces
This will definitely be something that I start using in the future now that I understand how it’s useful and how to implement the code. It’s a much cleaner approach than the gymnastics that I’ve been doing.
It might be worth updating the documentation at http://developer.xojo.com/userguide/interfaces to include a slightly more detailed explanation of:
That “generic code” could use a bit of fleshing out.
like I’ve said before a lot of the things that addhandler allows you to do were always achievable before - they just took a different design
I doubt the IDE could have been written with the Xojo had these sorts of patterns not been possible before
Out of curiosity, for an example like the one that Alexander had posted, is there a benefit to taking the extra steps to implement an interface rather than just storing a WeakRef to the ParentContainerControl itself and calling its methods from that?
From reading the docs, it seems that the class interface only brings real benefit when multiple classes share the same methods, but with different implementation.
How does the container get the reference to the parent ? Classes / controls should not just reach outside themselves arbitrarily.
At least with the constructor its obvious and self contained.
Interfaces help you keep things clear and help you separate concerns.
Regardless of whether you have 1, 10 or hundreds of classes that implement them.
They are really useful when you do have lots of classes that implement them but I would not say thats the only time they are useful.
Could you eliminate the interface in this case and pass the parent container itself devoid of the interface and do much the same (casting to the parent container type instead of the interface) ? Sure.
An interface just helps you plan for the future in a case like this.
[quote=347981:@Norman Palardy]Could you eliminate the interface in this case and pass the parent container itself devoid of the interface and do much the same (casting to the parent container type instead of the interface) ? Sure.
An interface just helps you plan for the future in a case like this.[/quote]
Yeah, that’s the structure that I was referring to.
I’m all for best practices though. Thanks for taking the time to walk through the benefits and implementation.
Thank you very much Norman.
Also great questions from you Jared.
I learned a lot here.