Friday, April 28, 2006

A little of Xailer without using the IDE

Original post by Rene Flores

I'm about to finish my first application fully written with XAILER, and, to be honest, I like it a lot !



Apart of how nice is the application, I'm very surprised on how you avoid a lot of "hard coding job" using the Xailer's IDE, saving you to write hundreds and hundreds of source code lines, that thanks to the IDE you don´t have to write them by yourself, specially talking about Textbox validations ("Gets" talking in Xbase) that I'm using a lot.

However there are certain things that you cannot do with the IDE, like the following programming challenge.

For my application (an accounting program) I needed a form with a folder, this is the easy part, but the folder had to have a variable number of tabs, (not more than 13), you can have 2, 3, 10 or 13 tabs, you really don't know how many tabs will the folder have, this value comes from an internal calculation, inside of the tab, I had to put a DBFBrowse along with a StatusBar, and here is where the challenge started.

To be aware on how Xailer creates the forms, my frist attempt was to create a form, using the IDE, drop a folder in, and add some tabs, all using the visual designer, I saved the form and then I opened the XFM file, this file is where Xailer storages all the from creation stuff, and here is where I found what I was looking for.

My first challenge was to make the folder to have a dinamic number of tabs, you cannot use the IDE for this task because you always have to indicate a fixed number of tabs, so far so good, What I did, was to create a MDI form, and just dropped a ToolBar inside:





The rest of the code was hand written, but believe me, it was too simple if you know a little bit about how Xailer builds controls.

First, I added a FOLDER component in the class definition, I wrote this directly in the source code:


CLASS Polizas FROM TForm

COMPONENT oReBar1
COMPONENT oFolderPolizas

DATA aFolderPages AS ARRAY(13)
.....

....
....


I wasn't able to create the folder in the IDE (just the folder, without tabs), and after that, add new tabs, for example in the ON INITIALIZE event, no way, because at the moment of creation, a call to the method ::Create() is done, and after that call you cannot manipulate the control anymore, later on, you will notice why the ::Create() method is so important.

After noticed that, I decided to use the ON INITIALIZE event, and write the code to build the folder, along with the needed tabs.

WITH OBJECT ::oFolderPolizas := TFolder():New( Self )
:SetBounds( 64, 44, 200, 160 )
FOR nContador := 1 TO LEN(aTitulos)
WITH OBJECT ::aFolderPages[nContador] := TFolderPage():New(::oFolderPolizas)
:cText := aTitulos[nContador]
:Create()
END
NEXT
:nAlign := alCLIENT
:nIndex := 1
:Create()
END


Let´s take a look into the code:

First, we need to instance the Folder objet, the call to ::SetBounds() is not really needed because later on we'll call the ::nAlign, once created the folder, we need to add the tabs, this is where fun begins, the number of tabs is stored in the aTitulos array, in this way, we can fill the array with the needed tab's titles and I will get the exact numbers of tabs I want.

Since the tabs creation is dynamic, I needed some way to make reference to them later. IF I had created the tabs in the IDE, the IDE had given them a variable name such "oFolderPage1, oFolderPage2...." etc. So, I noticed that it was easier and faster to create a new data in the class called aFolderPages, to store the pages in every element of the array instead of having 13 diffent data names.

There's something very important you should notice about Xailer's controls, and maybe this is one of the most confused terms related to the controls manipulation, the so called ::Create() method, which is the one that creates the control. In my firsts attemps to solve my problem, right after to instance the folder, I was calling the ::Create() method, and after that, I added the tabs.... WRONG !!!!, you have to instance the folder first, then add the tabs (you need to call the ::create() method of the tabs neither), and right after adding all the tabs you need, you have to call the ::Create() method of the folder, as you can notice in the previous code snipet.

Finally, and to make the folder fit in the client area of the form, the data ::nAlign is asigned to alCLIENT, so, when we change the form size, the control adjust itself to its container.

Done, now I have my dynamic folder, and now.... how can I include more controls ?, I needed a DBFBrowse and a Statusbar inside every tab, so in the same way I built the folder and its tabs, the same way I used to create the browses and the the status bars, using a FOR:

FOR nContador := 1 TO LEN(aTitulos)
WITH OBJECT ::aStatusBar[nContador]:=TStatusBar():New(::aFolderPages[nContador])
:SetBounds( 0, 243, 442, 22 )
:cText := "Total de pólizas: ";
+ALLTRIM(STR((aPublics[nContador+10])->(AdsKeyCount())))

:Create()
END
WITH OBJECT ::aBrowPoli[nContador]:= TDbfBrowse():New(::aFolderPages[nContador])
:SetBounds( 0, 0, 350, 166 )
:nAlign := alCLIENT
:nColDividerStyle := blFORECOLOR
:nMarqueeStyle := bmHIGHLROW
:lAllowEdit := .F.
:lAllowColSwapping := .F.
:lAllowColHiding := .F.
:lRecordSelector := .F.
:Create()
:SetDbf(aPublics[nContador+10],;
{"numpoli2","tipopoli2","fecha","concepto","total"})

:lFooter := .T.
nSuma := 0
(aPublics[nContador+10])->(DBGOTOP())
(aPublics[nContador+10])->(DBEVAL({|| nSuma += total}))
(aPublics[nContador+10])->(DBGOTOP())
:aCols[5]:cFooter := TRANSFORM(nSuma,"999,999,999.99")
:OnDblClick := "PoliDeta"
END
NEXT


Why did I create the folder and the tabs and then the DBFBrowse and the StatusBar ?, Can you do it in single step ?, That was what I tought the first time, because if in a single FOR-NEXT I could build all the needed controls, I save time.... really ?... no way man, and here comes the ::Create() method again. In order to add new controls to the tab, the tab has to exist, this may seen obvious because we have created the tab and called its ::Create method, but... YOU CANNOT ADD NEW CONTROLS, because the tab is not really created, the tab is created and exists as soon as its container is created, in this case the folder is not created until all the need tabs have been added. The fastest way to solve this problem is to have two separate routines, one to create the folder and the tab, and other to create the DBFBrowse and the Status bar.

Please notice: In Xailer you have to create the controls in the order you are going to need them at run time, first, because they use a determined area inside the client are of its container, and second, because the creation order will be the same order for TABSTOP, in my case, first I created the Staus bar, and after that, the DBFBrowse for every tab, you cannot do it in different order because at the moment of displaying the form the DBFBrowse will be painted OVER the StatusBar, so the bar will be hidden behind the browse until you resize the form.

To create the browse was a breeze, at the begining of my program I defined a PUBLIC array with all the alias of all the databases used in muy program, so I asigned the alias to the browse in a very fast way. Why didn't I used a DataSet... well my knowlege of DataSets was very poor, so I decided go into Xailer using the old Clipper style for database handling.

Finally I needed a footer in the browse with the sum of all the movements of the documents, so, once again, "a la Clipper", a DBEVAL() and a codeblock made the job.

The result is superb:





For a full size image, click over them.

This was one of my first Xailer challenges, and I really solved very fast, just a little of study of the product, some experiments and here is the result. To be honest, the programming in Xailer likes me a lot.

0 Comments:

Post a Comment

<< Home