Monday, March 27, 2006

Advanced Xailer Techniques, The application starting code (I)

[Original post by José F. Giménez]

When we are working with Xailer, the most common thing we do is to crete a project, then create a main form. This creates two source code modules, one with the same project name and other with a different name, given by us, when saving the file.

/*
* Proyecto: Ejemplo1
* Fichero: Ejemplo1.prg
* Descripción: Módulo de entrada a la aplicación
* Autor:
* Fecha: 13/10/2005
*/

#include "Xailer.ch"

//-------------------------------------------------------

Procedure Main()

Application:cTitle :="Ejemplo1"
Application:oIcon :="Icono1"
TForm1():New( Application ):Show()
Application:Run()

Return

//-------------------------------------------------------

As you can see, this function (procedure) just assign some properties to the Application object, the main form is created and shown, and finally, the call toApplication:Run(), starts the program's message loop.

This code is created by the IDE, adding, removing or changing lines when ever we make a change in the project's configuration. But, some times, you may not be interested on create and show a form, may be you could be interested on write the starting code by yourself. If you try and change the code by hand, the IDE will change it, undoing all our written code.

What can we do then ?, well, the solution is as simple as to write another function, after the MAIN() with the source code we want, after that simply change the main module in the project properties, setting the new created function as the new main module, from this point, the Xailer's ID will change the line which creates and show the form for a call to our function, like this:

/*
* Proyecto: Ejemplo1
* Fichero: Ejemplo1.prg
* Descripción: Módulo de entrada a la aplicación
* Autor:
* Fecha: 13/10/2005
*/

#include "Xailer.ch"

//-------------------------------------------------------

Procedure Main()

Application:cTitle :="Ejemplo1"
Application:oIcon :="Icono1"
Empezar()
Application:Run()

Return

//-------------------------------------------------------

Procedure Empezar()
Return

//-------------------------------------------------------

Now we can write the code we want, for example to show a "splash" window (the one that holds an image), may be we can use this function to load values from a INI file, show a dialog for the user and password, etc. and after all of that, show the main form, if it exist.

In the next article you will see how easy this task can be perform and you will a "live" simple example.

Saturday, March 25, 2006

Advanced Xailer Techniques, The Application starting code (II)

[Original post by José F. Giménez]

In the previous article we studied how to change the starting code of a Xailer application, in order to execute a function where we can do anything we want. Now we're going to see what stuff should we put inside that function, to show an entry window, at the same time that the program's main Window is shown, also, and before letting the user go into the main Window, we'll show the typical validation Window, you know, the one that asks for the user name and the password.

For the entry windows, the easier way is to create a form (called TEntrada) set its property nBorderStyle to bsSPLASH. Then choose a nice image, place inside the form, and rezise the form to fit the image's size (no image is included in the sample file). Now we need that this form be shown and removed few seconds later (let's say 4). To do so, just add a TTimer component into the form, set the nInvertval := 4000 (milisecs), lEnabled := .T., and write the event OnTimer, that looks like this:

METHOD Timer1Timer( oSender ) CLASS TEntrada

::Close()

RETURN Nil

For the main form, in this sample, we are going to use a single form, without any control or component inside (TPrincipal) with its property nShowMode := smMAXIMIZE. Now let's write the Empezar() function, we will place this function (procedure) in our program's entry module, just as we studied in the previous article:

Procedure Empezar()

TEntrada():New( Application ):Show()
TPrincipal():New( Application ):Show()

Return

If we compile an run this program, now we can see that the entry window shows itself and right after, the main window is shown, but thanks to the bsSPLASH style, the entry window remains on top, and after 4 seconds it closes itself.

A single detail is missing.... you may think: "ok, but as long as the entry window is runing, the user can do anything in the main window", you are right. We are going to solve this little "issue", but first, we are going to add a third window to get the user name and the password.

