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!

Showing selected grid item to another form 2

Status
Not open for further replies.

SitesMasstec

Programmer
Sep 26, 2010
523
Brasil
Here I am again, not able to do this:

Grid2a_rtzf9g.jpg


When I double click the desired item in the grid in child form (A), I want it to be displayed in the major form (B)






Thank you,
SitesMasstec
 
Hello dear colleagues!

Olaf said:
I don't get why you absolutely insist on doing something more complicated.

Olaf, of course I prefer simpler solutions, like the one you explained. Problem is I had not understood it, this is the reason why I had not adopted it befone.

Now after reading your today post, it is clearer to me: I understood that a Cursor can be shared by parent and child forms, is it right? I understood also that I can get back only one value from a child form using PARAMETERS (well, I was going to add the two fields in the child and back in the parent decompound it in the 2 fields). I will try your solution. Thank you.

I beg you pardon, Olaf, Tamar, Mike, Scott and other people trying to help me, for my slow VFP learning.


Thank you,
SitesMasstec
 
Don't apologize for needing time to learn. I think all of us experienced the move from procedural to OOP as a series of frustrations followed by "aha!" moments.

Tamar
 
In the Grid (from my Child Form), DblClick event I have:
Code:
* Codigo Escolhido
oCodi=itensest.wecodi 
oNome=itensest.wenome


CREATE CURSOR SELECAO1 (CODI1 N(6,0), NOME1 C(10))
APPEND BLANK
REPLACE CODI1 WITH oCodi
REPLACE NOME1 WITH oNome

thisform.Release

If this the right way to put the selected record into cursor SELECAO1 ?

or this way?:

Code:
* Codigo Escolhido
oCodi=itensest.wecodi 
oNome=itensest.wenome


CREATE CURSOR SELECAO1 (CODI1 N(6,0), NOME1 C(10))
INSERT INTO SELECAO1 (CODI1, NOME1) VALUES oCodi, oNome

thisform.Release


Thank you,
SitesMasstec
 
SitesMasstec,
We can see you are trying hard, so that's what matters. I'm still making the transition to a more complete usage of OOP too, and it's not intuitive. It can be really tiresome sometimes, but step by step, it gets better.
Regarding your question, both work. Some prefer one way others prefer the more SQL style of your second example. It has the benefit of being fewer lines, and does both the insert and replace in one call. But nether are right or wrong.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
SitesMasstec, no, you don't generate the cursor in the child form, you already create this in the init or even in the load of the parent form, so the cursor is already there. The keypress then only needs to set the field of the current record. I already posted the code you need in your other thread184-1777707.

It's exactly the same solution, that can be applied here, too.

Repeated:
Code:
SELECT * FROM itensest WHERE .F. INTO CURSOR crsMainform READWRITE
APPEND BLANK IN crsMainform

Code:
LPARAMETERS nKeyCode, nShiftAltCtrl
IF nKeyCode=13 AND nShiftAltCtrl=0
   REPLACE wecodi WITH itensest.wecodi IN crsMainform
   Thisform.Release()
ENDIF

