By default, uniGUI applications are standard web applications working in asynchronous mode. This means the server doesn't need to block a session thread while waiting for a user response coming from the client.
Asynchronous Mode
How to use ShowModal in this mode?
Let's use a very simple example. We will create a form for entering a person name with buttons for accepting or canceling the request. Another form will use the previous form for updating its own internal field FFullName.
This form will return the entered full name or 'John Doe' if the full name was empty. Its result will depend on pressing the button OK or Cancel (or closing the form). How we use it?
What happens here?
1
Step 1
We start by pressing the button GetFullName.
2
Step 2
As soon as we call UniForm3, an instance of the form TUniForm3 will be created and returned.
3
Step 3
The call to ShowModal will popup the form and it will behave like a client-side modal form (meaning that no further interaction is allowed outside of this form).
4
Step 4
However, server-side, the program will continue, which in this case means that it will exit the OnClick event handler.
5
Step 5
At this point, the server finished the previous request (that is, the OnClick event triggered client-side by the user).
6
Step 6
When the user finally clicks any of the buttons (Ok or Cancel) or closes the form, another request will go to the server: handle the ShowModal result for that form.
7
Step 7
The server will identify the user session, and will execute the anonymous method which still keeps the reference to UniForm3 (standard behavior for anonymous methods).
8
Step 8
Once the anonymous method is executed, the form UniForm3 will be available for disposal (its lifetime being managed by the uniGUI framework).
Synchronous Mode
It is always preferred to create the web application in asynchronous mode, but when migrating old VCL applications, the developer could find scenarios which couldn't be implemented or that will make the task too difficult. If necessary, uniGUI can be forced to work as a typical VCL application.
If the developer needs to reproduce the synchronous mode available in VCL applications, the property MainModule.EnableSynchronousOperations must be set to true.
ShowModal
How to use ShowModal in this mode?
This is the same code we typically use for VCL applications. It also stops the execution after calling ShowModal until a result is returned. This kind of code requires spawning a thread in the server which will be waiting for the client request/answer. As you can expect, this kind of applications consumes more resources than a standard web application. Fortunately, uniGUI uses an internal thread-pool to minimize thread usage, but for each waiting modal window a thread will be consumed. After the modal window is closed, the waiting threads will be returned to the thread pool for future usage.
In addition to ShowModal, uniGUI allows to execute synchronous operations in other scenarios. Let's examine some demos.
Blocking Modals
There is one demo illustrating blocking modals, "BlockingModals".
BlockingModals
The next code shows how each function is now a blocking call like it was in VCL.
Progress Bar
Demo "SyncClientUpdate-1" shows how to update a progress bar while executing a lengthy task.
SyncClientUpdate-1
Once the user clicks the button Start, this is the kind of code achieving the desired behavior:
The call UniSession.Synchronize forces an immediate update. Passing an argument allows to synchronize at a controlled period (every some milliseconds).
Synchronous processing using ShowMask
Demo "SyncClientUpdate-3" executes a sequence of tasks with status updates and using ShowMask for avoiding user interaction with other visual controls.
SyncClientUpdate-3
The code for the Start button is:
Synchronize waiting for events or timeout
Demo "SyncClientUpdate-5" shows the difference between Synchronize(Wait : boolean) and Synchronize(Delay : integer).
SyncClientUpdate-5
Pressing the button "Start No Wait" will move the UniPanel1 to the bottom right corner 5 pixels every 200 milliseconds (showing the movement).
Pressing the button "Start" will accomplish the same, but each step will happen only when some client-side event is detected (for example, pressing the dummy button "Press!" or resizing the form).
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, uniGUITypes, uniGUIAbstractClasses,
uniGUIClasses, uniGUIForm, uniGUIBaseClasses, uniButton;
type
TUniForm1 = class(TUniForm)
btnGetFullName: TUniButton;
procedure btnGetFullNameClick(Sender: TObject);
private
{ Private declarations }
FFullName : string;
public
{ Public declarations }
end;
function UniForm1: TUniForm1;
implementation
{$R *.dfm}
uses
MainModule, uniGUIApplication,
Unit3;
function UniForm1: TUniForm1;
begin
Result := TUniForm1(UniMainModule.GetFormInstance(TUniForm1));
end;
procedure TUniForm1.btnGetFullNameClick(Sender: TObject);
begin
UniForm3.ShowModal
(
procedure (Sender: TComponent; AResult: Integer)
begin
if TModalResult(AResult) = mrOk then
FFullName := (Sender as TUniForm3).FullName;
end
);
end;
end.
procedure TUniForm1.btnGetFullNameClick(Sender: TObject);
begin
if UniForm3.ShowModal = mrOk then
FFullName := UniForm3.FullName;
end;
procedure TMainForm.btnShowModalClick(Sender: TObject);
var
M : TModalResult;
begin
M := UniForm1.ShowModal;
case M of
mrOK : ShowMessage('OK');
mrCancel : ShowMessage('Cancel');
mrNone : ShowMessage('None');
end;
ShowMessage('Done, ' + UniForm1.UniEdit1.Text);
end;
procedure TMainForm.btnMessageDlgClick(Sender: TObject);
var
Res : Integer;
begin
Res := MessageDlg('Test', mtConfirmation, mbYesNoCancel);
case Res of
mrYes : ShowMessage('Yes');
mrNo : ShowMessage('No');
mrCancel : ShowMessage('Cancel');
end;
end;
procedure TMainForm.btnPromptClick(Sender: TObject);
var
sResult : string;
begin
if Prompt('Please type something?', 'Value',
mtInformation, mbYesNoCancel, sResult, True) = mrYes then
ShowMessage('Result: ' + sResult);
end;
procedure TMainForm.btnNestedCallsClick(Sender: TObject);
var
sResult : string;
begin
if UniForm1.ShowModal = mrOK then
if Prompt('Please type something?', '',
mtInformation, mbYesNoCancel, sResult, True) = mrYes then
if MessageDlg('Continue?', mtConfirmation, mbYesNoCancel) = mrYes then
ShowMessage('Result: ' + sResult + ' ' + UniForm1.UniEdit1.Text );
end;
procedure TMainForm.btnNestedForms1Click(Sender: TObject);
begin
if UniForm1.ShowModal = mrOK then
if UniForm2.ShowModal = mrOK then
if UniForm3.ShowModal = mrOK then
if UniForm4.ShowModal = mrOK then
begin
ShowMessage('All shown!');
end;
end;
procedure TMainForm.btnNestedForms2Click(Sender: TObject);
begin
if UniForm5.ShowModal() = mrOK then
ShowMessage('Completed');
end;
procedure TMainForm.btnFileUploadClick(Sender: TObject);
begin
if UniFileUpload1.Execute then
begin
ShowMessage('File upload completed.' +
^M^M'Filename: ' + UniFileUpload1.FileName +
^M^M'Temporary file is located under:' +
UniFileUpload1.CacheFile);
end;
end;
begin
FCancelled := False;
UniProgressBar1.Min := 1;
UniProgressBar1.Max := MAX_FILES;
btnStart.Enabled := False;
btnCancel.Enabled := True;
// Reset Progressbar and Label
UpdateClient(0);
// Initial refresh before entering the loop.
// (Progressbar and Label will be refreshed)
UniSession.Synchronize;
N := 0;
try
for I := 1 to MAX_FILES do
begin
// Refresh the client at "X_INTERVAL" intervals
if UniSession.Synchronize(X_INTERVAL) then
UpdateClient(N);
// Check if operation is cancelled.
// (Either when Cancel button is pressed or Form is closed)
if FCancelled then Break;
// perform some tasks here
CreateDummyFile(I);
Inc(N); // +1 number of created files
end;
UpdateClient(N);
finally
btnStart.Enabled := True;
btnCancel.Enabled := False;
end;
end;