There is a way to detect “code based” constructor call from a “layout based” constructor call inside the constructor call?
Sure that it sounds like a tongue-twister, but imagine that you have a custom canvas class called MyCanvas.
You can insert the object into a window layout or you can create the object from code (like dim codeInitializedCanvas as new MyCanvas() ; codeInitializedCanvas = new MyCanvas())
In both way the Constructor is called first, so the question is: how to detect the two different situations inside the constructor itself?
As a walk-around I’ve fount that you can create an additional constructor that take at least one parameter and use it from code (since layout always call the Constructor with no parameter).
I’m developing some custom UI stuff and I’m trying to optimize objects initialization.
The key point is that I want to create object that could be efficiently used in both way (code or layout).
Imagine that MyCanvas has a computed property called style (default = 0).
Into layout mode there is a menu into the inspector that let the end user to select the available styles (0,1,2…).
The computed property call a (think expensive) method, thus ideally it should be called once and only for the final desired style value.
From code there is no optimization problem because if I call “new MyCanvas()” or an overloaded constructor “new MyCanvas(3)” there is always single call for set style inside each constructor.
The worst case is when the object is inside the layout and the user also use the open event to set the style (is a common practice because inspector values could be lost if you change class, copy and paste to projects that lacks the needed class etc)…
Into the “layout based” situation (the most used indeed) I got 3 call to the “expensive” method.
Example: default style value = 0, inspector style value=2, open event style value = 3 (desired value)
Run: layout based constructor call set style=0 -> then before the open event the inspector properties are set (using inspector behavior order) so there is a call to style=2 -> last into the open event there is the final and correct call to style=3
Suggestion: Move your setup code into a Timer with a very short period instead and initiate the Timer from the computed property. The user could set the property a million times in a row and the code would only run once.The problem of how the control was created becomes moot.
Adding a timer for every object to improve efficiency is not so efficient and nor elegant.
Since real objects are UI widget of many types, there are may of them inside a typical app window.
Also I’m not sure that your solutions is able to guarantee that the style value is set to the desired one (the final desired call could be skipped while waiting for previous timer to complete).
I’m picturing that the results of this expensive method aren’t used/needed until sometime after the Open event. Like it’s drawing and composing pictures to be used in the Paint event. What you could do is add a property to track what the last used style is. Then whenever you need to use the style is when you run the expensive method, but only if style and oldStyle are different. I hope this code explains better…
[code]Class MyCanvas Inherits Canvas
Private mStyle As Integer //internal style property
Property style As Integer //computed property to mStyle
mStyle = value
Private oldStyle As Integer = -1 //track last style generated (initially a non-valid value)
Private bgPic As Picture //something expensive to make
Sub Paint(g As Graphics, areas() As REALbasic.Rect)
g.DrawPicture(bgPic, 0, 0)
Private Sub updateStyle()
if mStyle <> oldStyle then //need to load the style
oldStyle = mStyle //store as currently loaded
expensiveApplyStyle //run expensive code
Private Sub expensiveApplyStyle()
bgPic = new Picture(Width, Height)
bgPic.Graphics.ForeColor = &c00FF00
Select Case mStyle
bgPic.Graphics.FillRect(0, 0, Width, Height)
bgPic.Graphics.FillRoundRect(0, 0, Width, Height, 30, 30)
bgPic.Graphics.FillOval(0, 0, Width, Height)
System.DebugLog("run expensive for style: " + Str(mStyle))
Good idea Will, but I see a couple of potential problems here:
the “expensive” method also set various geometrical parameters that must be in place before the object own paint event (are used by other objects for layout purposes)
the object own paint event do not occurs at all if the object is initialized only from code (and is I set the call into the constructor the method will be called multiple times in “layout based mode”).
I’ve clicked “answered” but is a mistake… could be undone?
Since Timer code always runs on the main thread, under what circumstances do you envision that a call might be “skipped”? In fact, only the final state for each control will be used, which is what you want, and while calls to a scheduled Timer may be delayed based on other code, they are never skipped.
What you’d have is control where the expensive setup happens once per control no matter how many times the governing property is changed during instantiation. I find this both elegant and efficient, but perhaps we mean different things here.
I’ve used this technique with great effect to make sure that certain setup happens after all the properties of a control or window have been properly initialized, and this is typically the way I handle “expensive” code or code that may be redundant. (For example updating the Enabled state of my controls based on events.) It’s how how I’d do it in your case, but then, I’m not looking at your exact code.
Another idea: Set a flag from the computed property. During the Paint event, check the flag and, if true, perform your setup. Since Paint should only happen after instantiation, that should achieve the same effect.
Yes, it seems another form of lazy initialization.
As I said, I want to create objects that could be efficiently used in both way (code or layout), thus I can’t rely on paint event because the object could be initialized also only from code. Also point 1) of the Will response still remain a problem.
OK, how about this. It needs to happen after constructor -> inspector -> open. So mirror the Open event and flip a switch after the event is over then set the value for this initial time. The setter only does the expensive method if the switch is flipped.
[code]Class MyCanvas Inherits Canvas
Private hasOpened As boolean = false
Private mStyle As Integer
Property style As Integer
mStyle = value
if hasOpened then
Event Open() //New Event Definition
RaiseEvent Open //finish with initializing style
hasOpened = true //mark ready to apply style
style = mStyle //trigger it now with last set style
I had the same idea but can’t find anything that’s actually different in the constructor. Parent is nil and Width is 0. It’s not until Open that they have distinguishing values.
(Aside: even when creating in code the self.Window property is set to the current window where the code resides. self.Window can be made nil by putting the code “dim c As new MyCanvas” in a Module and calling it with a Timer.)
Maybe there is no way to distinguish this in the Constructor. Then the challenge is what’s the easiest way to choose the special ‘in code’ constructor.
Instead of using a dummy parameter how about a simple subclass, MyCanvasForCode’ that just overrides the constructor. For UI always use MyCanvas but in code always use ‘new MyCanvasForCode’.
Or always use a factory method when making in code. The syntax will be clumsier though
[code]dim c As MyCanvas = MyCanvas.newInstance
Class MyCanvas Inherits Canvas
Shared Function newInstance() As MyCanvas
dim c As new MyCanvas
All of this creating a RectControl in code is quite unusual. Might even be a bug that it’s working (?).
Another strategy is to divide the functionality of MyCanvas. Create a subclass of Object with all the stuff you need for your in code created instances. Then refactor MyCanvas to use that object.
The last pattern suggested by @Will Shank is the best solution
If you want to stay in a single class solution you could use this:
create an open event and call it your standard open event (so you can do more setup in the IDE)
then call your Init method (you have the all the info info your class is on UI)
so you class open event will contains:
Before use (paint for example, or whatever use you need) your class really, check if it has been inited, if not it’s a direct call and you can call the init method (or even an alternative one)
I’m a little confused. Are you talking about creating an instance of a control that you never intend to display?[/quote]
This is because I can also display the control not using its Paint event.
All UI widgets are custom classes that inherit (directly or not) from a Canvas and there is a base method called draw(g as graphics) that handle the appearance.
For standard layout use (object dragged into a window), the draw is obviously called from paint event.
In addition all objects are able to draw itself inside and arbitrary graphics context with a method called drawIntoGraphics(g as Graphics) that call the base draw.
With this design I can also create the object (think to a button) only from code and display it inside another canvas or portion of canvas or inside a picture: imagine that button inside a cellView inside a tableView, the latter is the only “real” canvas into the layout while the first two are code initialized canvases.
This is only to clarify the need of dual usage (layout and code), is obvious that the proposed suggestions can be applied into the draw method.