Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Custom class in a custom class

DDPL

IS-IT--Management
Sep 6, 2022
36
GB
Hi all,

I have a custom shape class, it has added properties and methods. These are dynamically created in the application, I am now looking at adding some additional detail to the shape in the GUI. I want to add a couple of labels that will display information about the shape, so was looking at creating a custom class based on a container, dropping in the custom shape class and adding a couple of labels.

So I created a new custom class based on a container and then put the shape and labels on it. If I go to Class menu and select 'New Method' it creates a new method for the container, if I select the Shape object and do the same the custom method is still created on the Container, so guess I need to add the custom shape class to the container rater than add a standard shape object and work on it, if that makes sense.

The other option would be to dynamically create the independent label instances at the same time as the custom shapes, just thought it would be neater to have the elements tied together in a container.

Darren
 
The add method always adds to the custom class that you are working with not any class that was added. To add to the custom label class, you would need to open it and add the methods there. You can add a blank method to the label class and then open the container class and add the custom code to the label's method that you added.
 
I don't detect a question you have. But I can perhaps clarify one thing: If you write a class, no matter if part of a VCX library or in a PRG with DEFINE CLASS, methods you define in the class always are methods of the top level class, so when your base class is a container, and another base class is a shape and you add a shape based class to a container class and derive yet another class of that combination of classes, the top level class is the container based class and the derieved class will therefore add methods to that container class.

So, taken from the other end: If you want to extend the shape class, you can't do that in the branch of classes that inherit from the container, when going backwards up the inheritance chain to the root, you have to derive another class from the shape based class to add methods on that level. And then later instead of adding your first shape class, you have to add the derived shape class.

So visualized in a tree

Code:
container (native VFP class)
 |
 +-- mycontainer
     |
     +-- myshapecontainer (based on mycontainer with a myshape added)
     |
     |  => New methods are added to the myshapecontainer class,
     |     which mainly is a container class
     |
     +-- mymorespcializedshapecontainer (based on mycontainer,
         but this time with a morespecializedshape added)
      
        => New methods are added to the mymorespcializedshapecontainer class,
           which mainly is a container class, but you can use/call methods
           that were already added to the morespecializedshape class on top of
           methods you already added when designing the myshape class.
      
 shape (native VFP class)
 |
 +-- myshape (derived from native shape)
     |
     +--morespecializedshape (derived from myshape)
        => New methods are added to the morespecializedshape class,
           which mainly is a shape class

So, overall, when you want to add methods to a subobject, you have to do that in that objects class hierarchy and you will need to combine them later, not already when the inner object is based on a class that doesn't yet have the methods you want it to have. So you work longer in separate branches and combine them later.
 
Thanks or the replies.

Chriss, apologies for not being more specific with the question, which is;

I create a custom class based on a container, I can drop a shape object on it from the form controls toolbar but can not see how I drop my custom shape from a VCX onto it?

Darren
 
You might have got the idea from the viewcode representation of code you can often see posted here. For example code of a button command1 on a form in the viewcode is like this:

Code:
DEFINE CLASS form1 AS form
    ADD OBJECT command1 AS commandbutton

    PROCEDURE command1.Click
        thisform.Release()
    ENDPROC
ENDDEFINE
That's just overriding the empty click method of the command1 button as you also would do in the visual designer when you doubleclick on the command1 button and get into the editor, editing the click event, that already existed.

Out of curiosity, I just tried to change that to a method the native button doesn't have:

Code:
DEFINE CLASS form1 AS form
    ADD OBJECT command1 AS commandbutton

    PROCEDURE command1.MyClick
        thisform.Release()
    ENDPROC
ENDDEFINE

The VFP compiler doens't complain, it'll actually add the MyClick method as member of the command1 command button. This doesn't change the native commandbutton class, obviously, but it adds the method "MyClick" to the command1 object, actually. In detail AMEMBERS of the form still only reports command1 as a sub-object, there is no command1.MyClick method (with a strange name containing a point) in the methods of the form, so in a PRG you can on the fly add methods to subobjects that are treated as if the subobject class had this method defined.

That's possible as a PRG allows this form of writing method names of subobjects, or from the other perspective, viewcode merges down all sub-objects into the root object and sepcifies methods of the sub-objects (or sub-subobjects, etc.) with points in the name as we know them from fully qualified names of the objects in our normal code.

So when you generate PRG code, you can do that as you like, you can have code in methods not defined on the class level, so you can manifest code "late", iof you like. The visual designer wouldn't offer you a MyClcik method to edit, as a button doesn't have that method, but in a PRG you don't pick the method you write, you just write it directly into the overall single PRG code.
 
Thanks or the replies.

