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

Macro substituition in a form 2

Status
Not open for further replies.

SitesMasstec

Programmer
Sep 26, 2010
526
Brasil
Macro substituition in a form

Hi colleagues!

I'm using a form for data entry with a maximum of 20 itens.

I will have in each item (row in form)
product 1 .....
product 2 .....
product 20 .....

I wrote for item/product 1 (Thisform.Pageframe1.Page1.txtPRO1) in the GotFocus procedure:

IF Thisform.Pageframe1.Page1.txtPRO1.Value="R"
Thisform.Pageframe1.Page1.txtDES1.InputMask='9999'
ENDIF
IF Thisform.Pageframe1.Page1.txtPRO1.Value="E"
Thisform.Pageframe1.Page1.txtDES1.InputMask='999999'
ENDIF

Will I have to repeat all this for the other 19 itens, changing only the numbers 2 to 19 OR is it possible to use macro substitution to avoid this?

For example, I imagine this:
MyNumber=2
MyCommand="Thisform.Pageframe1.Page1.txtDES"+ALLTRIM(STR(MyNumber))+".InputMask"
and then:
&MyCommand='9999'

If this is possible, WHERE I have to wrote this code?

Thanks,
SitesMasstec

 
SitesMasstec,

Maybe this will do:

lstr = 'Thisform.Pageframe1.Page1.txtPRO1.Value='
valueR = lstr+"R"
valueE = lstr+"E"
valueQ = lstr+"Q" && My own addition to extend the example

Thisform.Pageframe1.Page1.txtDES1.InputMask=IIF(&valueR.,'9999',IIF(&valueE.,'999999', IIF(&valueQ.,'999999.99', '<otherwise>')))

You write the code in the "Form.Init" event if it is a static distribution or in the "Form.Refresh" event, for a dynamic one.

I have not tested this, but it may be useful to you to get going.

Good luck

Dennis Kean
 
You should rather select the txtPro1 and txtDES1 controls and maybe labels of them and then in the File menu select Save As Class.

You'll be asked for saving selected items as a class and can specify a classname and destination VCX (class library)
Then add this VCX to your project. Before repeatedly using that class you'll need to modify the code in txtDES1 GotFocus (not in txtPro1 GotFocus) this way:

Code:
IF This.Parent.txtPRO1.Value="R"
 This.InputMask='9999'
ENDIF
IF This.Parent.txtPRO1.Value="E"
 This.InputMask='999999'
ENDIF

Reason to switch the event: GotFocus happens, before txtPro1 is edited. If the user enters E instead of R in that box, the Inputmask doesn't change. You have to decide the inputmask when txtDES1 gets focus, not too early.

Once you have that class you may also rename txtPRO1 to txtPro. Then drag&drop this class several times, you'll get classname1,classname2 etc container controls each having a txtPro and txtDes textbox. You write the code about the dependency between these two textboxes once and can also modify it later at this one place.


And a totally differrent approach would be using a grid for this. Everything that repeats in a form has a "smell", you should use a control that allows repeated display per record then and not add 20 controls to a form.

Bye, Olaf.
 
Hello!

Well, I also use some of what Dennis demonstrated:

But instead of putting this in the control:
lstr = 'Thisform.Pageframe1.Page1.txtPRO1.Value='

I will put this in the first control:
MyNumber="1" (in the second control: MyNumber="2" and so on, till MyNumber="9" in the 9th control)
DO MyTask


PROCEDURE MyTask
lstr = 'Thisform.Pageframe1.Page1.txtPRO" + &MyNumber + ".Value='
RETURN

As Dennis suggested, I tried to put the PROCEDURE code in the "Form.Init" or in the "Form.Refresh" event, but the VFP gives the error as soon as I try to save the Form: "Methods and events cannot contain nested procedures or class definitions"

My question is: where in a Form I have to write the MyTask procedure? Or may I put it in a default .PRG sub-routine?

Note: I am not confortable with use of Grid control yet, so for now, I prefer the use of procedures and macro substitution.

Thank you,
SitesMasstec
 
You can create a custom method by choose Form | New method while in the Form Designer. Once you've added the method, it'll be available in the Property Sheet and Method Editor to add the code.

Tamar
 
SitesMasstec,

By the way, there are times when it is advisable to use two events or more to affect an update as the one you are trying to do here. In that case, rather than repeat coding the entire DoCase complex you can use this shortcut.

In the textBox event you choose you will put the DoCase...EndCase complex, let's say in the event Init. But you may also need to trigger it when you refresh the page. So, in the Refresh Event of the TextBox you can enter this:

Code:
This.Init()

Now, whenever the event Refresh is triggered, it borrows the code from Init Event to do his bidding and update your Masks. And of course, you may want to update the masks whenever you change the letters which command your masks. So in the InteractiveChange event of the textBox you insert :

Code:
This.Init()

And you will always be up to date.

Let me know if I confused you.