For this third windows, the style will be nBorderSyle := bsDIALOG, and we are going to place the need controls to get the user name and the password, 2 Tlabel objects, 2 TEdit objects and 2 TButton objects, one for "OK" and one for "Cancel"

Please notice that the "Ok" button has the properties lDefault := .T. and nModalResult := mrOK, and the "Cancel" button has the property lCancel := .T.

Also the "Ok" button will this in its OnClick event:

METHOD Button1Click( oSender ) CLASS TIdentificacion

RETURN ::oEdit1:Value == "User" .AND.;
::oEdit2:Value == "Password"

As you can see, the only thing done is to return .T. if you write "User" and "Password" in the TEdit boxes, .F. is returned if you miss with one of these words. Now we just have to modify the Empezar() function (procedure):

Procedure Empezar()

LOCAL oEntrada

oEntrada := TEntrada():New( Application )
oEntrada:Show()

TPrincipal():New( Application ):Show()
oEntrada:ShowModal()

IF TIdentificacion():New( Application ):ShowModal() != mrOK
Application:oMainForm:Close()
ENDIF

Return

Ok, here comes the magic, be very very aware on what's going on here: First oEntrada is shown, right after, the main window, TPrincipal, is shown, both are been shown at the same time, and, as explained before, the oEntrada remains on top due to the bsSPLASH style, but the insteresting thing comes right afte the call to TPrincipal:New(Application):Show(), the oEntrada window is shown AGAIN, well this is not really truth, in spite the call to the oEntrada:ShowModal(), which it supposes to show the entry window again, what it really does, is to change the show style of the entry window into a MODAL style, the entry window is not shown twice, as you may suppose, just the showing style is changed, from no-modal, to modal, this avoids the user to take control of the main window, until the entry screen closes.

But.... what about the Identification dialog ?, well, please notice that AFTER changing the style of the entry window from non-modal, to modal, in the very next line, the TIdentificacion:New(Application):ShowModal() is launched, this means that right after the entry window closes itself (4 seconds), the new window will appear, asking for user name and password and as it has the Modal style set, the user still won't be able of touch the main window until he gives the proper data. Once pressed the OK button and if the data is correct, the main window will catch the focus.

That's all by now, as you can see, is very easy to control the application start in Xailer, and you don't have to write almost any line of code.

The project source can be downloaded from here

Friday, March 24, 2006

Advanced Xailer Techniques: X classes and T classes

[Original post by José F. Gímenez]

Many of you know that Xailer's class hierarchy is extensive and it also has the most of the classes duplicated in "X" classes and "T" classes, basically the class hierarchy is something like this:

XComponent -> TComponent -> XWinObject -> TWinObject ->
XControl -> TControl -> etc.

In the class hierarchy published in the help file this duality is not shown, you will only see the "T" classes, but the "X" classes also exist and they are the ones that hold all the code, so the "T" classes remain absoluty empty. For example, the TControl class is declared like this:

CLASS TControl FROM XControl
ENDCLASS

Now what ?, why do we want to have duplicated classes ?, what's the goal with this ?

If you don't know how to take advantage of this feature, this can be useless. But, as you will see as follows, it has enormous benefits.

In the past, I used to develop my windows applications with a well-known xBase library that you may already known and use. You also know that under this well-known-library if you want to correct a bug, modify certain behave of a control or perform any other modification, you must to work modifiying the original class source code, and then include the modified source in your list of .PRG files, or compile the code into an OBJ, and place it inside the original LIB o inside any other library, all a mess. At the end I had a library of patches as big as the original lib, and whenever a new release of the original library came, I was scared.

Using X classes and T classes you don't have to worry. Let's suppose that the SetSel() method of the TEdit class has a bug, to correct it, we just have to add the correction in any module of our application (or in a library of our own):

CLASS TEdit FROM XEdit
METHOD SetSel( nStart, nEnd ) INLINE ;
::SendMsg( EM_SETSEL, nStart, nEnd )
ENDCLASS

