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

Rerun Init procedure in a Form

Status
Not open for further replies.

SitesMasstec

Programmer
Sep 26, 2010
508
Brasil
Hello colleagues!

I have a form, let's call it Form1, with some commands in the Init procedure.

Well, when I click on a button in this Form1, it calls Form2, and I can do some operations there. When finishes, it returns to Form1.

I would like Form1 to run again the Init procedure, because some items have to be evaluated again, because of some operations done in Form2.

Is it possible? Every time the program return to Form1, its Init procedure be run again?


Thank you,
SitesMasstec
 
Pass as a parameter to Form2 a reference to Form1. Then before Form2 closes, call Form1 Init() method from Form2. Save the passed reference to Form1 as a custom property; i.e., Form1.

The code to call Form2 from Form1 would be:

Code:
DO FORM Form2 WITH thisform

In Form2's Init() method you would have:

Code:
LPARAMETER toForm1
thisform.Form1 = toForm1   && Store reference to custom property

Then before closing Form2 (you could use Destroy event):

Code:
thisform.Form1.Init()
thisform.Form1 = .NULL.

Greg
 
That's not what I would recommend.

But before going into details about what else to do and why, how about trying something much milder, first, like this?
Code:
Do form2 && well, or however you call form2 and wait for it to return
Thisform.Refresh()

If that's not enough it's still far simpler to let form1 reinit itself after the call to form2 by doing
Code:
Do form2 && well, or however you call form2 and wait for it to return
Thisform.Init()

The question is, if that's sufficiently doing a refresh of the forms data as you think it should. If you create grid cursors in the form init code that could cause problems with grids that are already bound to these results, doing init also would not go through all initialisation of the form, the data environment of the form and the load does not run. Therefore you might rather need to redo form1 instead. But it's worth trying.

You know it's not recommended to call events. It would be cleaner to have a separate form method reinit and call that, put all code of init into it that should be repeated, i.e. when it's all code that's all code, and in the actual init also do This.reinit().

The reason you don't call an event is that this only calls the code in it, it does not trigger its base behavior, but in case you don't need the base behavior to happen, so what? Just notice this means if your init code determines whether a user even has the right to use the form and returns .f. to close the form by that, that's standard behavor of init events of all VFP classes, return .f. from an init and the object destroys. If you call this.init() and it returns .f. that would not close the form, as you're not retriggering the actual event behavior, you just call the code of the init. Such a scenario is unlikely to be a showstopper, as the user actually has successfully started the form already, but this validity and permission check might be necessary per record the form shows and then you undermine the machanism and give users a way to sneak into access of records not belonging to them by first viewing a record they are allowed to see, then start form2 to change the outset to see other records.

So even if you redesign the form to have a reinit() method the actual init event calls, you have to consider this change of behavior, too. What reinit returns will not have the same effect as what init returns as event. So you'd actually need to do RETURN This.reinit() in the init event, but in second runs the return value of reinit still does not cause the form to quit. That's what's important to understand. It's simple to avoid calling an event by adding user define methods, which don't have a native VFP behavior, but then you still don't get that behavior by calling the method instead of the event.

So if you really need and want to restart form1 after form2 finishes, then come back. It's not as trivial to let a form restart itself without getting in its own way because it still runs. I'd put the rerun into form.unload, which should do that based on some signal value telling it to do that rerun. Because you surely don't want every unload to cause a rerun, only in the case you returned from form2.

Chriss
 
If you want the Init() event to be triggered as an actual event, then use RAISEEVENT() command from the second form (but you will need Form1 passed to Form2 as a parameter that I gave above). I would suspect that the underlying code of the Init() event is not needed again and only the programmer's code added. So then, you can just call Init() like any other method. From the help text for RAISEEVENT():

VFP Help Text said:
Activating a form or using Form1.Show triggers the Activate event for the form. However, calling the Activate event directly using a call such as Form1.Activate does not trigger the Activate event. The following example shows how you can use RAISEEVENT() to trigger the Activate event:

RAISEEVENT( Form1, "Activate")



Greg
 
I spared to explain RAISEVENT(), it's a broken halfbaked way to trigger events in my opinion.

See how it accts on Init:
Code:
Public goForm
goForm = CreateObject("myform")
goForm.Show()
Wait '' timeout 1

goForm.lOK = .F.
RaiseEvent(goForm,"Init")
Activate screen
Clear
? goForm.class && it still exists and still is "myform"...
? goForm ...and still an object.

Define Class myform as Form
    lOK = .T.
    left = 100
    top = 100
    
    Procedure Init()
       Return Thisform.lOK
    EndProc 
EndDefine

