"Chicken flocks have a well-defined hierarchy called a pecking order. A chicken at the top of the pecking order gets to do the things she wants by 'pushing the others around' a little, giving them a short, sharp peck if they don't submit to her, allowing her to go where she wants to go or access food and drink first. Chickens naturally have a pecking order to ensure they live in harmony. When food is available there are no fights. Everyone lives in peace.
The bird at the top of the pecking order will have first access to water, food, the best roosting place and so on. The bird at the bottom of the pecking order has the least ‘rights' in the flock and will usually be the last to the food and will ‘skirt' around food that is scattered for them, nipping in to grab a beak full when possible. She (or sometimes he) has last rights to food and other resources." Check out the full story and video at http://poultrykeeper.com/chickens/frequently-asked-questions/what-is-the-pecking-order.html
Your application has a window containing custom visual user objects (CVUOs) that require system services in their constructor events. Obviously, the services must be resident and active before the CVUO constructor is invoked. Using globals (functions, or NVO references) is one approach, but you want to code your solution in a Service Based Object Oriented manner. Being an object-oriented aficionado, you don't want the service to be of global scope. You'd rather define the NVO as a Window service. You define the service inside a Custom Class User Object (NVO) that you declare on the window and instantiate in the Open event. The window defines a NVO instance variable and creates it in its Open event. The CCUO calls the NVO service via the window in its constructor event. Figure 1 shows the code objects in the system tree. Figure 2 illustrates the scenario.
Figure 1: Code objects
Figure 2: CVUO in a Window calling service in constructor
The problem is that the NVO is not created until after the CCUOs. At runtime, because CVUO control constructors are invoked before the service is instantiated, a Null Object Error will be thrown. Figure 3 shows the Window event sequence. Notice that number 3, the NVO constructor, follows number 1, the CCUO constructor. In other words the NVO is lower down in the pecking order. How can we move the NVO up to number 1? After all, the open event always occurs after all the controls are created? I'll show you three possible solutions. You pick the one that suits your taste best.
Figure 3: NVO lower down in the pecking order
Solution 1: Deferring CCUO Constructor Code
I term the first solution "Classic" because it relies on a technique that has been around since the earliest versions. The solution relies on Post vs. Trigger event invocation timing. Every PowerBuilder programmer has seen this technique used in the Window open event cascade. You add an event that you usually call ue_postconstructor to the CCUO, code a this.PostEvent( ) to it in the constructor, and move the timing-sensitive code from the constructor into the ue_postconstructor. As you can see in Figure 4 the problem is solved because the ue_PostConstructor is invoked after the NVO is resident.
Figure 4: Sequence when CCUO code move to post constructor
Now it looks like the NVO is #1 but in truth it's really not. It's there because we lost the ability to record the CCUO constructor (which is still #1) and we recorded the Window Open (which is really #2) after we created the logging service. So the service is really still #3. It just faked its way to the top of the list. The question still remains: how can we really move the NVO up to the #1 position? In the sections that follow, I'll show you two methods to escalate NVO instantiation.
Solution 2: Auto Instantiate the NVO
In this approach you mark the NVO to be AutoInstantiated on its property sheet. That means THE VARIABLE DECLARATION IS THE INSTANTIATION. Figure 5 shows the design time AutoInstantiate designator.
Figure 5: AutoInstantiate designator
In addition, you must remove the now illegal CREATE statement from the Open Event. Since instance variables are allocated before controls are instantiated, the NVO reference is moved up to #1. Figure 6 shows our NVO in the top position.
Figure 6: The NVO is #1
However, a word of caution is appropriate: autoinstantiated NVOs behave like structures. That is, assignment statements cause a copy of the NVO to be passed, not a pointer to the original object. The will be a problem if your NVO maintains internal state.
Solution 3: Letting the Painter Define the NVO
The window painter has a little-known view called the Non-Visual Object List. As you can see in Figure 7, in the default layout, it's located on a tab on the bottom of the same folder containing the Properties View.
Figure 7: Non-Visual Object List
It lists NonVisual User Objects that you place on the layout surface using the painter. You can both drag and drop a non-visual control from the System Tree onto the Layout or use a menu item to accomplish the deed. Figure 8 shows the Insert Object menu on the Window Painter.
Figure 8: Placing an NVO on the layout
When you place an NVO, the painter gives it a default name just like any visual control - class name prefix followed by a numeric suffix. You can use the property sheet to have the name follow your standard naming convention. Figure 9 shows the view after defining an NVO. Please note that you can only define non-autoinstantate NVOs in this manner.
Figure 9:Non-Visual Object List
The painter is a privileged character. It can write code in framework source code places that are not in the developer domain. In our case, the painter defines the NVO in the Type Declaration section and instantiates it in the CREATE event. That means that the NVO is instantiated earlier in the instantiation process. Figure 10 shows part of the painter-generated code.
Figure 10: Generated Source Code
In addition to rising to number 1 in the pecking order, the NVO garners two other bonus features from this approach; first, the NVO behaves like an NVO. Assignment statements assign the address of a single object in memory; state is retained. Second, like a visual control the NVO becomes an inner control defined within the window. As you can see in Figure 11, you can write additional event handler code for it in the window painter. Now that may prove useful some day!
Figure 11: NVO in the Window Script Editor
One mystery still remains unsolved. A careful read of the CREATE code shows the NVO created AFTER the CCVOs. By all rights and logic, NVO calls in CCUO constructors should fail. But they WORK!!? Obviously there is some sleight of hand going on inside the runtime.
PowerBuilder, like just about everything in life, offers a reasonable face value experience, but provides hints and tools that you can follow and use to rise above the pack. Armed with the desire for a better solution and a bit of fortitude to see it though, you'll find a way to write better code, thereby becoming #1 in the pecking order.