That's all. If you notice, we just simply had declared a new class TEdit inherited from XEdit, and in this new class we had overwritten SetSel() method with a new corrected version. We don't need the original source of the class, either with any new release, nor we have a lot of stand alone modules with corrections.

This feature can be taken far away, not only for bug correction. Now let's suppose we want that all the Edit controls of our app were shown in yellow background when catching the focus, of course you can modify the nClrPaneFocus property of every single Edit control (a damn ammount of job). But it's easier to apply this techinique this way:

CLASS TEdit FROM XEdit
PROPERTY nClrPaneFocus INIT RGB( 255, 255, 192 )
ENDCLASS

Also, as TMaskEdit, TEditBtn, TDateEdit, TDBEdit, TDBMaskEdit, etc. are inherited directly or indirectly from TEdit, all these controls will have the very same behave.

Now let's think we want to create a demo of our application, limited by dates, that cannot be greater than 31-12-05, this code snipet can do the job:
CLASS TMaskEdit FROM XMaskEdit
METHOD Valid( oNextCtl )
ENDCLASS

METHOD Valid( oNextCtl ) CLASS TMaskEdit
LOCAL uRet := Super:Valid( oNextCtl )
IF ::cType == "D" .AND. Day( ::Value ) > 0
IF DTOS( ::Value ) > "20052131"
MsgStop( "Date not allowed in demo version." )
RETURN .F.
ENDIF
ENDIF
RETURN uRet

Well these are just some samples of this technique, but posibilities are enourmous and they are only limited by your imagination.

Thursday, March 23, 2006

Advanced Xailer Techniques, What happen during form creation ?

[Original post by José F. Gimenez]

Many of you are using OnInitialize event in the forms to do some control initialization, asigment of initial values, etc... But some of you are using it because someone has told you to do so, and don't realize really why you should do it.

In this article I'll try to clarify all the steps that happen during the process of forms creation, including the moment in which some events are launched. In this way all of you will notice where and when to use one event or other.

Back to the basics.... New(), Create(), Destroy() and End(). These four methods are the ones implied in Xailer's controls creation and destruction.

  • New() is the method which instances and initialize the control container. You just have to run the class function to instance the object, but the call to New() method, even is a tradition in xBase, also is used to indicate the "parent" of the control, and perform certain initialization of the control.
  • Create() This is the method that really creates the control. Between New() and Create() all the control's needed properties are asigned, so, at the very creation moment, all the properties are already asigned with a value given by ourselves, or with the default value.
  • Destroy() As you can imagine, this method destroys the control, but it doesn't free the resources used, nor destoys the container object.
  • End() is he one which frees all the resources used by the control (memory, images, etc.) On the other hand, remember that under xbase, you cannot destoy the object directly from himself, but notice all the time that once End() executed, the object cannot be used again.

So far so good, these applies to controls, but in the case of non-visual components, New() and Create() can be used all alike, due to the most of these components don't have an equivalent with any Windows object or control.

Talking about forms, there's also an important difference: You NEVER call Create() directly, you just need to call New(), because CreateForm() method is the one which calls Create(), we will be back on this later.

Ok, once we had clarified which methods are implied in forms and controls creation, let's see, step by step what happen when you execute a line like:

TForm1():New( Application ):Show()

New() intances and initializes the object. In the case of forms, it internally calls CreateForm() method, this method is written in the .XFM file. This .XFM file is just a source code file with the same name of the .PRG file, .XFM stands for "Xailer ForM". The main reason cause this file is separated from the .PRG is because it can be handle by the IDE in a easier way, after all, we don't have to modify it by ourselves, we just have to know what is it, and what does it have, and if you are curious enough and edit it, you will see something like this:

METHOD CreateForm() CLASS TForm1