And forget about the macros. Olaf is usually right. I did not grasp what you were doing at first, my apology! I went Macro crazy on you. I use Macros occasionally and they can bail you out in some super difficult situations. But always search for a clean solution. Macros should be the last resort on your agenda.


Dennis Kean
 
Thank you Tamar and Dennis.

I am working on Tamar approach now. I will test Dennis advice until I understand it better.


Working on Tamar advice:

In the GotFocus event of a Text Control I have this code:

IdRotina="GotFocus"
MyNumber="02"
DO SUBRCADAS

--------------------------------------------------------------------

In the SUBRCADAS Method of the FORM1 I have this code:

IF IdRotina="GotFocus"
frase="THISFORM.Pageframe1.Page1.txtROR"+MyNumber+".Value"
&frase=SPACE(1)

frase="THISFORM.Pageframe1.Page1.txtRCO"+MyNumber+".Value"
&frase=SPACE(5)

frase="THISFORM.Pageframe1.Page1.txtRNO"+MyNumber+".Value"
&frase=SPACE(10)

RETURN
ENDIF

--------------------------------------------------------------------

When executing, I click the mouse pointer on the Text Control to type enter a data, the following error appears:

File not found: DO SUBRCADAS (1)


How to tell FORM1 where is the location of the SUBRCADAS procedure? (in a PRG I would say: SET PROCEDURE TO SUBRCADAS; of course a SUBRCADAS.PRG is in the project)

Thank you,
SitesMasstec



 
As per your comment,
SitesMasstec said:
In the SUBRCADAS Method of the FORM1 I have this code:
...this is a Form Method. Therefore, to call a method you have to code it this way:

Code:
ThisForm.SUBRCADAS(1)

This is not a procedure. It is a Method. You use Do XXXXX on procedures.

Dennis Kean
 
SitesMasstec

I am reworking my former idea here because I tested it and I found a simpler way.

Since txtPRO1 is adjacent to txtDES1 on all the pages, you don't have to worry about mapping the total pointer path. You should not, as you will see below.

You need to use a container for these two textBoxes. Call it ctr and move the two boxes into it. So you will then have a container containing txtPRO1, txtDES1. The three of them together will become a class in a library. You will be able to dispense them to any location on your Form when they are built.

Candidate Events for running the code are txtPRO1's events:
GotFocus, Init, InteractiveChange, LostFocus or Refresh. And you may need to trigger the same code from several of them, depending on your design.

This is the code you need:

Code:
Do Case
Case This.txtPRO1.Value="R"
[indent]This.txtDES1.InputMask='999999'[/indent]
Case This.txtPRO1.Value="E"
[indent]This.txtDES1.InputMask='9999'[/indent]
Case This.txtPRO1.Value="P"
[indent]This.txtDES1.InputMask='99999.99'[/indent]
* Etc...
Endcase

The code will need to be run from the container object. You called your method SUBRCADAS, I called mine MaskIt. I'll stick to mine. Put the code in the MaskIt method.

Now wire the txtPRO1 events you desire to fire this code:

Code:
This.Parent.maskit()

I recommend you use events: Refresh and InteractiveChange

If you do it this way, you will not have to rewrite the code 40 times in the 20 objects. You will make this modification in the Library class called ctr and since the code accesses only members of its own class you won't have to worry about threading long pointer paths. Just drag the class objects one by one into place on the form and you will have every class object worrying for its own welfare. The reason? The class is encapsulated, (provided you put the textboxes in a container and drop the container on the page.)

If you do not know how to create a class in a library, you will have to learn that. It is not hard. Start a new question and we will help you. But library classes help to cut down on repeating the same code and save lots of time as well as allow you to make a change in one place to affect all the objects of that class.

Let me know if you need more help or if something is unclear.

I tested this and it works and the code is minimal. No MyNumber, IdRotina, frase, txtROR, txtRCO, txtRNO, no macros, just a slick and smooth way to do what you want.

You also will need to add "K!" in the format property of txtPRO1 in the library class of course.

I tested this code and construct and it works great.



Dennis Kean
 
Hello colleagues!

For now, I am trying to put the code in a sub-routine inside a Form. Tamar gave me some instructions above but I have something missed.

I will have to put the following code in EVERY control! (please see the figure in the link bellow) I would like to avoid this, putting the code just once in a sub-routine inside the form. I have not achieved to do this.

Thank you,
SitesMasstec
 
 http://files.engineering.com/getfile.aspx?folder=64bda569-f270-4bf8-83e1-20290238abdc&file=Duvida1.jpg
The right way to handle needing the same code in multiple controls is to build it into your "base" classes, that is, your first level subclasses of the VFP base classes. Then, put those on your forms, rather than the VFP base classes.

This really seems like a classic case where building the functionality you need into subclasses would make your life much easier. There are various tools you can then use to change your VFP base class controls into controls of the classes you create.

Tamar

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top