You just need to change what table to copy fields from (you may not need a copy of itensest.dbf this time) and what field to set (this most probably isn't the wecodi field this time).

It's depressing to me that you take a solution and forget about it the next instance and ask the same question again, not just month later, but at the next moment. This is exactly the same problem you already had and so the same solution applies. You just need to identify the "moving parts", the things that need to be individually changed. And what else is that but the involved tables and fields?

Bye, Olaf.
 
Hello Olaf!

Now I have it working, at last!

For practical purposes in my application, I wrote the code bellow in
txtCodi Textbox, Got Focus on the Parent form (instead of using a Click button)

Code:
DO FORM ITENSCAO3    && calls the Child form

thisform.txtCodi.ControlSource="crsMainform.WECODI"
thisform.txtNome.ControlSource="crsMainform.WENOME"

Thisform.Refresh


As I executed the Parent form, and selected one item (a row) in the Child form (ITENSCAO3.SCX) I noticed that the file ITENSEST.DBF is opened in area 1.
Well, in the Parent form, Destroy event, I put the code:
Code:
USE IN SELECT("ITENSEST")

As I have some files in my application, and all of them have a defined area (I am going to step by step change them all.
to open:
Code:
USE anyfile in 0

and to close:
Code:
USE IN SELECT ("anyfile")

Let's suppose that a file has its area automaticaly opened in area 1 (when using 'USE anyfile in 0').
If in the same form I have a hard command SELECT 1... USE anotherfile, which I haven't changed yet, there will be error.
So I have to change the entire application to 'USE anyfile in 0' instead of 'SELECT (number)... USE anyfile'

While I do not change the commands in the application AT ONCE, I need to define a select area number for ITENSEST.DBF, to avoid error, used in:
SELECT * FROM itensest WHERE .F. INTO CURSOR crsMainform READWRITE

I have not found in the full syntax for the command SELECT a way to define an area number before the command above. Is there a way to (for temporary use) define area number of a file to be opened?


Thank you,
SitesMasstec
 
No, there is no way to determine in which workarea number a cursor will be created. That's one of the reasons working with fixed workarea numbers is so bad.
Also see thread184-1777495. This exactly was already said there. Are you reading us?
myself said:
Think alone about this: SQL-SELECT INTO CURSOR (Select Areas 20-29 in your world) does not allow specifying a workarea number, only alias names.

The best thing you can do is create this cursor last in the initialisation process of the parent form. A temporary solution would be query INTO TABLE instead, generating a DBF file, then open that in a specific workarea number. But you loose performance.

Or you open anything in all the workarea numbers you need as fixed numbers, so the SQL-Select must use a higher workarea.

You could initialize your datasession this way:
Code:
CREATE CURSOR crsEmpty (cDummy C(1))
LOCAL lnWorkarea
FOR lnWorkarea = 1 TO 20
    IF EMPTY(ALIAS(lnWorkarea))
       USE DBF("crsEmpty") IN (lnWorkarea) AGAIN ALIAS (SYS(2015))
    ENDIF
ENDFOR   
SET
This occupies all workareas 1-20 with the same crsEmpty cursor and every further cursor thus must be generated in a higher workarea number. Also, it leaves any workarea as is, that has something opened in it already.

Everywhere you SELECT N and USE sometable the cursor in that workarea vanishes.

Bye, Olaf.


 
That hack to occupy all workareas is working, even if you first close the original cursor crsEmpty before closing all the other workareas/aliases of that cursor. Just notice this is not the thing you want to have and do just to be able to stay with your fixed workarea numbers. You can't make use of many advanced FoxPro features like referential integrity triggers also using arbitrary workarea numbers, if only temporary. the only way to reserve workareas is by always using them, but that makes it harder to debug something watching at a cluttered datasession window.

So changing all code to use fixed alias names instead of fixed workarea numbers has to remain your first priority to get going.

What you need to do here is paying what we call a technical debt. You kept things as they are instead of regularly refactoring and modernizing code. You have to take advantage of new features. Staying with old ways just becomes a barrier sooner or later and then this technical debt has interest and compound interest and is more work than a regular upgrading would have cost you. This is like leaving your home in a mess and only clean up once in decades. Of course, you'll find trash and mold once you begin tidying.

Bye, Olaf.
 

Hello Olaf!

Believe me: I am reading every post, and sometimes more than twice.

Besides the VFP 9 Help, I have a collection of books about VFP (including Hentezenweke's "The Fundamentals" for VFP 6, "Megafox", etc). But working with a language with such a large amount of commands and objects and their events, sometimes it's hard to find the best solution and sometimes getting caught using bad code (as Tamar alerted me about public variables). And because of the "technical debt", of course.

It was a surprise because I have been using public variable for more than 10 years and I have not received complaints from users of my applications (please note that I am not saying Tamar and others are wrong; indeed I have a book by Tamar)

I am going to change my code in order to apply the advices I have been learned in this forum. Thank you.

Thank you,
SitesMasstec
 
I have an application that have been running in many places since 1996.
There are some public declarations in the beginning of the main program, but I will not change anything just for the sake of it. "If it ain't broke...
 
Drdloittle, thanks for sharing your experience about public declarations. But of course, from now on I will avoid using them, and I am going to change my older applications. Maybe we have been lucky until now, but Murphy's Law may be applied soon...



Thank you,
SitesMasstec
 
I would actually say a working application is a proof in time. It's bad for maintenance and extensibility, but I wouldn't go as far and change any old applications, too. That would rather be like redrawing your childhood pictures because nowadays you can draw better. Concentrate on recent work you still maintain and extend.

In this case here, once you get to the bottom of it, it is the nicest and easiest solution not even needing passing forth and back parameters, just acting on the shared workarea. Also you get something you can apply in many cases.

And if you like every form to have a private datasession, you still can share data, you do it all the time, if two forms act on the same DBF, so you could also define a permanent DBF instead of a cursor as your shared data source. That means acting on two separate datasessions. Openening tables then remains just to be done by the forms data environment, etc. Cursor are just more compatible with local and current user session only usage without overlap and problems of multi user use of such a DBF. But in the end it shows you how far it is into your all day usage of VFP as a database application language. This time you just bind two forms to the same cursor instead of the same table.

Bye, Olaf.
 

Revisiting "passing parameters", I have tried to understand this:

In Parent Form, txtCodi Textbox, GotFocus:
Code:
DO FORM ITENSCAO5 TO lRetVal 

thisform.txtCodi=VAL(SUBSTR(lRetVal,1,6))
thisform.txtNome=SUBSTR(lRetVal,7,40)

Thisform.Refresh

In the Child form, Init:
Code:
LPARAMETER codinome
ADDPROPERTY(This,"lRetVal") && Used to hold a return value to the calling form
*
This.lRetVal = 0

Child Form, Grid1. DblClick:
Code:
* Chosen code
lRetVal=STR(itensest.wecodi,6)+itensest.wenome
thisform.Release


Child Form, Unload:
Code:
RETURN thisform.lRetVal

I want to get the lRetVal in the Parent Form. I tested and see that lRetVal has a value while in the Child Form, but Child form does not pass the result to Parent form!






Thank you,
SitesMasstec
 
Is child form modal? You can only return values from modal forms.

Tamar
 
Why again? Why go for this outdated and unnecessary concept? It's not fine, it's just stupid.

This is unnecessary code.

You want a form to act like a function. Passing in some parameters, getting back a value.

You are choosing VFP because it is a database oriented language and you want to work in it like it is some BASIC code. You can share what you always share in Foxpro Forms: data. This is the core concept of VFP, to share data. And you still rather want to copy data to variables pass them in, stores them in properties, return them and then finally store them back to the table it came from, now changed. Or similar.

How many more steps to do just to share data? This is something you don't want to learn or be capable to do, this doesn't bring you forward at all.

Bye, Olaf.
 

Yes Tamar, the child form and the parent form are both 1-Modal.

Olaf, the data I wish to get from the child form will not be used in another table, it is just for displaying it in the parent form.

With your valuable help I applied the use of cursor in the aplication I am currently working. About passing & get data between forms, I just want to test it in order to learn how to pass data to a child form and get result back in the parent form, without the use of cursor/table or global variable (Tamar advice is still echoing in my ears...lol). Maybe in the future I will need it, without involving any cursor.


Thank you,
SitesMasstec
 
>Olaf, the data I wish to get from the child form will not be used in another table, it is just for displaying it in the parent form.

I already said the same thing here, too: Even when you don't want to store data persistent, you can CREATE a CURSOR. CURSORs are just the kind of temporary DBFs ideal for that case.
You can't share a variable with two forms, but you can share a DBF or CURSOR (which also is nothing else but a DBF). Okay?
You can also bind form controls to a cursor, so that makes it the perfect match without all the ado of creating variables, passing them forth and back.

Bye, Olaf.
 

Ok, Olaf, I thought it would be more "natural" to get data through parameters when not using the result in another table, but just for displaying. So if I "can't share a variable with two forms" I will forget it.[thanks]

Thank you,
SitesMasstec
 
As said the natural way to share things is a table.

If you have time for the whole background:
There is the topic of passing parameters by value vs by reference, eg:

Code:
* Passing by value
LOCAL lnNumber
lnNumber = 1
lnNumber = Increment(lnNumber)
? lnNumber && prints 2

Function Increment(tnNumber)
Return tnNumber+1

Code:
* Passing by reference
LOCAL lnNumber
lnNumber = 1
Increment(@lnNumber)
? lnNumber && prints 2

Procedure Increment(tnNumber)
tnNumber = tnNumber+1

The @ extends the scope of the variable to the called procedure, though the procedure still has it's own name tnNumber (notice the t instead of l) for the variable in its code. It's not the procedure but the caller deciding with the @, what memory variable the called procedure acts on with its internal variable name tnNumber.

That works universally, but a form isn't a function or procedure with visual interaction, so if you add @ to your parameters what happens is only the first INIT event of the form with its LParamerters line would act on the passed-in variable by referencing the same memory. The rest of the form - and that is important to you - doesn't know about the init parameters of the form. The form parameter are there to influence the form state or behavior at its beginning, not to act as two-way in and out parameters.

So to act on the passed in values, no matter of you pass them in by reference or value, you need to copy their value to something persisting in the whole life scope of the form. And yes, you can pass something back, to yet another variable the caller specifies in its call as TO clause, TO lcTargetVariable. But that's only possible with modal forms, which in itself is a limitation I would not like, even though it makes the user focus on this current workflow step of picking something. And in that case, as the RETURN something goes back into another variable, you don't have that shareed variable thing at all, unless the init would already give you the feedback you want, but that would never be the intention of the form - to have a result before it is even shown.

You argue from the point of view a variable is the best thing to store a single value, but due to the limitations of passing by reference you're bound to copy your values around multiple time:
1. copy the variable value from the caller to the form init parameter
2. copy the init parameter to a form.property or anything else persistent in the lifetime of the form
3. copy that property or any form element value as return value in the Unload event
4. copy from the target variable back into the original caller variable you wanted it to be, or even copy it somewhere else, into a table field.

And besides this copying, you also have at least 3 names for the same thing, the caller variable, the init parameter name and the child form property or control name.

Using a cursor in the shared datasession, the value we talk about always is in one field of one record of that cursor, referenced by the one single name this has. The value never travels anywhere, it never needs to be copied. Though it takes only split seconds to copy even large values in memory, is it really more natural to do so instead of simply sharing a cursor?

The cursor also allows you to share as many as 254 fields in one record and you can have multiple records, too. Parameters are limited to 27. And Unload only returns 1 value.

There is one more thing you can do also without the @, you can pass objects or variables that refer to objects. Both just means passing the object reference, there is no passing of objects by value, so they don't get copied. Not, that the act of copying is the bad thing, but that has the advantage you act on the original. But you need the same steps as with variable, you need to store the passed object reference to a child-form property, etc. You don't need the RETURN in the Unload, as you can simply act on object properties and thereby let a picked choice come back to the parent also still knowing that same object, but it still is not as easy as sharing data, what a database system is made for, is it?

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top