Super:CreateForm()
::SetBounds( 220, 120, 450, 300 )
::cText := "Form1"
::oFont := TFont():Create( "MS Sans Serif", 8, 0, 400 )
::nClientWidth := 442
::nClientHeight := 266
::Create()

::oFileOpenDlg1 := TFileOpenDlg():Create( Self )

WITH OBJECT ::oMemo1 := TMemo():New( Self )
:SetBounds( 0, 0, 442, 266 )
:nAlign := alCLIENT
:Value := ""
:Create()
END

RETURN Self

As mentioned before, the XFM file just contains the CreateForm() method. Please notice that it also calls Super:CreateForms() and ::SetBounds(), and right after these calls, some properties of the form are asigned and then ::Create() is called.

But here is where the first event comes: OnCreate. Just in the call to Create() method this event is launched, as you can notice, in spite the form is created, none of its components or controls are created yet, so if you need to do something in this point, you can use OnCreate event, but be aware that at this point you cannot modify any component, simply because they still don't exist, and you will get an error.

Right after, the creation of the TFileOpenDlg component comes. As stated before, you don't need to call New() and Create(), because it is instanced and created when you call Create().

Please notice that non-visual components should be created BEFORE to create the controls, the reason is simply, some of these components are used by the controls, for example a TDbfDataSource or a TDbfDataSet.

And finally we find the creation of the TMemo() control. In this case we need to instance the object calling the New() method, asign its properties, and finish it calling to Create(), to create the control, at this moment, the event OnCreate is launched, so you can use it if needed.

Once finished with the CreateForm(), the form al all its controls are created, and here is when OnInitialize event is launched, so when you need to perform an initialization task over an already created control, you can use this event. At this point, the form still is not visible.

After that, the events that are launched are: OnShow, when the form is visible by calling the methods Show() or ShowModal(), and OnActivate, when the form got the keyboard focus. These two events are not only launched when the form is created, they will be launched whenever needed.

Well, with this lesson you have already learned all the steps until the form is visible and got the focus. If you have learned this, you will now have learned a very important part of how Xailer works.

Wednesday, March 22, 2006

Advanced Xailer Techniques, Drag and Drop operations

Xailer let you to perform 3 kinds of different Drag & Drop operations:

  • Dragging files from the Windows explorer into the Xailer program
  • Dragging elements inside a TreeView
  • Dragging controls over other controls
The most basic drag & drop operation is the drag of files from within the Windows Explorer into your Xailer Application, its functionality is limited because only let you "to send" a file name or a list of file names to a control, and let it to process them. It works in a very easy way, just set the lDragAcceptFiles property to TRUE, and finally catch the event OnDropFiles, in such event, we will recieve an array with a list of the file names and maybe we could handle it like this:

METHOD ListBox1DropFiles( oSender, aFiles, aPoint ) CLASS ...

Aeval( aFiles, {|v| oSender:AddItem( v ) } )

RETURN Nil


In this sample, we are adding into a TListBox control, the name of files dragged into the control.

The second Drag & Drop system in Xailer is used to perform drag & drop operations among elements inside a TreeView control, you just have to set lDragDropItem property to TRUE. However, the TreeView control has some other events to give more flexibility and power to this kind of operations:

  • OnBeginDragItem: This event is launches whenever you start a Drag&Drop operation between two TreeView items, recieves as a second parameter, after oSender, the TTreeViewItem object which launched the event.
  • OnDragOverItem: This event is launched when, inside a Drag&Drop between items, the cursor is over a certain item. It recieves as a second parameter, after oSender, the TTreeViewItem objetct where the cursor is over. If it returns FALSE, you will see the "prohibited" cursor, indicating that is not possible to perform the D&D operation over that item.
  • OnEndDragItem: This event is launched when a D&D operation is finished between items. It recieves as a second parameter, after the oSender object, two TTreeViewItem objects, one called oItemFrom, the one that launched the event, and other called oItemTo, the object where the mouse was dropped. If it returns NIL, o this event is not captured, oItemFrom will move right after oItemTo. Catching this event, is possible to reject the Drop operation or, instead of move the item after the oItemTo, it moves to a child branch of the oItemTo.
