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 SkipVought 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
317
1
18
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
 
Well, the proper solution is to program a textbox class that has the valid event code and all txtboxes then have that. You can't do that easily now as aftermath.

One thing before I come to what I wrote initially: Is it really necessary to do validations in all textboxes? You could let each textbox pass the validation by programming nothing in them and then do an overall validation in one form method or button.click. That's surely the easiest solution.


That's much shorter not only in the description of the solution against what I wrote first about a kind of emergency solution available: BINDEVENT(). I could now end by just recommending Tamar Granor's article "Put Event Binding to Work". Part 1 is here:
But you see, that means BINDEVENT has a higher introduction hurdle, there's a whole two part article about it to make it usable for developers, just reading the BINDEVENT help topic won't bring you far. If you take this learning curve, you learn a very useful tool, though. Even though that's also debatable, as we could do things without BINDEVENT in all versions before VFP8, too.

On top of Tamar's article, let me address your specific case a bit more: BINDEVENT first needs a delegate object, normally a class you create for that usage. In your case it could be the form for sake of not needing to create a class for it - simply from the pragmatic perspective: If you would create a new class just for the BINDEVENT use, you could also have it much simpler by creating a textbox class that won't need the BINDEVENT mechanism at all. We're looking for something that saves work, not makes more work.

You still have a bigger hurdle on top of that need for the delegate object and its delegate method. When that method of the delegate object runs instead of the textbox.valid, the keyword "THIS" loses its usual meaning. The problem is whenever you write THIS in your valid code, for example to read THIS.VALUE, THIS does not mean the textbox that actually has the valid event. You are lucky that you can use THISFORM.CURRENTCONTROL instead - with a gotcha, if you talk about textboxes in grids, though, but let's not speak of the devil. There's also a mechanism to know which original object had the original event, by using the function AEVENTS(), but trust me, in your case you'd like to take the shortcut and use THISFORM.CURRENTCONTROL.

So in short you need to copy your valid code to a user defined form method that's your previous valid code amended with THISFORM.CURRENTCONTROL instead of THIS. And then bind all your textboxes to that. It would be easy as init of a textbox class, but then again a textbox class would mean you don't need this mechanism at all. So the usual idea is to do all the BINDEVENT calls in one central place, for example in form INIT. There you could run through THISFORM.OBJECTS and see which of them are the texboxes you need to bind to or generate their names from a counter, like textboxes Text1-Text30 could be addressed with a counter from 1 to 30 and macro substitution.

All that said I didn't even explain what BINDEVENT needs as parameters, but you'll see or have seen that in Tamar's article.

Chriss
 
Steve, this is yet another reason to use classes and objects. If all the textboxes are based on the same class, you could simply add the required code to the Valid of the class, and all the individual textboxes would inherit that code.

But, given that you need to ask this question, I'm assuming you didn't do that.

It is possible to re-define each of the texboxess as belonging to a class. You would first have to create a textbox class with the required code in its Valid. You could then use the Class Browser to base each of your existing textboxes on that new class. The problem is that you would have to do that individually for each textbox, so you are back where you started.

Sorry I can't come up with anything better just now.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Steve, If I remember correctly form some of your previous posts, you are not yet comfortable working with objects and classes (my apologies if I am wrong about that).

But if that is the case, you could perhaps resort to a brute force method.

Are all 24 textboxes on the same form? If so, consider using the macro recorder to paste the required code into each of the relevant events:

1. First, write the code for the Valid - it doesn't matter where you put it. Then copy it to the clipboard.

2. Open the form in the Form Designer. Click on the first textbox.

3. Start the macro recorder. Record a macro which records the keystrokes (not mouse clicks) for the following actions:

- Open the code window (ALT+V then C)
- Navigate to the Procedure drop-down (ALT+R).
- Navigate to the Valid event (V then ENTER) (this assumes that the textbox has no other events or methods that begin with V).
- Paste the contents of the clipboard.
- Close the edit window (ESC)

4. Stop recording.

Now click on each of the texboxes in turn. Press the key that activates the macro (which you would have specified when you started recording).

When you've done them all, save and close the form.

If the texboxes are spread over multiple forms, the same method could be used, but you would have to manually open each form in the designer.

There just remains the question of any future textboxes you create. One way to handle that would be to copy and paste an existing textbox (one with the required Valid code) when you need a new one.

