If you starts it 5 times you have 5 windows with invoices in the windows taskbar.
You don't need to create 5 independent windows with taskbar presence. A thread is just a capsuled process where something happens inside. It has connections to the application framework, to global objects, statics and, of course, the database. The advantage of a thread-based model is that the app doesn't need to care whether a table is already opened outside the thread or not, since every thread comes with it's own workspace and workareas. If some logic is created, like a dialog showing some data, it can easily be moved into a thread and used x times with no other changes. If the application is able to deal shared tables, it can be multi-threaded aswell, since this is almost the same.
But it's not a must to place the logic running in a thread in an own dialog (window). A threaded dialog can have a parent in another thread. If you do something like this:
Code: Select all
@ 0,0 DSAY .. GET ...
... more dialog elements
@ 30,0 DCSAY '' SIZE 8,1 OBJECT oTime
DCREAD GUI EVAL {||SetTimerEvent(100,{||ShowTime(oTime)})}
FUNCTION ShowTime(oTime)
oTime:SetCaption(Time())
RETURN NIL
You have a new thread where "ShowTime()" runs. "oTime", which is a static on the main dialog, is known by that thread. It updates the caption of this object from inside a different thread. This is possible since there is only one UI-thread which manages the screen output. It doesn't care for from where it's called from. If it's able to access the object which is to be updated, it updates the object.
Something quite similar could be done with complex dialogs. You can place elements on a dialog and use them as parents for other dialogs created in different threads. Look at this simple code:
Code: Select all
#include "dcdialog.ch"
#include "thread.ch"
#pragma library ("dclipx.lib")
FUNCTION Main()
LOCAL GetList := {}, aTabs := Array(3), nCounter := 0, aThreads := Array(3)
@ 0,0 DCTABPAGE aTabs[1] CAPTION 'Test 1' SIZE 115,25
@ 0,0 DCTABPAGE aTabs[2] CAPTION 'Test 2' RELATIVE aTabs[1]
@ 0,0 DCTABPAGE aTabs[3] CAPTION 'Test 3' RELATIVE aTabs[2]
@ 27,0 DCPUSHBUTTON CAPTION 'New dialog' SIZE 12,1 ;
ACTION {||aThreads[ThreadCounter()+1] := Thread():New(),;
aThreads[ThreadCounter()+1]:Start({||NewDialog(aTabs,ThreadCounter(1))}),SetAppFocus(aTabs[ThreadCounter()])} ;
WHEN {||ThreadCounter() < 3}
DCREAD GUI FIT ADDBUTTONS TITLE 'Test'
RETURN NIL
PROC AppSys() ; RETURN
FUNCTION ThreadCounter(nInc)
STATIC nThreadCount := 0
IF PCount()>0
nThreadCount += nInc
ENDIF
RETURN nThreadCount
FUNCTION NewDialog(aTabs,nCounter)
LOCAL GetList := {},cText := Space(30)
@ 2,1 DCSAY 'Test '+Str(nCounter,1,0)+':' GET cText
DCREAD GUI PARENT @aTabs[nCounter]
RETURN NIL
It shows 3 tabpages with nothing on them at start. If you click "New dialog", the next free tabpage is used as the parent for a new dialog running in a different thread. This is not very complex, it might be not very useful, but it works. The four dialogs are completely independent.