Hello stanlyn,
you asked me to undelete my posts to add back to the discussion. I said I do so by reposting instead. And let me summarize everything I said and concentrate on what's not in the rest of the discussion since then and reinstate some code samples. So I skip some things I mentioned Mike already said and I skip some things I later posted again. And, for example, since I now know you're familiar with using SET STEP ON, I spare advice for novices in debugging.
Let me just repeat one piece of debugging advice, though: To see
everything happening, you write code that does the CREATEOBJECT('formmclass') and then step through all by starting there, at the line doing the CREATEOBJECT().
stanlyn said:
"I appreciate detailed answers, but they are not helping as I never use a "Define Class parentform as Form" to create a form.
Remark at this point: As my tests turned out a whether you have a PRG or visual form classes, the behaviour turns out the same. So it is not irrelevant. I still only post the code samples and screenshots from the VCX implementation.
These are the classes:
In the visualparentform I have code in the Load() event:
Code:
Public gcVar1
gcVar1 = 'test'
In the visualchildform based on the visualparentform I also have code in the Load() event:
Code:
On Error ? Message()
? gcVar1
Nothing more.
I still wrote a PRG on top of these classes that is used to start the debugging. It is
Code:
Release gcVar1
oForm = CreateObject("visualchildform")
Obviously I have SET CLASSLIB so this works. I start debugging in the CreateObject() line. The first thing the debugger shows, when I single step into the Createobject() call is:
What you see is that the visualchildform Load() happens first and since it doesn't call DoDefault() the parent form load does not run.
When ? gcVar1 is executed that leads the trace window to show the On Error code:
The error "Variable 'GCVAR1' is not found." is put on _screen.
I see the behaviour you expect. I think your issue here is that the variable still exists from a previous run. But make your own debugging experiments to see for yourself.
At this point, I thought the more relevant question could be, what do you actually want to achieve? If you want something visible throughout the form, then a form property would be better scoped for that as a public variable.
It would be worth another question, to talk about properties. If you want a child class not to inherit a property of the parent class, it's not possible to state that in the child class, the Property/Method editor window only allows you to change the visibility of the property you inherited, from public to hidden, for example. But that does not change it to be hidden from yourself. It's then only hidden for the next levels of classes inheriting from the child class. So you may want to define a parent class property that's only visible for the parent class itself, then set this to hidden in the class definition of the parent.
If you want a property to be inherited by some child classes and not others, there is no visibility for that in VFP. Hidden means no inheritance of the property, it's only visible on that class level, public means it becomes inherited. Protected is inherited too, but access to it is only possible from code within the class family, not from the outside. Those are the inheritance possibilities of properties. And you see there is public visibility of properties, but that differs from a public variable. Same keyword, but a totally different technical meaning. Actually same natural language meaning, but a variable is still something totally different in scope than a property, a variable isn't scoped to an object.
In my last post then I picked up that idea and implemented it that way. Pardon me for still posting this as a pure code example. The point here is not whether the forms are Declared in PRG code or visual classes. The point is to demonstrate how something you actually didn't need but I thought you could have meant could be done: A property that's not existing by default and is generated in a parent class if, but only if a child class wants it by calling DoDefault() or by having no code in the Load to prevent the property creation.
Code:
Clear
On Error ? Message()
? 'creating childform1...'
oChild1 = CreateObject('childform1')
? '-------------'
? 'creating childform2...'
oChild2 = CreateObject('childform2')
? '-------------'
? 'creating childform3...'
oChild3 = CreateObject('childform3')
Define Class parentform as Form
Procedure Load()
This.AddProperty('cProperty','test')
EndProc
EndDefine
Define Class childform1 as parentform
Procedure Load()
DoDefault()
? This.cProperty
EndProc
EndDefine
Define Class childform2 as parentform
Procedure Load()
* this will error
? This.cProperty
EndProc
Enddefine
Define Class childform3 as parentform
Procedure Init()
* this will not error
? This.cProperty
EndProc
EndDefine
It would be of no use, if you ask me. If a child class wants a property to exist, it could do the AddProperty() call itself. The only reason to "delegate" this task to the parent is to think of it as the responsibility of the parent class definition to provide properties necessary for the whole following tree branch of subclasses. And perhaps also to set it to some default or initial value, which could depend on the current state of the whole application and that's also the responsibility of the parent class, already.
But well, then it also is just the decision of the parent class and not of child classes, to define and to set properties. So it's really an irrelevant piece of code. You can perhaps learn from it, once more, that a method not doing the Dodefault as in the case of childform2 Load() does mean the parent method does not run. That leads to the error, as the parent Load didn't define a property the childform2 uses. Childform3 then demonstrates how you can use the property even though you did not Dodefault(). Well, because the Load() event is not overloaded and so runs. And Load() runs before Init(). Remember what I also said: An inheritance chain exists per method. So breaking one of the chains does not break all others. Childform3 breaks the Init() inheritance chain and doesn't DODEFAUL of parentform Init(). That could cause problems also if you now have no parentform.Init() code, if later the parent form has Init() code, Childform3 would still behave as if it didn't. That's often a reason you program something on a low hierarchy level and wonder why it doesn't affect some classes.
It's wise to put in a Dodefault() even if you know some method or event currently has no code. But it can also become a case of "however you do it, you do it wrong." as in some cases you might really want some branch of a family of classes to not inherit something you add later. And then, well, you change a base class and also change some subclass of it and remove a Dodefault() where it's harmful, or put it as the last line and not the first line of the subclass code. You can't foresee which order of execution is best.
I can say from my experience and gut feeling in most cases the behaviour you initially assumed, that parent code automatically runs before your class, that would be a disastrous case to not be able to decide that. I usually will do something, then call Dodefault() and then react to the parent class result of that. And you can rarely know in advance, where you want base class code to happen. I will often try to call DoDefault as soon as possible as it's also true to say that it's odd to delay what you inherited and do something yourself, first. On the other side you often declare a child class as you want it to have some different and more specific behaviour and so you often "know better" and may then even only conditionally sometimes call Dodefault(), if your class level is not the usual case of "knowing better" than you knew when programming the parent class. Well, and one extreme case of that I already said also is valid, that in some cases you know better about everything and then don't Dodefault() at all, not even conditionally. Why then not start a new base class? Well, remember inheritance is a thing per method and event.
Chriss