The above might sound a bit fiddly. Perhaps someone will come up with a better suggestion. Otherwise you could give it a try.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I've just remembered that Rick Schummer has an excellent tool called HackCX. Amongst many other things, it provides an easy way to assign an object to a different class. You would still have to create a new textbox class containing the required Valid code. But that's a one-off. Once you have done that, you can use HackCX to re-base all your textboxes on that class.

I used the product many years ago when I took over someone else's project and needed to reorganise the class hierarchy. It saved me loads of work.

HackCX is not free, but it is not all that expensive considering the time it will save. See for details.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
To get back to the initial idea of centralizing all your validation into one validation method:

Adding a user defined method to a form would also be the way to go for that, as a form has no valid event. If your form has a save button you could let it depend on what thisform.validatedata() returns, if you name your new method that way:

Code:
*save button click code
If thisform.validatedata()
   * previous save button click code
Else
   * maybe nothing to do here, perhaps set focus to the first invalid textbox you found
Endif

The forms validatedata method then should return .T. if all textbox values are good and .F. if not, you can adapt this to your needs, too, for example return 0 in case all is okay and the number of the textbox that has a validation error, if not.

As Mike already said the usual solution is to use classes and object oriented programming methods, this also can be used to once more recommend buffering data, which would mean any changes that go into data - because no textbox validates its input - would first only enter the buffer and can be reverted at this stage instead of needing to undo a change of data the textboxes already saved.

It's no secret how often we recommend OOP, buffering and other things - they make life much easier than needing to handle problems as aftermath. Rather take part of your time with planning ahead than needing to piece together broken situations.

A class, for example, is not only justified by how many times it is used, but to have ways of extending it, replacing it by just instanciating an alternative class, etc. Really look into what you're missing instead of avoiding it as if it was a fatal desease.

Chriss
 
Mike & Chris,

Thank you guys for all your help and valuable information. I will reread your posts (a few times) & Tamar's to digest & learn your suggestions. [bigglasses]

You both correctly noted my lack of creating classes. Your approaches are smarter. My (poor?) excuse is I'm a 1-man shop, created apps for small, but widely diverse businesses. Objects rarely had the same set of properties & events from one app to another.

BTW within the single app I mentioned I used just 5 copy/paste operations to create Text1-Text24. I understand had I used a class, it would be no problem to add a single Valid procedure covering all 24.

I can only say I've gotten away with it or worked around it so far. I'm retired but still supporting some clients. But I still want to learn this for some non-commercial (i.e. non-profit) apps I'm writing (e.g. poker, stocks).

Steve
 
Well, if you have the time, it surely is good invested in learning all of it, bindevents and classes.

In the meantime I can report after tests, that whatever you return (.f., .t. or a numeric value) from the delegate method that is triggered by a valid event using BINDEVENT, your return value does not determine the behavior of the textbox, so you can't centralize the valid evemnt that way.

The proposed solution to use a centralized validation method which you actively call from a save button - alternativelyy from the QueryUnload event of the form, if you want to handle the validation when the user closes the form - that solution will still work and is the simplest way to not need valid code in all textboxes at all. Your central validation code will just need to iterate over all the 24 textboxes that need the same validation.

Chriss
 
Chris said:
..handle the validation when the user closes the form

Yes, that will work in this particular circumstance (I think Mike suggested that too). The user clicks a button to open a second form. At that time the Valid procedure can send the user back to correct the culprit(s). Might even check some other stuff in that Valid proc.

Thanks.

Steve
 
I would open the form .scx file (use myform.scx) get the text of the method you want to add into a memory variable (m.string) and then do this:

Code:
replace methods with methods+m.string for class = "textbox"

Your method would need dressing with crlf code and might look like this for gotfocus()

Code:
PROCEDURE GOTFOCUS
	THIS.BACKCOLOR = RGB(255, 255, 0)
ENDPROC

By the way, you could easily change the underlying class the same way.

Code:
Replace Class with "MyTextBox" for class = "textbox"

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
That's something I'd call the poor man's hackCX. Yes, as a form is just a table, you can make such changes on the level of the data it is. I didnt recommend this, as it's operating an open heart. Do it on a copy of the SCX/SCT file pair.

And what's crucial after modifying the methods memo field of an SCX or VCX is to compile it afterwards, otherwise the compiled object code in another memo field is out of sync and bad things can happen if you run it in that state. That means you do [tt]COMPILE FORM your.scx[/tt] as second step after the replaces.

