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 Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Class object as array

Status
Not open for further replies.

D-Ward

IS-IT--Management
Sep 6, 2022
32
GB
Hi,

I have a custom class called shpNode, what I want to be able to do is create a number of objects based on this class into an array.

I can create the objects doing it this way;

WITH thisform.Container1
FOR _x = 1 to 10
cName = "shpNode" + ALLTRIM(STR(_x))
.NewObject(cName, "shpNode", "mClass")
cComm = "." + cName + ".Top = 50 + (_x * 10)"
&cComm
cComm = "." + cName + ".Left = 50 + (_x * 10)"
&cComm
cComm = "." + cName + ".Visible = .T."
&cComm
ENDFOR
ENDWITH

This all works fine, but ideally would like to reference the objects as array elements, have tried things like;

ADD OBJECT cName(_x) as shpNode
cName(_x) = .NewObject(cName, "shpNode", "mClass")

Am sure I am close, but can't figure it out and although been using VFP on and off for 20 years have never really used classes before!!!

Thanks

Darren

 
Hello Darren and welcome to the forum.

I think the following should put you on the right lines:

Code:
DIMENSION laNodes(10)
FOR _x = 1 TO 10
  laNodes(_x) = CREATEOBJECT("shpNode")
  laNodes(_x).Top = 50 + (_x * 10)
  * set other properties in the same way
ENDFOR

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi Mike,

Thanks for the welcome, have been using the VFP forum on here for years under the username 'DarrenWard', VFP wasn't my native programming language but use it to write a large accounts and ERP application for the company I work for after attending a DevUK event back in the early 2000's hosted by Marcia Akins and Andy Kramek.

Have seen and admired your posts and commitment to the VFP community over the years. So not technically a newbie, but my questions will lead you to question that :)

So, to prove that point, had already dimensioned the array and if do the above, the code runs but the objects do not appear on the form, I assume I have to make reference to a parent class somewhere, in this instance thisform.container1 as I want the class objects (which are modified Shape objects) to appear within a container object places on a form using the form designer.

Also tried things like;

cName = "shpNode" + ALLTRIM(STR(_x))
laNodes(_x) = thisform.Container1.NewObject("cName", "shpNodes", "mClass")

although am not happy with the "cName" clause but it executes but just populates the array with .T. and is not referenceable as an object.

Need to do a little more reading up on ADD OBJECT and .NewObject I think.

You don't fancy coming out of retirement :)

Darren



 
D--Ward said:
code runs but the objects do not appear on the form

That's the key. Form elements must be names in the tree starting at the root thisform and every further name can have numbers, but can't be an array.
Even when you use native things that have a count, like an option group, the option buttons it creates are named option1, option2, option3 and not available as array option(n).

So all you can do is have a double way of referencing the object, storing them into an array aside from adding objects to the form.

One more interesting way to address objects in a container or form (or anything that can host controls) is using the Objects property. You can do a FOR EACH loControl IN thisform.container1.Objects, for example, and then don't need an array or index to go through a series of elements, especially if all objects in this container will be your shape class. If there's a mix of controls, you can of course check the loControl.class to be your class and skip over controls that are not your class.

Chriss
 
I hadn't taken in that these objects were members of the form.

Basically, you have two approaches. You can stick with my original suggestion, except that the array would be an array property of the form. You create array properties in the same way as other properties, but you include a subscript in the name. In other words, in the form designer, go to forms / New Property, and then enter the names as something line [tt]aNodes(10)[/tt].

Then, whenever you reference the array, you precede it with THISFORM. So the loop becomes something like:

