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!

A way to have GetAll() by using SetAll() 1

Status
Not open for further replies.

Chris Miller

Programmer
Oct 28, 2020
4,914
DE
In thread184-1826223 (which by the way earns more credit to Gerrit Broekhuis) I indicated that the SetAll method could be used to implement a GetAll(), too.

Here's the principle by which that would work:

Using the SetAll() method you can only set all properties of a specific name and - if you want - of a specific class to a value. But combine that with an assign method of a property and you can use a little trcikery to have a GetAll() method.

First of all, the idea stems from the fact that Thisform.SetAll() is a way to reach all controls of a form. At firt glimpse you only reach one property of them all. But then let that property have an assign method and you indirectly call that assign method, which can do other things than assigning the passed in value to the property.

Here's a simple example of how that would work to implement a GetAll():
Code:
o = CreateObject("form1")

Define Class form1 as form
   ADD OBJECT text1 AS getalltextbox
   ADD OBJECT container1 AS getallcontainer
    
    Procedure Init()
       Activate Screen
       Clear

       ? 'all controls of form.Controls:'
       For Each loControl in thisform.Controls
          ? loControl.name
       EndFor 

       Local loAllControls as Collection
       loAllControls = CreateObject("Collection")
       Thisform.Setall("getall",loAllControls)
       ? 
       ? 'all controls by GetAll'
       For each loControl in loAllControls
          ? loControl.name
       EndFor
       
    EndProc
EndDefine

Define Class getalltextbox as TextBox
    getall = .null.
    
    Procedure getall_assign()
       Lparameters toCollection
       
       If Vartype(toCollection)='O'
          toCollection.Add(This)
       Endif
    Endproc
EndDefine 

Define Class getallcontainer as Container
    getall = .null.
    Add Object text2 as getalltextbox
    
    Procedure getall_assign()
       Lparameters toCollection
       
       If Vartype(toCollection)='O'
          toCollection.Add(This)
       Endif
    Endproc
EndDefine

Notice how thisform.Controls only returns the controls directly on the form, whereas Setall with the help of the assign method also gets the text2 textbox within container1.
And since GetAll is cascading into all objects on the form or within containers, pageframe etc. you get them all with one SetAll() call.

The only downside is, this needs the getall_assign method defined in all controls. Which you can see from my sample. I can't write a parent class of which both a container and a textbox (and a listbox, editbox, grid, etc) can inherit. So each base control needs this method implemented once. If you think such a parent would be possible by using the Control baseclass: In short no, that's just not possible.

If you'd impleent that in all base controls, it should be worth hte effort and be capable of more than just the getall functionality, you could pass in something that includes instructions (code) to process, so you could make the assign method mechanism a VFP message queue like the Windows messaging system and let the assign method react to different passed in parameters. Using a collection to gather the full list of controls then is just one example of what can be done with this mechanism.

Chriss
 
I see, you already had the same idea

myself said:
make the assign method mechanism a VFP message queue like the Windows messaging system

Your sending in a broadcast object, which can do different things also depending on what the controls broadcastmessage_assign method accepts. I think there are two categories of things a message is for. Either you just need to have an object reference and do something with it, that's doable for every control or almost every control, that would just need the assign method to call a standard method of the broadcast message object with THIS (the control itself) as parameter. Or you want the control to do something individual for the control and that's best programmed in each controls assign method or another control method with the code specific to that control.

To pick 2 examples you used yourself:
An example of the first kind is a style message, which should trigger to set some font properties (if they exist on the control), so the assign method calls oBroadcastMesssage.SetStyle(This) and the SetStyle method is programmed within the broadcast object you send in with Setall.
An example of the second type is a default text message, which instructs each control receiving it to set a default value to itself. And since that's very individual this is done by each control individually in its own SetDefaultText() method.

So, fine, those are fitting for a general purpose assign method like this:
Code:
LParameters toBroadcast

If Vartype(toBroadcast)<>"O" or toBroadcast.class<>'Broadcastmessage' && well, or one of the parent classes is that
   Return
Endif

If Pemstatus(toBroadcast,"Process",5)
   toBroadcast.Process(This)
Endif

If Pemstatus(This,toBroadcast.Controlmethod,5)
   Evaluate("This."+toBroadcast.Controlmethod+"(toBroadcast)")
Endif

The latter one make it a CallAll. And the Process method could be as simple as storing "This" into a collection, like m original getall_assign just did. If instead the control method is called, passing in the broadcast object means the control method can get parameters from its properties, for example. Everything else is individual to the broadcast message Process method or the control method you wish to trigger in all controls (if it exists). So all that isn't necessary to program into the assign method or an individual assign method. And so the assign method also doesn't need to know message types anymore and you don't need an ever growing DO CASE statement and you don't need individual assign methods of different controls.

My next idea is to just define a container that has this assign method and all controls will be based on that, with Basecontainer.Controls(1) always being the actual base control that this containrer will contain. Then you also only need this assign method once, not in every basecontrol. This mesage receiver is defined on that level once and for all controls, which embed themselves within. The container can also hold further default values to controls, like a default fontname and fontsize, and scatter them to the contained control.

Then you have all these defaults in the basecontainer class that governs everything else. It just fails on designing forms with such a construct. The usage of the designer then means fiddling with all these containers. You'll end up with everything contained in a container and that's perhaps the end of that idea unless I find something which means resizing the container automtically adjusts its inner control(s), like resizing/anchoring at designtime.

Chriss
 
Hi Chris,

Your way has one small problem. Did you try add protected object to collection?
(all inner objects in Control object are protected)





mJindrova
 
I know, that's why I said I'm NOT building based on Control, but based on a container. I just don't like that everything would be inside a container for the designer phase.

That inner structure of a class based on Control are protected is only a minor problem, the major problem is that once you have a class based on Control and subclass it, you can't add inner controls to it. The Control class is a final class.

Chriss
 
For the container, page, and form classes there is already a property (array) that contains a list of the objects (Objects property) that is populated by VFP. You could create a custom method GetAll() for each of these classes that will read this property and return what you want. It could be designed to return all controls or a list of controls for a particular base class.

Greg
 
Yes, Greg, but the charm is that SetAll simply gets into all control objects, no matter if they are part of the form.controls, the controls array of containers on the form, the pages array of pageframes or whatever else. And you don't need to program something getting all the objects, they are simply covered by one SetAll() call.

Indeed for compatibility with old VFP versions mJindrova already programmed a SetAll replacement doing what you suggest.

And the discussion already is on another subject of using the SetAll() for more than GetAll(), but for broadcasting "messages".

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top