Chriss, apologies for not being more specific with the question, which is;

I create a custom class based on a container, I can drop a shape object on it from the form controls toolbar but can not see how I drop my custom shape from a VCX onto it?

Darren
If your main problem is placing your custom control inside a container using the IDE rather than code, it's just a matter of knowing where things are.

As Chriss said, there is a definite hierarchy and when there's a container you can place objects into it by doing two things, right click your container to edit the container itself, then you'll need to click the View Classes navigation to select Add... to select the VCX that has your custom classes.


1747669849348.png
From that point, just remember to use the right mouse to Edit your container, so that when you place your class it will be IN your container, not ON it.

1747669969567.png
 
but can not see how I drop my custom shape from a VCX onto it?
Well, from the vcx. Use a project manager, add the VCX into it, design a container (based on whatever) put the project manager side by side, in the classes tab expand the vcx library with whatever class you want to drag into your editor and drag it there.
 
Step 1: Modify your container class
1747670410152.png
Step 2, drag the shape class into it, just drag it into the container designer
1747670542744.png
What you can't do is drag the mycontainer class into the mycontainer designer, that'll lead to a message "Cannot add object, class definition is cyclical."
Well, just don't do such silly things, but otherwise you can drag classes into designers even from yet another project manager open in parallel. Just be cautious how you introduce cross references and dependencies into multiple project folders and shoot in your own foot if you then later want to reorganize and move project folders.

There's also the Toolbox in which you can organize not only your VCXes, also PRGs, code scraps, etc. and you can drag from there into an open designer, you can drag anything into something else that can become a child object within, otherwise you get informed what you're not able to combine with a drag&drop.

And to be clear how OOP works in VFP: You don't just add the myshape into the mycontainer in the state it is when you drag it. Any changes you make to the myshape class later, change the myshape1 object within the mycontainer class, too. The mycontainer class only has a record of the myshape1 object pointing to the myshape class with all its properties, methods, etc. and uses whatever that is or will become in the future. That aspect is not a flaw, that's how you want your OOP to be.

Just note one limitation: While you do have the class designer of the mycontainer class open with the myshape in it, you can't in parallel open the myshape class in another designer window and edit it, because the designer shows an instance of the myshape class in its designer window and as long as only one instance of a class is in memory, that class can't be edited. But close the mycontainer and open the myshape and you can modify it as you like and it will reflect in the mycontainer afterwards.

And one more thing to be clear about: Once you resize the myshape within the mycontainer in the mycontainer designer, that new size is displayed in bold in the property window and overrides the width/height of the myshape clas for this one object. It doesn't change the size in the myshape class, though. Now, when you edit the myshape class and resize it there, that won't change within the mycontainer class, you made the class and object independetn from each other. But when in the mycontainer designer, right click on the width/height properties in the property window and select "Reset to default", they will go to whatever is the current class width/height definition.

The same goes for code: You can write code into the myshape1 object within the mycontainer class designer, that doesn't get written back to the myshape class. And when you then edit code in the myshape class, that change doesn't run in the mycontainer as you overrode the code in the mycontainer. Again "reset to default" will remove code and let the class code run, also the DODEFAULT() written int mycontainer.myshape1.mymethod will run the myshape.mymethod class code at that point, so you can write base code in the class and extend it within another class, not only by further subclassing.
 
Last edited:
Thank you Chriss / Joe,

Think I have it working now, came across some of the limitations that Chriss mentioned while playing with it a little earlier.

Also get the hierarchy now, the first time I couldn't work out why there was no code inside the methods of the shape class, so was trying to paste the code in, that is when I came up against the issue of opening a parallel copies of the shape class, but now realise the the code is sitting in the parent code, not the instance of he class in the container !!

Spent a lot of time trying to work this out today, so again, thanks to you both.

Darren
 
I couldn't work out why there was no code inside the methods of the shape class
Yes, that's a pitfall of how they chose to not tempt developers into modifying the code of the class within one instance of the class within another class. And empty method/event editor doesn't mean the class code is not "inherited", it just means whatever is in the class is not overridden. You have the "View parent code" button to read the class code without needing to close and reopen and close and reopen designers of the two classes. It'll show all hierarchy levels of parent and grand parent and so on classes. But you can only read the code, not change it.

If you would like to interactively edit a class while already using it in another class, you need to rethink your working process, as good as it sounds, it makes sense to concentrate on what you work on and work on that solely and do integration and integration testing when every component you combine is ready for that, not too early. The way the designers work forces you into respecting that order of progressing instead of back and forth.
 
Last edited:
My final 2 cents...