Code:
FOR _x = 1 TO 10
  [highlight #FCE94F]THISfORM.[/highlight]aNodes(_x) = CREATEOBJECT("shpNode")
  [highlight #FCE94F]THISFORM.[/highlight]aNodes(_x).Top = 50 + (_x * 10)
  * set other properties in the same way
ENDFOR

The point about that approach is that the objects are not really members of the form. They simply a bunch of objects that you happen to store in the form. In most cases, that's fine. But there is another approach.

To understand that, keep in mind that the form (like all container objects) already has a built in array that holds the individual controls on the form. It is called "controls", and you can address it by reference to [tt]THISFORM.Controls(_x)[/tt] (where _x is a subscript).

This code will add the ten nodes directly to your form:

Code:
WITH thisform
  FOR _x = 1 TO 10
     lcName = "shpNode" + TRANSFORM(_x)
     .AddObject(lcName, "shpNode")
     loObj = EVALUATE("thisform.shpNode" + TRANSFORM(_x))
     loObj.top = 50 + (_x * 10)
     loObj.Left = 50 + (_x * 10)
    loObj.Visible = .t.
    ENDFOR 
ENDWITH

If you then want to loop through the nodes in some way, you can do so using FOR EACH:

Code:
FOR EACH loNode IN THISFORM.Controls
  IF LEFT(loNode.Name, 7) = "shpNode"
    loNode.ForeColor = RGB(128, 128, 128)  && for example
   ENDIF
ENDFOR

You need the test for loNode.Name because the Controls array will also contain all the other controls on the form, so you will need to make sure your code disregards these.

I hope this makes sense and that I haven't made it more confusing than it needs to be.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I should add that, with my second approach (using AddObject), you can also address an object individually rather than as a member of an array. For example, if you want to do something special with the seventh shape, you could simply reference it as [tt]THISFORM.Shape7[/tt].

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi Chris,

Thanks for the reply, think that is where I am/was getting confused. I understand the concept of root and elemental hierarchies, but was hoping there was a way of creating an array of classes and 'binding' them to a parent object/class.

I can do what I need to do with macro substitution, so applying the "thisform.ParentObject.MyClass" + str(_x) + ".property = " + _nValue and then running the macro, just looks messy to me in code so was hoping to use an array as looks more elegant and is how I have handled these issues with similar project types I have coded in the past.

The main application I have has hundreds of forms so use the FOR EACH loControl approach for loading things like form themes etc. for different users, so that didn't occur to use a similar approach for this, but seems like something worth exploring.

I may be back a bit, as am looking to achieve a lot that is new to me in VFP as the C# full stack developers I have working for me tell me it can't be done in VFP, and I do like a challenge. Am already ahead of them in terms of the backend data management and starting to assemble elements of the GUI. One of the firmware guys (we are creating our own hardware for this project) asked me this afternoon after seeing my progress if he could still get hold and VFP to take a look at it :)

Darren
p.s. sorry to drone on and get off topic, am not low level VFP technical but know how powerful it can be so get 'passionate' when people dismiss it without understand how versatile it is, and what a travesty it was that it dropped so long ago.
 
Instead of iterating controls via Objects, another approach to reach all controls is via SetAll(). You can create a cascading "event", by adding a property you set via SetAll in all instances of a control of a specific class and then have a property assign method that is triggered by setting that property. It wouldn't even matter what value you set if you're only interested in catching all controls with this mechanism and let each of them act for itself.

It's a bit like the windows messages system redone in VFP style.

As an example, that's usually done in the init phase of controls/forms, think of translating any label caption, tool tip text, etc. to the currently selected language of an international application.
Let each control have a property translate with translate_assign method, then do thisform.SetAll("translate",.t.) and in the translate_assign method you lookup texts for label captions etc. and set them. Each control in itself best knows which labels of it need which translation, think of compound controls that are a bit more complex, for example a calendar month with many labels for weekdays.

One advantage of doing it that way instead of at init is you can switch language at any time and cascade the change to all currently open forms, each only needing one SetAll call.

I also think that's how Steven Blacks INTL toolkit does it.

Chriss
 
I just want to add one more point. If this were my code, and I wanted to loop through and find all of these, I wouldn't rely on their names. Instead, I'd look for objects of this class:

Code:
FOR EACH loNode IN THISFORM.Controls
  IF UPPER(loNode.Class) = "SHPNODE"
    loNode.ForeColor = RGB(128, 128, 128)  && for example
   ENDIF
ENDFOR

That, of course, assumes you want to hit all items of this class and not just a subset that have a particular name scheme. Also, Chriss is right that SetAll is better for simple tasks.

Tamar
 
Thanks all. I have used most of the above in one shape or form, think I just got fixated on the one path, but can see there are better ways of doing this.

Putting some context on what I am doing, I have a drawing of a building that I can pan/zoom within a container, during a physical commissioning process I am adding 'hardware' to the drawing represented by shapes. These shapes have different properties so will be different sizes, colours etc. As I pan/zoom the drawing the shapes need to resize and re-position, I never know how many elements there are going to be so they need to be dynamically added/deleted and changed, both individually and collectively.

I did this many years ago in VB 6.0 and way before that in a DOS CAD application I wrote, but that was all done with pure graphical elements, so the whole schema had to be re-drawn each time something changed, looked at the shape object this time around to take advantage if it's properties and methods rather than having to cycle through data/coordinates to see if mouse event etc. interact with pseudo objects.

I may revert back to old code I have elsewhere and convert it from VB to VFP as a lot of similarities in context especially with the graphical engine.
 
Good to hear that this has been useful to you, Darren. It sounds like a perfect reason to use OOP. Tamar did a conference session a few years ago that might be relevant to your application. See Getting Your Head Around Business Objects

VFP wasn't my native programming language but use it to write a large accounts and ERP application for the company I work for after attending a DevUK event back in the early 2000's hosted by Marcia Akins and Andy Kramek.

I might have met you at that event. My memory is fading, but I do recall attending (and speaking at) some conferences at the Microsoft campus in Reading, which Andy and Marcia attended. Or it might have been somewhere else.

Thank you for your kind words, by the way.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I think that's quite simple. All shapes would need to react to a change of zoom factor by computing their default top,left,width, and height with the zoom factor. Panning becomes a step after that.
If shapes are child objects off a container, they are also clipped by it. so you can have negative coordinates by panning or coordinates becoming larger than container size by zooming or panning.

That's doable with the SetAll approach.

Chriss
 
What you're doing reminds me of a project I worked on (that's referenced in the paper Mike linked). We were dealing with multiplexers and adding circuit boards to them and needed to show the whole thing graphically. The article talks only about the business object side of the thing, though it has some images of what I built. (Check out especially figures 2 and 5 of that article.)

The article doesn't talk about how I did the graphical stuff. I built a bunch of classes to represent the objects of interest and I used BindEvents() and Access and Assign methods to make containers filled with other containers and shapes and labels act like single objects. The paper does talk about how to link your business objects with your graphical objects.

Tamar
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top