Some weeks ago I played around with a project to make a listbox of containers. I now want to add different containers to the listbox.
This is the code to add a container:
Public Sub AddItem(theContainer as ContainerControl)
theContainer.EmbedWithin(Self, 0, InsertOffset, Self.Width, theContainer.Height)
If theContainer IsA ItemContainerControl Then
Dim ICC As ItemContainerControl = ItemContainerControl(theContainer)
AddHandler ICC.RemoveMe, AddressOf ItemClickedRemove
End If
items.Append(theContainer)
theContainer.TabIndex = items.LastIndex
InsertOffset = InsertOffset + theContainer.Height
// resize container to fit new one
Self.Height = Max(Self.Height, InsertOffset)
End Sub
The reference to ItemContainerControl needs to be changed to an interface. I think that I need to use a delegate. But from examples and the docs I can’t work out what delegates are let alone use them.
I think it’s a fancy way of describing having a variable that contains a method address (which you can therefore alter at runtime), in order to have a set of methods and can decide at runtime which to call. I think.
So, in Pseudo-Xojo, one might imagine having:
Var myPtr as Method, methods(2) as method
methods(0) = AddressOf Method1
methods(1) = AddressOf Method2
// etc
myPtr = methods (i) // i caluculated according to some criterion, at runtime
Call myPtr (args, ...)
Delegates aren’t actually that hard to use once you understand them. The problem is, this use case is far better suited for an Event Definition / Handler, so it’s not a great example for me to describe delegates with.
The elevator pitch for how to use Delegates might be something like this:
The Delegate definition should be the signature of the eventual target function but the name doesn’t matter (parameters and return type do!). Store an address that handles the Delegate (has the same signature, name not important) as the value for a property or variable defined as MyDelegate.
Any time you want to call the function you defined the address of, check the Delegate property isn’t nil, and then use the Invoke function as if it were the function you intend to call.
In pseudo-code:
var oHandler as MyDelegate = WeakAddressOf SomeFunction
if oHandler <> nil then
var bReturn as Boolean = oHandler.Invoke(sSomeParameter)
end
Private Function SomeFunction(sParatmeter as String) as Boolean
So, to add a delegate to a Module, I’d go RightMouse → Add Delegate and there I can define the delegate’s parameters and return value. Its name too - using your example that would be MyDelegate.
This appears to define a type, essentially, which I can use to declare a variable in the usual way, and so to adapt my example of upthread, this becomes:
Var myPtr as MyDelegate, methods(2) as MyDelegate
methods(0) = AddressOf Method1
methods(1) = AddressOf Method2
// etc
myPtr = methods(i)
myPtr.Invoke (same parameters here)
Method1 and Method2 are just ordinary methods whose signatures must match that of the delegate declaration.
In fact I use this approach so that I can have a method for reading from a socket, another for reading from a textinputstream, and have the same code using either.
Thanks for the explanations. Still not sure why and where I need this. I can have a method from a socket and reading from an inputstream with the same name far easier with an interface and a factory.
I once wrote an app for testing a line of audio products. It had a whole bunch of test methods, each for one parameter of the product: frequency response, gain, distortion, power consumption, etc. The particular tests to be performed and the order in which they were to be performed could vary depending on the product and wasn’t 100% known at design time, so flexibility was required.
The test sequence was controlled by text (XML) files, which could be loaded at runtime. Each “test spec” file contained among other things a list of the names of the tests to be performed and their order of performance. The app would iterate over this list, looking up and invoking delegates pointing to the test methods from a dictionary whose keys were the test names.
This decoupled the test procedures from the code and made it easy to have different procedures for different products or to make changes without having to recompile and re-deploy the app.