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!

Converted Form Advise Requested 4

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,814
JP
All,
I have a form that I'm in the midst of converting from FPW2.6 to VFP 7. One control is a ListBox that has code along these lines in the Valid:

SELECT CTINFO
SEEK M.LISTVALUE
IF FOUND()
SCATTER MEMVAR MEMO
ENDIF

The basic premis of this code is, when the value in the list box changes, it locates the value in a lookup Code Table. If it finds it, the SCATTER MEMVAR MEMO "Automatically" replaces the values of about 15 fields. This techinque works great, and was very common in the 2.x days, as I'm sure many of you are aware.

Now, to 2002, and VFP7. This doesn't really work in my converted form. There are no "Memvar" equivelents to these fields. (Since, everything now is an Object, and has to be set with:

"ThisForm.txtInfo.Value = CTINFO.INFO"

What I am trying to avoid, is having to creat a great huge routine, which specifies every field in a table. This is a big problem, because if the name of a field changes, I have to go to the form, and make changes as well. With the "Scatter" method, they would be automatically assigned, regardless.

The second part of this problem is, some fields are displayed, but others are not. I still want all the values to be "Updated", so that when I issue a "TableUpdate()" command to save my changes, the values from the CTINFO table will be reflected.

Is there some VFP super-cool trick to this, or am I left to forceibly hard code every "ThisForm.txtObject.Value =" statement for every field & variable I want to update?

Thanks,
-Scott

s-) Please let me know if this has helped s-)
 
Scott,
Actually the simplest conversion trick in this area, is to create a form property, and use the NAME <objectname> option on the SCATTER (and GATHER) to store your current data record values there.
e.g.
SCATTER NAME ThisForm.r_oDataRecord MEMO

Instead of m.info, you'd refer to it as ThisForm.r_oDataRecord.info. Using WITH blocks and a shorter form property name, references could be only a bit longer than the original code, and the program &quot;logic&quot; wouldn't have to be changed.

Note: While I don't recommend this in all cases, it's good enough for a quick and dirty conversion.

Rick
 
The other way, which would be a direct translation of your old screen (and NOT good programming practice, IMHO, but easier) is to change your control bindings (ControlSource property) back to the variables that are created with SCATTER MEMVAR. The variables must all be PUBLIC (or declared in your main PRG file) for this to work.

Again, I don't recommend this approach for programming in VFP, but it's probably the easiest way to get your converted form working as it used to.

Ian
 
RGBean, and CHPicker,
Okay, I see both your points... so, next question is, instead of doing these (let's say, I re-write the whole screen, which, I have the time luxury to do in this case), what's considered the &quot;New&quot; best practice for something like this? Is the new &quot;Norm&quot; to go ahead and phsyically name all of your items with TextObjects, and then reference their value??? I want this to be as &quot;OO standard practice&quot; as possible. My primary objective here is reusable code, that won't require &quot;Search & Replace&quot; should a field name change.
Any suggestions???


Thanks,
-Scott

s-) Please let me know if this has helped s-)
 
Hi my friend
Start a new form
Add 3 text boxes Text1, Text2, Text3
Add 3 command buttons with captions FillArrys, Sync, Next

In the Valid of FillArrays insert this code
Code:
*// Task : add code to make sure that the two arrays has the same dimension
*// Otherwise generate error.
ThisForm.StringToArray(cFieldName, @aFieldName)
ThisForm.StringToArray(cControlName, @aControlName)

In the Valid of Sync insert this code
Code:
ThisForm.Synchronize()

In the Valid of Next insert this code
Code:
lnRecorNum=RECNO()
IF BETWEEN(lnRecorNum,1,RECCOUNT())
  Skip
ELSE
  LOCATE
ENDIF  
ThisForm.Synchronize()

In the initialize event of the form insert this code
Code:
Public Array aFieldName(1) , aControlName(1)
Public cFieldName , cControlName
cFieldName=&quot;Billto,Account,Store&quot; && replace these field names with any valid ones                   from open and selected table.
cControlName=&quot;Text1,Text2,Text3&quot; && replace with the real names of the controls.