If you run this the init still doesn't cause the form to disappeear and the object to be destroyed. The major behvior of triggering the init event should be that its native behavior occurs, including to destroy the obect, if .f. is returned from init. But that's not happening. I bet MS VFPTeam would tell us this is by design and the outset differs in the first init, where goForm is not yet set and will only become an object, if the init return .t. in the first run, when it already exists, secondary runs of the init can't destroy it, but I don't see why.

RAISEVENT() is a helping hand and a construct to support BINDEVENT() behavior but it does not really raise an event, that's the downside of it.

On top of that, even if RAISEVENT() would work 100% properly, if you really need a form to restart under the changed current new circumstances and let all its initialization rerun including the dataenvironment and load event and all control intializations, that's only possible by closing the form and starting it all over again.

But I guess SitesMasstecs only problem is that the running form1 doesn't refresh, and that can be solved by calling thisform.refresh(), that's the easiest way to let form controls update to show the current record of a workarea, for example. That doesn't need any trickery, nor a form restart.

Chriss
 
I also thought I could easily rerun Form1 with the commands bellow after returning to Form1:

Code:
Thisform.Refresh()
or
Code:
Thisform.Init()

but... NO, it doesn'r work.

Please see the big picture bellow:

TekTips19Set2023_rpu5xu.jpg


Thank you,
SitesMasstec
 
SitesMasstec,

I don't understand from your screenshot what's not working. Sorry, remember we're not in your shoes. What change should happen, that doesn't happen?
One thing that has to be done is have form2 modal, so that thisform.refresh() runs after form2 closes and exxecution goes back to form1. If you don't do that you refresh practically at the same time form2 starts, and the change form2 does will not yet reflect in form1.

I'm not sure, but to me it seems you don't really know what needs to run to get form1 display what it needs to display. If form1.init() doesn't do it too, maybe also because it's done too early, again it should of course run when form2 has done its change. But we know nothing about your process of what form2 does that should reflect in form1, I only guess it's the data binding, i.e. a change of data. If you process files and delete theem the form1 init shows an array of files like you get from ADIR(), then a form.refresh() won't do that, even if done at the right time. If you have code that even runs before form1 in preparation, then maybe you actually rather need to rerun that and not form1.init nor refresh help.

I would even state it as follows, if a form.refresh() isn't able to refresh a form by its native base behavior to let all controls redraw and refersh with their data binding by controlsources/rowsource/recordsource. Then you forgot to program what else needs to be done in form.refresh(), that's actually the given method to add your own necessary code for reinitializing aka refreshing a form, and a part of the init that should be redone when you want the form to refresh is meant to go into that method, it's foreseen for that task, actually.

Chriss
 
Some more thoughts on a form.refresh() and how it works and what it's meant to do:

First of all, it's not an event, but it has some characteristics of an event, it cascades to all further levels of form objects and subobjects, every form contrl redraws and refreshes what it displays. You could ask why that's not also triggered by similar events forms have, the form.resize or the form.activate, which are events that occur and also are related to first drawing or redrawing and refreshing of a form. Well, if VFP would have been programmed to do what even just the limited native behavior of thisform.refresh() does and also cause it with each form.resize and form.activate, this could cause an endless feedback loop, so they decided to make this a point you have to actively call. It's cascading gives you power but also responsibility, as by thisform.refresh() you also cause any control on the form to run its refresh method, too. So you can use one very basic concept of OOP programming: encapsulation. In the sense of responsibbility of objects for themselves, to do what is necessary for themselves and for themselves only, in their own refresh, while you are able to trigger all refreshes with the major form.refresh() call.

The base behavior can be sufficient, if you make extensive use of control data binding and all refresh has to do is redraw and reread from the controlsources what to display. The moment in which you have code that sets a control.value instead of a control.controlsource a refresh() won't redo that and won't refresh what a textbox displays, for example, that's just bad design. VFP is very much focused on binding to data by means of the controlsource mechanism. And you mainly bind to data. There are a few other recordsourcetypes and rwosourcetypes, but your array of similar controls shouts out to me to better be done with a grid, for example, and show data from the grid.recordsource using a recordsourcetpy of alias, a workarea. That in itself would then mean a form.refresh redraws all grid rows with current data of the recordsource that presumably changes by what form2 does with the data.

Chriss
 
Export whatever you want to run in separate method and run this in INIT of Form1.
Then add a property in this form named RUNNED_FORM and set assign method
In Init of this Form:
Code:
...
   thisform.MethodThatYouWantToRun()
...

There where you run Form2
Code:
DO Form2 NAME thisform.RUNNED_FORM LINKED

