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

Adding a Procedure to Multiple Objects

Status
Not open for further replies.

Steve Meyerson

Programmer
Sep 17, 2020
318
US
Is there a way I could add a procedure to multiple textboxes (24 in this case) programatically?

The textboxes are already created, but now need a Valid event.

I know I can easily add the Valid event's procedure individually for each textbox, but I'm looking for a programming shortcut. It's a lazy solution in this case, but there could be more objects in the future.

Thanks for any help.

Steve
 
Yes, not a bad solution, Steve. I would prefer to put the validation in the Save button (assuming the data is buffered) rather than in a button that calls another form, but you know your system better than I do.

In fact, I try to completely avoid using the Valid events of individual controls - mainly for user interface reasons.

My last word on the subject: Once you are completely on top of this problem, do yourself a favour and learn at least the very basics of classes and objects. I feel sure you won't regret it.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike said:
prefer to put the validation in the Save button

FYI I actually HAD a Save button, but removed it to eliminate a step for the user. Instead I combined the save stuff with the validation code (above) in the Click event of the button which calls the 2nd form ("Timer"). Same result, one less click.

Steve
 
Are you forwarding what the user (you) enter(s) in your form to the next one? The easiest way for that is to bind all controls to properties of an object you then pass on to the next form, so you don't need to save to a DBF the next form reads and you also don't need to share the datasession, and you don't need 24 parameters, just one.


Chriss
 
Chris said:
Are you forwarding what the user (you) enter(s) in your form to the next one?

Yes. Form 2 displays a countdown timer. Form 1 has 125 objects. They are parsed, sorted and saved to a single dbf which is all form 2 needs. It's all done in a single validation procedure (as above). Everything appears to be instantaneous.

I forgot to mention this app is single-user only. No server. Even so, I'm not sure how all this could be buffered, binded and put into a single parameter instead of a single dbf if multi-user. The dbf (data) in this case is saved to fill Form 1 on the next startup.

Steve

 
VFP9 offers CREATEEOBJECT("EMPTY") a truly empty object and ADDPROPERTY(object,"propertyname", value), with which you can create an object with 125 properties. You still only pass the object, one parameter, and have all 125 values. I think this even has no limit like 254 fields a record can have. Though you might store 125 records with more details than just the value of each control. I don't know.

But such "empty" objects also can be set up with SCATTER NAME <<objectname>> from data and saved to a DBF with GATHER FROM NAME <<objectname>>, which obviously needs a dbf still, with the 125 field names., but there are many other ways to store such objects, for example with XML libraries that "serialize" an object as XML and vice versa create the object from that XML.

I just wanted to share the possibility. Since a controlsource can not only be a field but also a variable or an object property, you can bind all controls and have all user input in the properties of one object.

Chriss
 
Thanks, Chris!

I had always used the AddProperty method. Hadn't realized the simple fact that ADDPROPERTY function allows the object to be specified. Changes my thinking.

Steve
 
Well, it only makes sense for those objects you create with CREATEOBJECT("EMPTY"). Becase they are literally empty, they don't have a class, an init or an AddProperty method. The addproperty function was added the the EMPTY object class. Without it you couldn't make sense of it.

The point is not that ADDPROPERTY exists as a function instead of a method, the point is that you can bind controls to sch an object, which can have more properties than a DBF can have fields, because I think there is no limit, and it's still just one object variable, which can be passed to everywhere else, even when that means a different datasession. Without writing anthing to harddisk.

Chriss
 
Code:
Without writing anthing to harddisk.

Hi Chris,

I'm a little confused. So if I need to save some of these properties from the (initially empty) object to the disk, perhaps for the next startup, can I access this object and its properties on that next startup without a dbf (or some other saved file)? How so?

Thanks.

Steve
 
No, if you need something for a next session of course you will need to save it to a file.

Since we talk about an object with X properties I think saving it as XML is fine, and there are libraries that take an object and turn it to XML, like for example as described in its basic usage:
Code:
oName = CreateObject("EMPTY")
ADDPROPERTY(oProperties,"Firstname","Steve")
ADDPROPERTY(oProperties,"Lastname","Meyerson")
Thisform.Addproperty("oName",oName)