Add a method to your form and call it StringToArray and implement it with this code
Code:
*//////////////////////////////////////////////////////////////////////////////
*// Receive : 1) A string consists of some words separated by delimiter in 
*//                     the first parameter lcString.
*//                 2) Array by reference in the second parameter 
*//                     laArray.
*// Function: 1) Parsing lcString to detect How many words in this string
*//                     This number will be used to assign the right dimension for 
*//                     the array.  
*//                     2) Extract every word and assign it to new array element.   
*// Returns      The array successfully Redimensioned and populated with the 
*//                    words in the string.
*// Implementation notes : In VFP 6.0 the function that return the number of words in a           *//                                      delimited string and the function that return the word number n
*//                                      from delimited string are both external functions in the foxtools
*//                                      library and they are Words(), WordNum() respectively.
*//                                      Consult foxtools.html help file in the tools directory for more.
*//                                      In VFP 7.0 the two function became native and they are
*//                                      GETWORDCOUNT(),GETWORDNUM()  respectively.
*//////////////////////////////////////////////////////////////////////////////   
PARAMETERS lcString, laArray
Local llNativeFunction, llSetLibrary, lnWordCount, lcExpWordCount, lcExpWordNum, lcOldLib
STORE SPACE(0) TO lcExpWordCount, lcExpWordNum, lcOldLib
llSetLibrary     = .F. 
lnWordCount      = 0
lnIndex          = 0 
llNativeFunction = IIF(Left(Version(4),2) == &quot;07&quot;,.T.,.F. )

IF !llNativeFunction
  IF !(&quot;FOXTOOLS&quot; $ UPPER(SET('LIBRARY')))
    lcOldLib = SET(&quot;LIBRARY&quot;)
    *//Make sure FoxTools.fll in the root directory of your application b4 issuing this command.
    SET LIBRARY TO FOXTOOLS.FLL ADDI  
    llSetLibrary = .T.
  ENDIF 
ENDIF

lcExpWordCount   = IIF(llNativeFunction,[GETWORDCOUNT(],[Words(])
lcExpWordCount   = lcExpWordCount + &quot;lcString,[,])&quot; && Assuming the delimiter is &quot;,&quot;

lcExpWordNum     = IIF(llNativeFunction,[GETWORDNUM(],[WordNum(])
lcExpWordNum     = lcExpWordNum + &quot;lcString, lnIndex,[,])&quot;


 lnWordCount = EVAL(lcExpWordCount)
 IF lnWordCount > 0
     DIMENSION laArray[lnWordCount]  && Redimensioning the array to the right number 
     FOR lnIndex = 1 TO lnWordCount
         laArray[lnIndex] = EVAL(lcExpWordNum)  
     ENDFOR    
  ENDIF
   

IF llSetLibrary  
  SET LIBRARY TO &lcOldLib
ENDIF

Add method to your from and call it
Code:
LOCAL lnIndex, lcExp
FOR lnIndex = 1 TO ALEN(aFieldName)
  lcExp = [THISFORM.]+aControlName[lnIndex]+[.VALUE]+[ = ]+ aFieldName[lnIndex]
  &lcExp
ENDFOR
That is it, run the form and in sequence press FillArrays,Sync,Next just one time and then press Next. Each time you press Next you will find your controls updated with the fields values.
From the OO point of view we built an Observer mechanism which is by definition
Define a one-to-many dependency between objects so that when one object changes state,
all its dependents are notified and updated automatically.
If you are interested in the design patterns consult Design Patterns (Gamma) for more in the subject. Don’t worry if you don’t understand the book from the first reading, the authors didn’t understand it from the first writing. they said so.

Now if you need to add field/object or change the name of field, you need to change only one line of code cFieldName=&quot;Billto,Account,Store&quot; in one place which is the initialize event of the form. If you want to change the correspondence relationship between the filed and the controls, it is also modification in one place. Same is true if you want to add new field and new control.

From OO point of view, this observer suppose to be in a separate class not in the form, so if we need to Enhance/Modify/Add to the functionality, we need to do this in one place and all the forms that uses this observer in the application will still working provided that we maintain the same interface of the observer.

Hope this will help.
Final word, I admired your desire to learn the right OO design not just to convert your old code.

Walid Magd
Engwam@Hotmail.com
 
Walid,
Wow... this is VERY deep. I'm going to try this over the next few days. I think there is a lot to be learned from your post. (I may ask clarifing questions later, if that's okay...)

Well done!

Thanks,
-Scott

s-) Please let me know if this has helped s-)
 
wow this great man for me it is high math but I will figure it out

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top