Obviously replacing the class from textbox to another class is exactly the thing you would also do with the HackCX tool Mike recommended. Just changing class is not enough though, there also needs to be the classlib path and filename in the classloc field.

It seems expensive, if a single REPLACE can do the same as HackCX, but HackCX is much more convenient than that.

So you could use what Griff suggest as second solution, once you have a classlibrary with a textbox class that has your valid code in it. I recommend you do this:
1. Create a class in a classlib (VCX) - if you create a class you'll be asked what its name should be and into which vcx to store it, where you can specify a new vcx file.
2. drag that new class onto your SCX
3. USE the SCX (as data) and browse it, in one of the last records you should find the textbox you added and can see what's in its class and classloc fields. Then you know what to put into the records of the other textboxes.

I second Mikes recomendation of the HackCX tool, though.

Chriss
 
I was originally going to suggest hacking the SCX as per Griff's idea, but I was worried that I might accidentally overwrite some existing code in the Method field, or that the added code wouldn't get properly compiled. More generally, there is a risk of clobbering the SCX in various ways.

HackCX is safer, not least because it automatically backs everything up and also has an Undo feature. And it is very much a tried and tested product.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
If you experiment with copies you can get there, but it also depends how you feel about such a hack (the tools name is very valid). Even if it works the confidence is not as good as letting it be done with a tool that's from someone knowing what he has to do.

Mike Lewis said:
I might accidentally overwrite some existing code in the Method field
Literally adding to the methods as per Griffs REPLACE methods with methods+m.string will never overwrite something. But you might end up with two procedures of the same name, for example. You can only add if some procedure does not yet have any code in it.

I would recommend changing the class/classloc combination anyway, as that means redefining the textboxes as a new textbox class that also has future value as it can be reused for further tetboxes. And if there is a change necessary in the code, using Griffs first method won't work a second time as you would end up with two procedures VALID, which I don't even know would cause a build error or take the first (old) one instead of the changed one. Easy and safe to change the class code, though.

Chriss
 
C'mon guys - surely you've done this before? B-)

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Yes, Griff, I have done it before, and I am not in any way detracting from your suggestion. What I am nervous about is giving Steve advice when I am not completely sure of the situation: what's in his SCXs; whether there is already code in his Valid events; whether he has some textboxes where he does not want to change the Valid code; and so on.

It's one thing to sit down at your own computer and methodically examine the SCXs and work how best to do it. It's another thing to tell someone else how to do it - at a distance and within the constraints of a forum post.

At least, that's my opinion, for what it's worth.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I'm reminded of a situation a developer in a customers it department (yes, some customer have developers and still enough work to outsource projeccts) said I essentially reprogrammed the stonefield database toolkit, just because I used ATagInfo() and a bunch of further code to generate code that reindexes a dbf not using the REINDEX command but generating all necesary ALTER TABLE and INDEX commands after a DELETE TAG ALL. It's just one tiny feature the stonefield database toolkit can do for maintainig and also repairing a database.

What's true is that the tool GenDBC coming with VFP does generate code to recreate a whole DBC from scratch, including a section of code in the MakeTable procedures it generates to build all indexes of a table.

So there always are things that are already available or can be done easily. You can also always bake your own bread (I sometimes do) instead of bying at a bakery. Just think how mad it is to buy bread as all you need is flour, water, yeast, and (optional) salt.

Chriss
 
Hey Griff,

Another interesting approach. Wish I'da thought of it.

Steve
 
It is essentially the same as HackCX, but without the safety features, but if you look at the structure of the .scx
you can find the elements you want to change - even check for existing validation procedures and replace as you see fit.

But as Olaf and Mike say - take a back-up first!


Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Instead of a Valid, I added a procedure in the Click event of the button calling the 2nd form.

If any of the textboxes don't meet my (fairly complex) criteria, I used RETURN command with a message describing the user's "error" and identifying which textbox. .

So in effect, it serves as .TextN.Valid() for each textbox. Works ok.

It's a brute force method, but fixes what I had already done (or NOT done) in this case.

Steve
 
It's a good solution, I always think too individual valid events (in 24 boxes) will just hinder a user to get on. For example, if only accidentally activating a textbox, not knowinng what to write into it...

A central check is fine. Also much simpler to maintain.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top