* usage example of the object:
1. Binding to controls
txtFirstname.Controlsorce = "Thisform.oName.Firstname" 
txtLastname.ControlSource = "Thisform.oName.Lastname"

* user can input/modify data which ends p in Thisform.oName properties

* 2. saving the object to XML for a next session
cXml = nfXmlCreate( Thisform.oName ) && creates XML from a VFP empty-based object
StrToFile(cXML,"nameobject.xml")

* 3. loading the object from the XML file
* oName = nfXMLRead("nameobject.xml")

* 4. Passing all object properties by passing the object as single parameter
* Do Form xyz WITH Thisform.oName && passing the object instead of needing several parameters, however more properties the object has

Note, adding the oName object to the form as property is just the easiest way to have it scoped to the form lifetime you could also make oName a PUBLIC object variable, but its not good to make too many things PUBLIC, especially when they are not of interesst everywhere in the application,, but just in one or two forms. There's always the need to scope variables or more generally speaking data in just the scope it is needing, i.e. on the local level for a method or function, on the form property level for a form, etc.

The comment I made earlier about no need to have a DBF or any file was about the single session where you have a form to enter data and another you call from that form (which you seem to do) to continue with that data and process it. It would always be a solution to combine these two forms on one form with a pageframe that has form1 on tab1 and form2 on tab2, to share the form including its datasession, but with such an object you can pass it. And since it is NOT a dbf form2 can run in a different datasession and the passed in object still is there available. form2 also does not need to open up the same tables form1 used to save data. If you only need an input temporarily to execute it, then this way you don't need to store that object anywhere, you can pass it around.

I don't want to say this is better than DBFs and cold replace the usage of databases, but I see the main role of a database as persisting data you want to have persisted forever, not data that's just of temporary usage like input that is processed and then results in data you finally want to persist.

You can do the same with a DBF that has a firstname and lastname field. True, the only difference about this would you need to forward an ID or recno to form2 to let it know which name record it should take as input. There is nothing very hard about this, too, oth forms have a name.dbf in their dataenvironment and use it.

I have used a pattern of forms that where for setting up a complex filter for data and passed in that filter to the next form, which then queried a database. And I could have even implemented a save feature for the filter input, but that form by default always started empty, just like a google search, but with sepcific input fields, not just one textbox, you know what I mean? I had no interest in saveing these search input. Others put such filter input onto the same form where a button executes the query, that's also fine.

I was just giving that example, since you had a form with many input control and talked about validating all the input before you start a next form, and that sounded like you provide that validated input to that second form. Well, in the exact detail step to forward data by a parameter, a single object is the simplest, unless you have the input in dbfs the other form can also use and there is no need to pass anything. That's also viable, of course.

I just think such objects have their usage as alternative, too. Objects can also have object properties which have properties, so you can do hierarchical objects. Using multiple tables you can have that, too. There's nothing that's only possible with objects or only possible with tables. But let me highlight anytimme ou want any form to participate, passing an object is simple, setting up the necessary data environment to participate in the data that the forms share is taking a bit more than that. Especially in the case you don't need all that inputs permanently, but just for a session. There you have a slight advantage over the tables with such objects.

Chriss
 
Chris,

You're right in that form2 operates using the data entered by user in form1. I see NOW I could have created an object to do the whole thing (except the saving part).

Actually, all values in form1 are saved. Nothing in form2 is saved. I might then save the 125 properties by extracting their values into a dbf (or some other format).

Yes, the 2 forms could be tabs on a 2-page form. The long explanation is the font size of the time displayed (hh:mm:ss) is 127. Other info is somewhat smaller but the timer window is on a laptop and needs to be seen across a room. Screen space is an issue. Form2 size (h/w) is 664x877. It covers part of form1 which is still visible (purposely).

Steve
 
I still don't have an exact picture of how this looks, but

a) there's a timer that's displayed with a very large font size and takes enough area to be seen across a room
b) you still show form1 after form2 is started.

It still doesn't make it necessary to have these forms separated, but I guess that has other reasons.