The most confusing thing for some people is that when you create a class based on another class, people have a tendency to forget that when you create new code for an existing event, that code is used AFTER the original unless you call DODEFAULT() somewhere else in the child's event.

So, if you create a child with different code for an Event, and you want to control WHERE it runs, you should specify DODEFAULT where you prefer the old code to run.
 
Last edited:
PS... I should clarify.

I posted before thinking about what I said.

If you want the new class to run ONLY the new code, add NODEFAULT() to the child. Otherwise, you can place DODEFAULT someplace else, such as before or after the new method code.
 
More confusion ahead. NODEFAULT only suppresses inbuild native behavior. To suppress inherited userdefined code in parent classes (not native base classes) all you need is a comment in the method/event. And it's the NODEFAULT command, no NODEFAULT() function call. DODEFAULT() is a function, and the parameters are the mehtod/event parameters.
 
I've always loved and hated that part about Object Oriented Programming.

Years ago I made it a point to implicitly add DODEFAULT() either at the start or end of most of my custom classes, just to be sure they fire WHEN I want them to, and I use NODEFAULT() for the handful of times I need them to NOT fire.

For example, if a parent method sets a background color or icon based on the value of a field and I create a sub-class that can change the value itself, I would want the code that can change the value to run FIRST to ensure the value was changed BEFORE I allow the parent code that changes the color or icon.
 
I seem to be late to the party :)

I would like to add that I realized this idea a long time ago.

Simply running some code in the command window of the VFP IDE is a natural way to implement it. But running similar code briefly or frequently is annoying, so I wrote it as a tool, see attached.
 
Last edited:
I'm sorry, the attachment doesn't seem to be showing up.
It is a gif animation.
I hope someone can get more inspiration from this than to market this tool.
Its source code has been lost, maybe I'll rewrite it again if I have enough time.
 

Attachments

  • Tools_Refactoring6.gif.zip
    1.4 MB · Views: 6
Last edited:
and I use NODEFAULT() for the handful of times I need them to NOT fire.
Again, that's wrong.

1. NODEFAULT is a command, not a function.
2. Anything in a method/event prevents the inherited code to run, even just a comment. If you have any code, you need to call DODEFAULT() to also run the inherited (user defined) code. Code in a method or event does not prevent native base code/behaviour, though. That's sthe big difference towards the native base classes only.

=> NODEFAULT is only necessary and is necessary everytime you want to prevent native base class default behavior. Most iconically in the Keypress event.
You can only see the subtle difference about that, if you have at least two subclass levels from a native base class. And even if you have that, as any code in a class prevents inherited parent class code to run, you will mostly never see a difference in NODEFAULT vs no DODEFAULT() call and you'll see no use case for both of them, though there is.

I'll not go down the rabbit hole once more, there are enough threads about NODEFAULT and DODEFAULT() in the forum and this gets off topic.
 
Last edited:
I stand corrected. As I said, I only use NODEFAULT in rare circumstances, such as when I want to bypass the keypress event.

As you said, we have plenty of discussion here about DODEFAULT such as the last time we talked about it here: https://www.tek-tips.com/threads/setfocus-not-working.1832034/

For me, I always use custom classes for everything including simple textboxes where I have additional properties and methods that are inherited. I build upon those in child classes without giving up whatever I put in the parent. For me, when I don't want the parent code, I use an entirely new class, so virtually 100% of the children have a DODEFAULT() somewhere at either the top or bottom, so that the original code can still work as it was intended.
 
The bad thing about it is that you can't use NODEFAULT consistently in any case you want to prevent base behavior, for example the form Load and Init reacts by not running the default behavior of actually creating and initializing a form instance by RETURN .F. instea. NODEFAULT has no effect there.

You just have to know what to use where, it's something comparing to irregular verbs you just have to know or pronounciation which evades any consistent rules.

You might explain Init or Load have to work differently as it's a totally different thing to not create the object instance you deal with instead of not executing some methods or events defult behavior, indeed the object creation comes first and init is aftermath of that enable you to intialize what you need to once in the lifetime of the instance, so the usual nodefault would only do as much as no code in load or init would do and the return value based mechanism is a different offer to opt out of form instanciation after it already is halfways done and not just suppressing that events default which is nothing, actually.

On one hand it's just a weakness of VFPs OOP concept, on the other hand this makes it easier for OOP beginners that don't need to distinguish between a constructor and initialization and not to need to know about static or abstract or other such concepts of OOP.

I would say VFP is the ideal playground to learn about OOP, not the academic Smalltalk and it offers a mature OOP architecture, wheras the small brother MS Access only offers some hints on OOP concepts. VFP lacks some advanced features, but you can do very much anything you want already.
 
Last edited:

Part and Inventory Search

Sponsor

Back
Top