then in RUNNED_FORM_ASSIGN put:
Code:
LPARAMETERS vNewVal
IF ISNULL(vNewVal) && the form2 is closed
   thisform.MethodThatYouWantToRun()
ENDIF

That way you

Borislav Borissov
VFP9 SP2, SQL Server
 
Notice, SitesMasstec,

that you get advice from several sides. It's not contradictory, but it won't all work in conjunction. Doing the form.refresh() method I propose means form2 has to be modal.

The method to share a form reference as Greg and now also Boris suggest, once making form1 known to form2 and once, as Boris suggests putting form2 reference into a property of form1, means you have ways to act on form1 in both cases whenever it is appropriate to let form1 do its refreshing. If you rather follow their advice it makes you free from making form2 modal and that's a plus in the bigger picture as then form2 and form1 can be used in parallel, form2 isn't having exclusive focus and you can even call up further forms if form2 would need assistance from a third or fourth form, too. So more freedom. I don't think this is easy to understand, but may be necessary, if you need form2 to be non modal.

If you even don't know what modality means, in short it gives exclusive focus to one form that can disallows anything else to be activated. The simplest modal form we all know is a messagebox, you have to click one of the options the buttons give you, like OK, or close the messagebox, before anything else can be used again. In VFP a modal form also stops execution at the point a modal form is called and code execution goes back afer the DO FORM, making things more straight forward by not needing to think what can run in parallel, if all forms are non modal. So in short, non modal forms are surely an advanced way or organizing things as they give more options also to the end user of what he can use in parallel, modality is more of a guided process were the user is guided to what he should do right now and that also makes life easier as a programmer.

I'm not against Gregs or Boris advice, but it might go even further over your head as my adive to make form2 modal, if it isn't already. There's a lot to digest right now, but I hope you can pick up the pieces and puzzle them together so they work for you.

Chriss
 
Yes, Chriss, Form2 has the property WindowType set as 1-Modal.

But if I rerun the form, as you suggested above, yes, Form1 is refreshed and I got the expected result in it!


Thank you,
SitesMasstec
 
I don't know whether you now follow my advice or rather Gregs, but rerunning a form means you will have two of them. Calling the form init is not creating a form, DO FORM creates a form, init is not a constructor of an object, it's an event triggered by the creation of an object, its a reaction, like any event, to the creation, it isn't calling the creation, also not by raiseevent.

If you followed Gregs' advice, you start form1 newly and destroy the current instance of the form If that works, fine, I think it's quite drastic, you could program form1 to not restart from scratch, but only refresh its content as necessary.

Anyway, good to hear the problem is solved, probably.

Chriss
 
In Chris's above reply:

Chris said:
you start form1 newly and destroy the current instance of the form

That is misconstruing what I intended. Form2 merely calls the Init() code in the existing instance of Form1 via passing the parameter or via RAISEEVENT(); it does not recreate Form1. I do not think RAISEEVENT command is needed but merely re-excuting the Init() code contained in Form1.

I had another thought on a possible solution. Not that I would recommend using this, but it would work and could possibly be a reason to use FormSets. You could create the two forms under a single FormSet. Then you can reference each form from one another via thisFormSet. You would not use thisform.Release() to close Form2, but instead use thisformSet.Form2.Hide(). You can use thisFormSet.Form1.Methodname to access a method in Form1 from Form2. To close the formset, use thisFormSet.Release().

Another way for a possible solution, would be to call Form1 like the following:

Code:
PUBLIC goForm1
DO FORM1 NAME goForm1

Now you can use the name goForm1 in Form2 to call the method:

Code:
goForm1.Init()

Just a couple of thoughts...

Greg

 
Greg said:
That is misconstruing what I intended
Sorry, but I was thinking about the oother thing you recommended before:
Code:
thisform.Form1.Init()
thisform.Form1 = .NULL.

Where Form1 would be destroyed. Maybe I misinterpret this too. And you just meant to forget the additional form reference to not get into trouble releasing the form later.

Anyway, if you actually want to restart a form, then this goes hand in hand with destroying the current form. I myself would rather redesign the code to not need to do that, as I said:
myself said:
you could program form1 to not restart from scratch, but only refresh its content as necessary.

So I guess we agree on that being the better solution.

Chriss
 
Chris

Yes, the code

Code:
thisform.Form1.Init()
thisform.Form1 = .NULL.

Is to call the Form1's Init() method code (it does not perform the Init event). The second line does not destroy Form1 since it is a property of Form2 that was assigned to Form1 (thisform) reference. This statement's purpose is to remove the reference to Form1 so that it is not being held in memory which could cause problems if not handled properly.

Greg
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top