I don't know if I get what you do now to pass over the input. In theory the best option with that mass of data shared is either working in the same form or at lest the same datasession, by normal means of sharing data with workareas. Sharing data by passing around an object (object variables are always references to the object itself and when passing an object you always pass it byy reference, so both partners, the caller and the called (or callee) actually have a reference to the same object. So it's like shainrg data, without the need to save it to a file. Unless you need tht for the next session.

Anyway, I think I brought over my points about how to use an object to share a lot of information instead of doing everything with DBFs only.

Chriss
 
Her's another aspect to close the ring once more and bring it back to validation of many controls:

If you bind these many controls to many properties of one object, the validation can be changed not address th value of all these controls, nor the fields of a dbf, but the properties of that one object. And there's a way to go through all properties of an object by creating an array of all property names with AMEBERS and then using this array of names in a loop and use GETPEM() to read out the properties. To apply the same check to them you would have applied in a valid event.

So also that has another alternative with an object that holds data.



Chriss
 
Chris said:
I still don't have an exact picture of how this looks, but

I'm pretty sure all you've said can apply to my app. Here's the options window (form1):

Timer_grvlpd.jpg


Form2 counts down each level (12 min. in this case) in the order listed.

It's a timer for a poker tournament. I wrote it for our weekly home game. The "Timer" button opens form2.

Steve

p.s. The numbers represent chip amounts, not actual dollars.
 
Chris said:
Well, that asks for a grid, not for repetetive controls.

Yes, I thought of that. Just thought this was a little "prettier" in my opinion. Either way works.

Steve
 
You can remove everthing from a grid, which visually displeases you. And since you can use Sparse, you can let the grid draw the controls you put into it the way yyou like them, for example without themes.

This is a grid:
agrid_zj8igy.png



Chriss
 
Steve,

I haven't been following your recent discussions with Chris in detail, so my apologies if what I am about to say has already been covered. But I have been thinking about your original question, and it occurs to me that there might be a simpler solution.

Let's start from the premise that you have 24 very similar textboxes (I assume these are the 24 boxes under the heading "Min" in your "Blind Levels" form) and also that these are all based on the native VFP textbox class, that is, you have not created a custom class for them. You now want to add the same code to one of the methods (the Valid, in this case) of all 24 boxes.

Here's what I perhaps should have suggested in the first place:

1. In the form designer, select the first of the textboxes. Add the relevant code to its Valid event. Test the code to your satisfaction.

2. Back in the form designer and again with the first textbox selected, select Save As Class from the File menu.

3. In the resulting dialogue, select "Selected controls"; enter a name for the class (for example, txtMin); specify a name for the class library (if you already have a suitable class library, navigate to it here, otherwise invent a name, for example, Controls).

4. Add the class library (the one that you created or specified above) to the Classes tab of your project. Within the project, drill down into the class library so that you can see the name of the txtMin class.

5. Now delete all 24 text boxes. (You did back up the form before you started?)

6. Drag the txtMin class from the class library in the project manager onto the form, in place of the first of the textboxes.

7. Repeat for the other 23 boxes.

You should now have 24 textboxes, all containing the same Valid code. They will be named something like txtMin1, txtMin2, etc., which you might want to alter. All their properties and method code will be identical, except for obvious things like their Left, Top, Width, etc.

If at any point you want to make a change to all 24 boxes (you might later decide that want to modify the Valid code, for example), open the class in the class designer and make the change there. The 24 controls on the form will immediately inherit that change.

I hope this makes sense. Of course, if you have already found another solution with Chris's help, that's fine - go for it. I also take Chris's point that a grid might have been a better interface in this case. However, I hope the above suggestion will give you something to think about, and also that it might be useful for other people in a similar situation.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi Mike & Chris,

Thanks Mike for your clear step-by-step procedure to create classes. Incidently, the Valid was intended for .Text1-.Text24 at the left, but it's the same idea.

Both you & Chris have given me an education from this and other posts on ways to replace some of my "brute force" and "work-around" programming. Although to be honest I believe what I've already done (inefficient as it was) has stood me well. I am changing my approach, however, which proves an old dog can be taught new tricks. I thank you both for the education and will definitely save these posts!

Steve
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top