MDI
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
MDI
Hola compañeros!
A ver quién me puede echar una mano. En un ambiente MDI, aparte de los alias de las bases de datos, qué más necesitamos controlar?. Imagino que haya que tener un control sobre variables, objetos, ventanas...
Alguna lectura recomendad? algún manual - tutorial?
Gracias!
A ver quién me puede echar una mano. En un ambiente MDI, aparte de los alias de las bases de datos, qué más necesitamos controlar?. Imagino que haya que tener un control sobre variables, objetos, ventanas...
Alguna lectura recomendad? algún manual - tutorial?
Gracias!
- Antonio Linares
- Site Admin
- Posts: 37481
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Contact:
Re: MDI
David,
Aparte del control de los Alias, es importante recordar que las ventanas MdiChild no detienen la ejecución, por lo que se sale de la función en donde se crean, y esto hace que las variables locales se destruyan, salvo que se usen en codeblocks (FWH usa automaticamente codeblocks en las cláusulas ACTION...)
Por ejemplo, si cerrasemos una DBF despues de activar una ventana MDICHILD, la DBF se cerrará de inmediato y no se esperará a que se cierre la ventana MDICHILD.
Además de esto es conveniente revisar la documentación de las Clases asociadas: TMdiFrame, TMdiClient y TMdiChild:
http://wiki.fivetechsoft.com/doku.php?i ... _tmdiframe
http://wiki.fivetechsoft.com/doku.php?i ... tmdiclient
http://wiki.fivetechsoft.com/doku.php?i ... _tmdichild
Aparte del control de los Alias, es importante recordar que las ventanas MdiChild no detienen la ejecución, por lo que se sale de la función en donde se crean, y esto hace que las variables locales se destruyan, salvo que se usen en codeblocks (FWH usa automaticamente codeblocks en las cláusulas ACTION...)
Por ejemplo, si cerrasemos una DBF despues de activar una ventana MDICHILD, la DBF se cerrará de inmediato y no se esperará a que se cierre la ventana MDICHILD.
Además de esto es conveniente revisar la documentación de las Clases asociadas: TMdiFrame, TMdiClient y TMdiChild:
http://wiki.fivetechsoft.com/doku.php?i ... _tmdiframe
http://wiki.fivetechsoft.com/doku.php?i ... tmdiclient
http://wiki.fivetechsoft.com/doku.php?i ... _tmdichild
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
Re: MDI
Antonio,
Gracias por la info. Las clases no son mi fuerte, aprovecho para recordarte que tenemos ese asuntillo pendiente, así que en cuanto me organice y haya avanzado en FW más, hablo contigo para seguir adelante.
Por otro lado quería hacer una preguntita a ver cómo resuelve la gente este problema. Estoy configurando un entorno para hacerlo MDI, controlando varias cosas, pero a la hora de controlar las ventanas, tengo la duda. En principio controlo alias, variables y objetos con un array tridimensional de la forma:
El primer orden, me especifica si es variable/objeto, el segundo para alias. El segundo orden, lo sustituyo por el nombre (por ejemplo):
Y el tercer orden, es donde viene la madre del cordero, lo quiero sustituir por la ventana en concreto. Si utilizo una variable para controlar el número de la ventana, según vaya aumentando porque se abran más ventanas, va variando y no la puedo usar para hacer referencia a un determinado objeto de esa ventana. Por ejemplo, si hago referencia al objeto oSayEmpr de una ventana, sería así:
Pero si oWnd es una variable, ir´ña cambiando cada vez que se abra una nueva ventana y siempre estaríamos haciendo referencia al oSayEmpr de la última ventana abierta. No sé si me explico con claridad.
Cómo se puede solucionar este inconveniente?
Gracias!
Gracias por la info. Las clases no son mi fuerte, aprovecho para recordarte que tenemos ese asuntillo pendiente, así que en cuanto me organice y haya avanzado en FW más, hablo contigo para seguir adelante.
Por otro lado quería hacer una preguntita a ver cómo resuelve la gente este problema. Estoy configurando un entorno para hacerlo MDI, controlando varias cosas, pero a la hora de controlar las ventanas, tengo la duda. En principio controlo alias, variables y objetos con un array tridimensional de la forma:
Code: Select all
#define aObjVar(i,j) aArrayVars[1][i][j]
#define aAlias(i,j) aArrayVars[2][i][j]
Code: Select all
#define oWndChild 1
#define oDlg 2
#define oSayProc 3
#define oSayEmpr 4
#define oSayFecha 5
Code: Select all
aObjVar( oSayEmpr, nWnd )
Cómo se puede solucionar este inconveniente?
Gracias!
- Antonio Linares
- Site Admin
- Posts: 37481
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Contact:
Re: MDI
David,
Si necesitas tener controladas y accesibles las ventanas MDICHILD lo más sencillo es que crees una Clase:
Y creas un objeto publico de esta forma:
asi en cualquier parte de tu aplicación puedes acceder a oMiAplicacion:oWndFacturas o lo que necesites
No se si es lo que quieres, pero es la manera mas simple y facil
Si necesitas tener controladas y accesibles las ventanas MDICHILD lo más sencillo es que crees una Clase:
Code: Select all
CLASS TMiAplicacion
DATA oWndClientes
DATA oWndArticulos
DATA oWndFacturas
...
METHOD New() // desde donde construyes todo
ENDCLASS
Code: Select all
public oMiAplicacion := TMiAplicacion():New()
No se si es lo que quieres, pero es la manera mas simple y facil
-
- Posts: 330
- Joined: Fri May 25, 2007 9:06 pm
Re: MDI
QUE ESTAS INTANTANDO HACER AMIGO::
NO TE ENTENDI..
PODES PONER UN CODE DE LO Q QUIERES HACER::
HABER SI PODEMOS AYUDARTE
NO TE ENTENDI..
PODES PONER UN CODE DE LO Q QUIERES HACER::
HABER SI PODEMOS AYUDARTE
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
Re: MDI
Antonio,
No es eso lo que busco concretamente, pero me sirve ese ejemplo para no usar public's
A lo que me refiero, es que cuando hago referencia a un objeto de una ventana concreta, para indicarle qué ventana es, no puedo usar variables:, porque nWnd cada vez que se abre una ventana nueva, toma un valor distinto, que se le va asignando cada vez que se abre una ventana nueva:
Abrimos una ventana, los objetos de la ventana1, se puede referenciar perfectamente bien con aObjVar( oSayEmp, nWnd ). Cuando abrimos una ventana2, viene el problema, nWnd toma el valor de 2 y a partir de ahí, toda referencia a objetos hecha en la ventana1 no es correcto, puesto que se referencia con aObjVar( oSayEmp, nWnd ) y nWnd ya ha tomado el valor de 2.
Siento explicarme tan mal, entiendo que el problema es no poder o no saber explicarlo mejor. A ver si ahora quedó más claro. Lo que quería saber es cómo solucionan los demás ese problema, para no limitar las ventanas a un número fijo determinado.
Gracias!
No es eso lo que busco concretamente, pero me sirve ese ejemplo para no usar public's
A lo que me refiero, es que cuando hago referencia a un objeto de una ventana concreta, para indicarle qué ventana es, no puedo usar variables:
Code: Select all
aObjVar( oSayEmp, [b]nWnd[/b] )
Code: Select all
if nWnd = nil
nWnd := 1
else
nWnd ++
endif
Siento explicarme tan mal, entiendo que el problema es no poder o no saber explicarlo mejor. A ver si ahora quedó más claro. Lo que quería saber es cómo solucionan los demás ese problema, para no limitar las ventanas a un número fijo determinado.
Gracias!
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
Re: MDI
mantonio_08,mantonio_08 wrote:QUE ESTAS INTANTANDO HACER AMIGO::
NO TE ENTENDI..
PODES PONER UN CODE DE LO Q QUIERES HACER::
HABER SI PODEMOS AYUDARTE
A ver si con esa explicación queda más claro mi problema... es que el código es demasiado complejo ya para ponerlo aquí. Si no se entiende, trataré de hacer algo sencillito y ponerlo aquí.
Gracias!.
- Antonio Linares
- Site Admin
- Posts: 37481
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Contact:
Re: MDI
David,
No se si te refieres a esto, pero dada una ventana MdiChild puedes calcular su "índice" de esta forma:
No se si te refieres a esto, pero dada una ventana MdiChild puedes calcular su "índice" de esta forma:
Code: Select all
nWnd = AScan( oWndPrincipal:oWndClient:aWnd, { | oWndChild | oWndChild == oWndFacturas } )
aObjVar( oSayEmp, nWnd )
- Antonio Linares
- Site Admin
- Posts: 37481
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Contact:
Re: MDI
Tambien puedes calcular el "índice" de la ventana MdiChild activa:
Code: Select all
nWnd = AScan( oWndPrincipal:oWndClient:aWnd, { | oWndChild | oWndChild == oWndPrincipal:oWndActive } )
aObjVar( oSayEmp, nWnd )
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
Re: MDI
Antonio,
Perfecto! con esto ya me sirve (creo) para llegar a lo que necesito. Muchas muchas gracias!
Saludos!
Perfecto! con esto ya me sirve (creo) para llegar a lo que necesito. Muchas muchas gracias!
Saludos!
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
Re: MDI
Pues ya no sé si es que ayer fué luna llena o algo de eso, que no doy con lo que busco. Voy a poner un trocito de mi código que creo representa el meollo. Esta función es llamada desde el menú de la ventana principal. Por cada vez que se ejecuta, se crea una nueva ventana.
aArrayVars es un array que va contener todas las variables y objetos y nombres de alias. El primer parámetro o índice del array lo utilizo referenciar varibles/objetos o alias. El segundo índice del array, lo uso para
El problema está, como comentaba en post anteriores, que cuando quiero hacer referencia a cualquier objeto de alguna ventana, he perdido la referencia para buscarlo en el array, porque nWnd cambia su valor. Por tanto, cada vez que generamos una nueva ventana, nWnd va a ser referencia únicamente de la última ventana creada. Por ello, si cambiamos de ventana, al hacer referencia a aObjVar( oArt, nWnd ), tan sólo me estaré refiriendo a la última ventana por el valor que tiene nWnd.
La solución de Antonio podría ser valida, excepto porque mi variable para el objeto ventana, la tengo que referenciar con nWnd también, de la manera siguiente: aObjVar( oWndChild, nWnd )
A ver si con el trocito de código se entiende mejor y yo entiendo mejor cómo solucionarlo.
Gracias!
aArrayVars es un array que va contener todas las variables y objetos y nombres de alias. El primer parámetro o índice del array lo utilizo referenciar varibles/objetos o alias. El segundo índice del array, lo uso para
El problema está, como comentaba en post anteriores, que cuando quiero hacer referencia a cualquier objeto de alguna ventana, he perdido la referencia para buscarlo en el array, porque nWnd cambia su valor. Por tanto, cada vez que generamos una nueva ventana, nWnd va a ser referencia únicamente de la última ventana creada. Por ello, si cambiamos de ventana, al hacer referencia a aObjVar( oArt, nWnd ), tan sólo me estaré refiriendo a la última ventana por el valor que tiene nWnd.
La solución de Antonio podría ser valida, excepto porque mi variable para el objeto ventana, la tengo que referenciar con nWnd también, de la manera siguiente: aObjVar( oWndChild, nWnd )
A ver si con el trocito de código se entiende mejor y yo entiendo mejor cómo solucionarlo.
Code: Select all
#include "FiveWin.ch"
#define aObjVar(i,j) aArrayVars[1][i][j]
#define aAlias(i,j) aArrayVars[2][i][j]
#define nTipVarMax 2 //Tipo máximo de variables (ObjVar y Alias = 2)
#define nWndMax 10 //Tope ventanas Man_Art()
// Objetos y variables
#define oWndChild 1
#define oDlg 2
#define oSayProc 3
#define oSayEmpr 4
#define oArt 5
#define cProc 6
#define cEmpresa 7
#define nObjVarMax 7 //Número máximo de variables y objetos
// Alias
#define Tec 1
#define Art 2
static aArrayVars[nTipVarMax][nObjVarMax][nWndMax]
static nWnd
**********************************************************************************************************************
function ManArt()
**********************************************************************************************************************
if nWnd = nil
nWnd := 1
else
nWnd ++
endif
aAlias( Tec, nWnd ) := CnwOpenDbf( oWndPrinc, "TEC.DBF", 5, .t., .f. )
//Funcion para abrir una base de datos compartida o exclusiva, retorna un alias nuevo y único
SET INDEX TO Tec_Cod, Tec_Nom
aAlias( Art, nWnd ) := CnwOpenDbf( oWndPrinc, "ART.DBF", 5, .t., .f. )
SET INDEX TO Art_Cod, Art_Nom, Art_Sub, Art_Int
DATABASE aObjVar( oArt, nWnd )
aObjVar( oArt, nWnd ):lOemAnsi = .t.
aObjVar( oArt, nWnd ):Load()
aObjVar( oArt, nWnd ):bBOF = { || MsgInfo( "Primer Registro" ) }
aObjVar( oArt, nWnd ):bEOF = { || MsgInfo( "Último Registro" ) }
SELECT ( aAlias( Art, nWnd ) )
DEFINE WINDOW aObjVar( oWndChild, nWnd ) MDICHILD OF oWndPrinc ;
FROM 5,5 TO 6,6;
COLOR "N/W" ;
BORDER NONE // prevent resizing
DEFINE DIALOG aObjVar( oDlg, nWnd ) RESOURCE "Articulos" OF aObjVar( oWndChild, nWnd )
REDEFINE SAY aObjVar( oSayProc, nWnd ) PROMPT aObjVar( cProc, nWnd ) ID 10 OF aObjVar( oDlg, nWnd )
REDEFINE SAY aObjVar( oSayEmpr, nWnd ) PROMPT aObjVar( cEmpresa, nWnd ) ID 20 OF aObjVar( oDlg, nWnd )
REDEFINE GET aObjVar( oGetArt_Cod, nWnd ) VAR aObjVar( oArt, nWnd ):Art_Cod ID 40 OF aObjVar( oDlg, nWnd ) PICTURE '@!' UPDATE
REDEFINE GET aObjVar( oArt, nWnd ):Art_Nom ID 50 OF aObjVar( oDlg, nWnd ) PICTURE '@!' UPDATE
REDEFINE GET aObjVar( oArt, nWnd ):Art_Col ID 60 OF aObjVar( oDlg, nWnd ) PICTURE '@!' UPDATE
REDEFINE BUTTON aObjVar( oBtnAnterior, nWnd ) ID 1000 OF aObjVar( oDlg, nWnd );
ACTION ( aObjVar( oArt, nWnd ):Skip( -1 ) , If( aObjVar( oArt, nWnd ):BoF(), MsgBeep(),), aObjVar( oDlg, nWnd ):Update() )
REDEFINE BUTTON aObjVar( oBtnSiguiente, nWnd ) ID 2000 OF aObjVar( oDlg, nWnd );
ACTION ( aObjVar( oArt, nWnd ):Skip(),;
If( aObjVar( oArt, nWnd ):EoF(), aObjVar( oArt, nWnd ):Skip( -1 ),), aObjVar( oDlg, nWnd ):Update() )
ACTIVATE DIALOG aObjVar( oDlg, nWnd ) NOWAIT CENTERED;
ON INIT ( aObjVar( oBtnSiguiente, nWnd ):SetFocus(), aObjVar( oDlg, nWnd ):Move( 0, 0 ), .f. );
VALID ( aObjVar( oWndChild, nWnd ):End(), .t. )
aObjVar( oWndChild, nWnd ):nWidth := aObjVar( oDlg, nWnd ):nWidth+7
aObjVar( oWndChild, nWnd ):nHeight := aObjVar( oDlg, nWnd ):nHeight+32
ACTIVATE WINDOW aObjVar( oWndChild, nWnd )
return nil
-
- Posts: 330
- Joined: Fri May 25, 2007 9:06 pm
Re: MDI
disculpa amigo el comentario,, y lo directo::
pero me podrias cual es el beneficio que tienes al programar una aplicacion de esta manera,, metiendo en varios array todo los objetos , y datos necesarios para crear tus window, dialog, say, get, etc,etc.::
??? que beneficio esperas::
pero me podrias cual es el beneficio que tienes al programar una aplicacion de esta manera,, metiendo en varios array todo los objetos , y datos necesarios para crear tus window, dialog, say, get, etc,etc.::
??? que beneficio esperas::
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
Re: MDI
Mantonio_08,mantonio_08 wrote:disculpa amigo el comentario,, y lo directo::
pero me podrias cual es el beneficio que tienes al programar una aplicacion de esta manera,, metiendo en varios array todo los objetos , y datos necesarios para crear tus window, dialog, say, get, etc,etc.::
??? que beneficio esperas::
No hay por qué pedir disculpas por un comentario y mucho menos por directo
Primero comentar que no son varios array, sino UNO solo. Esto tiene varias ventajas, una de ellas es que tan sólo utilizas una variable static (al menos bastante menos), evitas errores GPF y el paso de las mismas a otras funciones es mucho más sencillo (con pasar ese array, pasas absolutamente todo). Por otro lado, para funcionar en un ambiente MDI, es necesario, pues puedes ir aumentando el número de variables y alias a medida que se abren nuevas ventanas MDI, si quieres que el número de ventanas sea ilimitado, tan sólo limitado por memoria del sistema o las 255 workareas que permitía clipper y que no sé si esa limitación sigue en FW y Harbour, etc. Todos estos, digamos, "pros", los he ido sacando de post, ejemplos, etc.
Otro de los motivos y este ya de mi propia cosecha, es que estoy intentando migrar una aplicación de clipper, en la que se han utilizado en un 90% variables private. Efectivamente había que elegir entre depurar todo eso y luego migrar, o a la inversa, ir migrando y luego depurando. Una de las soluciones que he encontrado ha sido esta de ir conteniendo todas esas variables en array y luego irlos pasando a las distintas funciones. Existen en esa aplicación clipper funciones que manejan con 90 variables private, definidas en distintos puntos de la aplicación y el irlas pasando una a una a la función, luego a otra, luego a otra, es tarea inhumana, por lo que teniéndolas contenidas en un array, paso un array y aligeramos un poco el trabajo.
Espero que te ayude a comprender mejor el por qué de este sistema de variables. Por cierto que para MDI se hace casi imprescindible un método similar.
Saludos!
- Antonio Linares
- Site Admin
- Posts: 37481
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Contact:
Re: MDI
David,
Si nos pudieses proporcionar un ejemplo pequeño y autocontenido que podamos probar facilmente, sería más sencillo poder revisarlo e intentar darte una solución, gracias
Si nos pudieses proporcionar un ejemplo pequeño y autocontenido que podamos probar facilmente, sería más sencillo poder revisarlo e intentar darte una solución, gracias
-
- Posts: 199
- Joined: Tue Apr 22, 2008 9:54 am
- Location: Valladolid, Spain.
Re: MDI
Antonio,
Gracias a tí por tu preocupación y ayuda.
He hecho un pequeño ejemplo sin ventanas pero con diálogos MDI donde se ve el mismo problema. Funciona con la base clients de \samples.
Pare ver el problema, basta con abrir dos o tres ventanas, situarnos en la primera con las demás a la vista y pulsar sobre siguiente. El diálogo que se actualizará será el tercero siempre.
Gracias a tí por tu preocupación y ayuda.
He hecho un pequeño ejemplo sin ventanas pero con diálogos MDI donde se ve el mismo problema. Funciona con la base clients de \samples.
Pare ver el problema, basta con abrir dos o tres ventanas, situarnos en la primera con las demás a la vista y pulsar sobre siguiente. El diálogo que se actualizará será el tercero siempre.
Code: Select all
#include "FiveWin.ch"
#define aObjVar(i,j) aArrayVars[1][i][j]
#define aAlias(i,j) aArrayVars[2][i][j]
#define nTipVarMax 2 //Tipo máximo de variables (ObjVar y Alias = 2)
#define nWndMax 10 //Tope ventanas Man_Art()
// Objetos y variables
#define oDlg 1
#define oSayNom 2
#define oSayDir 3
#define oCli 4
#define oBtnAnterior 5
#define oBtnSiguiente 6
#define oBtnVer 7
#define nObjVarMax 7 //Número máximo de variables y objetos
// Alias
#define Cli 1
static oWndPrinc
static aArrayVars[nTipVarMax][nObjVarMax][nWndMax]
static nWnd
**********************************************************************************************************************
function Main()
**********************************************************************************************************************
DEFINE WINDOW oWndPrinc Title "Ventanas" MDI MENU MenuPrin()
ACTIVATE WINDOW oWndPrinc MAXIMIZED
return nil
**********************************************************************************************************************
function MenuPrin()
**********************************************************************************************************************
local oMenu
MENU oMenu
MENUITEM "&Ventanas"
MENU
MENUITEM "&Ventanas" ACTION ( ManArt() )
ENDMENU
ENDMENU
return oMenu
**********************************************************************************************************************
function ManArt()
**********************************************************************************************************************
if nWnd = nil
nWnd := 1
else
// Hacer control ventanas máximas.
nWnd ++
endif
aAlias( Cli, nWnd ) := NewAlias( "Clients.dbf" )
DbUseArea( .T.,, "Clients.dbf", aAlias( Cli, nWnd ), .t., .f. )
DATABASE aObjVar( oCli, nWnd )
aObjVar( oCli, nWnd ):Load()
aObjVar( oCli, nWnd ):bBOF = { || MsgInfo( "Primer Registro" ) }
aObjVar( oCli, nWnd ):bEOF = { || MsgInfo( "Último Registro" ) }
DEFINE DIALOG aObjVar( oDlg, nWnd ) TITLE "Diáologo: " + Str( nWnd ) FROM 0,0 TO 20,40 OF oWndPrinc
@ 1, 1 SAY aObjVar( oSayNom, nWnd ) PROMPT aObjVar( oCli, nWnd ):Name OF aObjVar( oDlg, nWnd ) UPDATE
@ 2, 1 SAY aObjVar( oSayDir, nWnd ) PROMPT aObjVar( oCli, nWnd ):Adress OF aObjVar( oDlg, nWnd ) UPDATE
@ 5, 1 BUTTON aObjVar( oBtnAnterior, nWnd ) PROMPT "Anterior" OF aObjVar( oDlg, nWnd );
ACTION ( aObjVar( oCli, nWnd ):Skip( -1 ) , If( aObjVar( oCli, nWnd ):BoF(), MsgBeep(),), aObjVar( oDlg, nWnd ):Update() )
@ 6, 1 BUTTON aObjVar( oBtnSiguiente, nWnd ) PROMPT "Siguiente" OF aObjVar( oDlg, nWnd );
ACTION ( aObjVar( oCli, nWnd ):Skip(),;
Iif( aObjVar( oCli, nWnd ):EoF(), aObjVar( oCli, nWnd ):Skip( -1 ),), aObjVar( oDlg, nWnd ):Update() )
@ 7, 1 BUTTON aObjVar( oBtnVer, nWnd ) PROMPT "Ver" OF aObjVar( oDlg, nWnd );
ACTION ( MsgInfo( "Alias: " + aAlias( Cli, nWnd ) + CRLF +;
"Valor nWnd: " + Str( nWnd ) ) )
ACTIVATE DIALOG aObjVar( oDlg, nWnd ) NOWAIT CENTERED;
ON INIT ( aObjVar( oBtnSiguiente, nWnd ):SetFocus(), aObjVar( oDlg, nWnd ):Move( 0, 0 ) );
VALID ( aObjVar( oCli, nWnd ) )->( DbCloseArea(), .t. )
return nil
**********************************************************************************************************************
function NewAlias( cNomDbf )
**********************************************************************************************************************
static nNum := 0
local cAlias := ""
nNum++
cAlias := SubStr( cFileNoExt( cFileName( cNomDbf ) ), 1, 4 ) + LTRIM( STR( nNum ) )
return ( cAlias )