I only agree with Tamar and Dan, if you accept modal states in general, I would avoid them as far as I can, so the user is never forced to a single form.
Of course users can't do 1000 things in parallel anyway and normally concentrate on one workflow, but actually we are used to parallel working more than we are aware of. Even a single form offers many options in parallel, you can use any control unless you disable them and only enable them in a certain order to guide the user through the form, but I dislike such guidance, and I think I am not in the minority with this. Often enough you get stuck as a user or as the developer of such guidance, as rules for the intelligent guiding are a moving target.
So normally you can act on any button or other control you like, you're not forced to sequentially working on something and modal states do that on the scope and granularity of forms. So a) the user isn't liking modal states and b) as a developer you're already used to program for parallelism without being an expert on threads and multi-threading or multi-processing. There is no reason to use and support the concept of modality, just because that's the intended way of MS for DO FORM ... TO Variable.
Your general idea of wanting a non modal form is welcome to me. Your reasons are pointing out lack of knowledge. All code is accessible at all times, that's just a matter of visibility of the code by set path, set procedure and set classlib, if a form is modal all other things still exist, all object instances are there, all forms. It's just the OS UI concept of modal blocking the user to switch the form, you can only switch to other applications, but within the current application the modal form asks for your attention until you're finished with it.
So "should you still be able to access methods in other objects or procedures in programs?" Yes, other things are available. So if the modal form calls back into the calling form or changes data displayed there, that's possible with modality. Modal states are only about the UI accessibility, modality is hindering deactivating the modal form and switching/activating another form. Calls to the other forms or procedures or anything else are not blocked, the only thing blocked is the focus/activation of other forms than the current modal form.
So modal states are hindering the user to start a new completely unrelated workflow, eg for answering someone on the phone. Also modal states hinder a shutdown, especially if you have such a form opened in the middle of a long running process while the user already went to lunch. A modal state can hinder you to react instantly to a phone call, because you first need to finish your workflow to start a new one. Modal states can be even frustrating, if you don't know what wants to have your attention, eg a Messagebox on the wrong screen, or some other modal form appearing behind some alwaysontop form.
I think of the modal form as a visually supported function. The machanism of a modal form returning a value is working like a function, just with a UI the user can interact with, it's just written differently, rather like STORE. So instead customeraccount = SearchAccount(filterparameters) you DO FORM SearchAccount WITH filterparameters TO customeraccount. It's normal, that code only continues after a function call, if the function returns back. From that perspective the VFP way to return from a modal form UNLOAD is fine, but only as long as that doesn't get in your way of doing several things in parallel.
In the case of picking one account that's not a major problem, but there can be modal forms with a longer initialising time, if looking up something more complex than just a customer account, and depending on the size of a company even that can take a while. A while, which a poweruser may use to switch to mail or something else, but not within the same application, which now is locked to the modal form.
So a major benefit would be, to change everything you can to non modal interactivity. The principle of a callback is sometimes not as easy to implement as a sequential program flow, but just because the workflow isn't thought through good enough. If you need interaction within some sequential program flow, it's better done at first or last, than in between.
If a process just sometimes need user interaction at all, it'd be nice if a user can still continue with what he does and react to the attention modal state without it being modal. To be able to do that you have to break your sequential program flow in at least two sequential parts with a continuation, so the wait state is there, but is no halt to anything else. It asks a developer to think more in terms of parallelism. But as said we are used to that already since moving away from a command prompt with interactions in the form of y/n questions, we have forms, that let you interact with several things at once. You just need to cut sequential program flows so that a interaction is not halting the application, but is just starting a non modal form and having an agreement on where and how to continue.
An agreement on a common return value property or a callback method does not hurt, just as the convention of returning a value from unload does not hurt. If you make your own parameter class for passing in and out values and offering a callback function, you already have a mechanism to decouple that for the general case. If you design a normal modal form you also agree on the number and type of parameters, so an additional agreement doesn't overcomplicate this. Providing a parameter class you make the former parameters properties, that's not making it more complicated. If you do a normal modal form you can pass back one value, so why not keep it that way, use a value property for that matter and set it. The only really new thing is, the called form needs to know what to do next, so there has to be an agreement on that. Well, a method of a parameter class can generically be used for that, and that's it. It's not a too complicated change of ways to win non modality from it.
My idea once was, like a function is specifying it's parameters and return value, a formparameter does that definition now, it could take the place of the form and call it. That's decoupling things further.
Let's start by reformulating the original DO FORM WITH param1,param2,param3 TO outputvariable to an OOP form call:
Code:
oForm = CREATEOBJECT("nonmodalform",param1,param2,param3,@outputvariable)
oForm.Show(1)
First of all, Show(1) makes the form act modal, no matter if it's designed modal or not. That's an advantage we don't actually want to make use of, but that's how far you can redo the DO FORM TO variable with a form class. A disadvantage now is, the variable passed in by reference after param3 indeed needs to be expected as another parameter of the nonmodalform, there is no TO variable construct in using form classes, the return value of unload will not arrive anywhere, you have to change that to act on the outputvariable. That's not possible with a string variable, as you can't keep the variable reference alive after the Init. So an object parameter comes to the rescue. An object lives as long as it lives, independ on how many times it's passed around. Doing that without caution it can get problematic, but I decide for it. And if I change the outputvariable to an object I can also get rid of param1-param3 and in general just pass one object, that's an optional decision, you can also stay with the param1-3, but I'll show the way with just one parameter:
Code:
oForm = CREATEOBJECT("nonmodalform",oParam)
oForm.Show(2)
Now the code execution continues, the nonmodalform acts on oParam when it's finished and that can be as simple as setting oParam.value = returnvalue or however you name the property for the return value. It could also be you additionally call a predefined method of the oParam object: oParam.callback(). In that case you may even get rid of the value property and do oParam.callback(returnvalue). So instead of returning (passively) you call something. What's cause by this is up to the developer of the oParam class and it can be different, when you inherit a generic oPAram class in different sub classes.
There's of course one thing the nonmodalform has to do with the passed in oParam: It has to store it into some form property (that again is just one idea, but a simple enough idea to program into a base form class):
Code:
*nonmodalform init:
LPARAMETERS toParam
Thisform.Addproperty("oParam",toParam)
Code:
*nonmodalform unload (or whenever you're read to return):
Thisform.oParam.value = returnvalue
* OR
Thisform.oParam.value = returnvalue
Thisform.oParam.callback()
* OR
Thisform.oParam.callback(returnvalue)
* OR (you can't do that with Unload):
Thisform.oParam.callback(returnvalue1, returnvalue2)
* OR you define a property callbackcode, which contains something to execute by macro substitution:
lcCode = Thisform.oParam.callbackcode && again just your imagination is in the way of what you can put in here.
&lcCode(returnvalue)
Since oParam can be anything, eg a child object of another form, that call can now do anything in that context. You can also continue in a third form or another application.
So isn't that also introducing more options than you have with a return value from Unload?
Bye, Olaf.