The third way to perform D&D opeartions in Xailer, allows you to do it between Xailer controls, even if these controls are in different forms. To acomplish this task, Xailer has these events, at TControl class level, so they are inherited to all the classes from it:

  • OnBeginDrag: Is launched when there's a request to begin a D&D operation. If returns .T., this means that the D&D opearation is allowed and you notice it inmediatly because the mouse cursor changes its shape.
  • OnDragOver: Is launched when the mouse cursos moves over a control, and a D&D operation has been started. It recieves as a second parameter, after oSender, the control which started the D&D operation. If returns .T. this means that the D&D operation can be performed, and you notice it because the mouse cursor shape changes to the oCursorDropYes. If this event is not trapped, or returns a value different of TRUE, the mouse cursos shape change to oCursorDropNo.
  • OnEndDrag: This event is launched when you release the mouse left button over a control, and previously a D&D operation has been started. It recieves as a second parameter, after the oSender, the control which starte the D&D operation.

Please notice that the second and third ways cannot be used together, this means that you cannot perform a D&D between controls and at the same time do a D&D between TreeViewItems.

You can find a sample of D&D in the directory \Samples\DragDrop showing you the 3 ways at the same time.

Advanced Xailer Techniques, Moving from other enviroments

[Original post by José Lalín]

Many people have told me they are very comfortable working with Xailer, they use it, or going to use it, for new projects because they don't want to re-write already finished and working applications.

For sure, no programming tool let you to move automatically from one enviroment to other: in the most of the cases, we can rescue the code that handle the data layer, but you always have to to make changes in the interface to fit your code to match the features of your new programming tool.

Comming from this, in the Xailer team we thought we could make something about, it and we decided to include an option in the IDE's menu that let you to import previously designed dialogs in Workshop.

Note: The import dialogs feature only works under .RC files, if you have a DLL or a previously compiled .RES file, you should export them to the .RC format, the most of the resource editors, including Workshop can do it for you with some mouse clicks.


To see how it works, let's go to the main menu, select TOOLS / IMPORT DIALOGS




Then you will see a form like this:



This form lets us, among other things:
  • Select the .RC file to be imported
  • To choose which dialogs we want to import
  • If we want to see how the form is creating while importing
  • Configure which custom control has an equivalent in Xailer


A simple sample of how it works: Let's suppouse we have a dialog with a Hernan's TWBrowse control, and we use it to display an array, and we also have a Manuel Mercado's TSButton.

Pressing the "Configure" button, we can easily make that a dialog defined TWBrowse turns into an Xailer's TArrayBrowse, and a TSButton to become into a Xailer's TBtnBmp.



Once imported, we will have a new FORM in our Xailer project with all the controls used in the original dialog, and we only have to do some minor stuff using the Objects Inspector and some mouse clicks.

I kindly invite you to play around a little with this option, and you will see how easily you can become an old dialog, into a powerful Xailer form.

Welcome to Sailing with Xailer

Hi every one and be welcome to "Sailing with Xailer"

We at CiberTec are very happy to start this new blog fullfill with Xailer's information.

Xailer is the most powerful XBase based development tool actually, It's really a dream came true because Xailer has all the tools you always wanted, full integrated in a single, yet elegant IDE.

Xailer features go far away you are used to in the XBase world, based in the powerful xHarbour compiler, Xailer offers you all the needed tools for mission critical projects integrated in one single place, and let you to use all the Clipper / Xbase knowledge you already have.

Our goal in this blog is to share with the non-spanish speakers Xailer comunity all the information and articles published regarding to Xailer to help you to speed your learning curve.

In this first stage we will be translating the articles written in different forums, please feel free to let us know what you want to know, we are here to help you.

So, let the fun begin......