miércoles, 23 de julio de 2014

Arrastrar / Mover un formulario sin barra de titulo, solo haciendo click sobre y moviendo el puntero

Hola a todos. He estado un poco ausente y me disculpo. Tengo algunos nuevos tips. Siempre que deseen, pueden consultarme a

sagaxton@hotmail.com

para cualquier duda.


Hoy veremos como hacer para que puedan arrastrar un formulario o moverlo de posicion haciendo click en cualquier area del mismo y mover el mouse para desplazarlo. A veces, tenemos formulario a los cuales deseamos agregar un toque de elegancia o "modernidad". Quitar el titlebar = .F. es una buena opcion... algo asi:



pero tenemos el inconveniente que no podemos arrastrarlo para ver lo que esta debajo. Para que puedas moverlo haciendo click sobre el, manteniendo presionado y desplazando el mouse, basta con agregar en el evento LOAD del formulario un codigo igual a este:

**--
DECLARE INTEGER ReleaseCapture IN WIN32API

DECLARE INTEGER SendMessage IN WIN32API INTEGER, INTEGER, INTEGER, INTEGER
**--//


Y en el evento MouseDown del formulario, un codigo igual a este:

LPARAMETERS nButton, nShift, nXCoord, nYCoord


LOCAL lnHandle

    IF nButton = 1

      ReleaseCapture()

      SendMessage(THISFORM.HWND, 0x112, 0xF012,0)

ENDIF
**--//



Y listo. Luego guardamos y ejecutamos. Hacemos click encima del form y mantenemos presionado mientras desplazamos el mouse y el form debera moverse al mismo ritmo. 

Bien.. cualquier duda o comentario para mejorar, es bienvenido. 

Un saludo y un abrazo! 
Desde Managua, capital de Nicaragua! 
Ravenn :D :D :D 


viernes, 9 de mayo de 2014

Convertir un número en texto (125,700.63 = ciento veinticinco mil setencientos con 63/100)

Hola a todos. A veces queremos traducir un numero a texto. Esto es util cuando imprimirmos algun recibo o factura... o para la impresion de cheques.


Antes que nada, quiero dejar claro que ésta rutina la tome hace un par de años del foro de todoexpertos.com... aunque no recuerdo de que experto, creo que era Don Javier Cortez, gran conocedor de VFP... maestro! ... pero todos los créditos son para esa persona que adecuó dicha rutina, no es de mi autoría directamente pero es magnifica y sencilla! :) :) :)


Va así:


1 - Crea una rutina .prg llamada 'NToLetra.prg' (sin comillas). En ella, copia el siguiente código:



*** Regresa numero en letras
Procedure NToLetra
Parameter numero
num_car = Str(numero,15,2)
num_dig = Subs(num_car,14,2)
pos = 1
Store "" To num_car_fin,leyenda
For t=1 To 4
Store 0 To uni,dec,cen
cen = Val(Subs(num_car,pos+0,1))
dec = Val(Subs(num_car,pos+1,1))
uni = Val(Subs(num_car,pos+2,1))
pos = pos + 3
letra3 = centena(uni,dec,cen)
letra2 = decenas(uni,dec,cen)
letra1 = unidads(uni,dec,cen)
Do Case
Case t=1
leyenda = IIf(uni+dec+cen=1,"BILLÓN ",IIf(uni+dec+cen>1,"BILLONES ",""))
Case t=2
leyenda = IIf(uni+dec+cen=1,"MILLÓN ",IIf(uni+dec+cen>1,"MILLONES ",""))
Case t=3
leyenda = IIf(uni+dec+cen=1,"MIL ",IIf(uni+dec+cen>1,"MIL ",""))
Case t=4
leyenda = IIf(uni+dec+cen=1,"",IIf(uni+dec+cen>1,"",""))
EndCase
num_car_fin = num_car_fin + letra3 + letra2 + letra1 + leyenda
EndFor
num_1 = Val(Subs(num_car,1,12))
num_2 = Val(Subs(num_car,4,9))
num_3 = Val(Subs(num_car,7,6))
leyenda = ""
If num_1=1
leyenda = "PESO"
Else
If num_2=0 .Or. num_3=0
leyenda = "DE PESOS"
Else
leyenda = "PESOS"
EndIf
EndIf
If num_1 = 0
num_car_fin = "CERO "
leyenda = "PESOS"
EndIf
num_car_fin = num_car_fin + leyenda + ' CON ' + num_dig + "/100"
Return num_car_fin
** Unidades
Procedure unidads
Parameter uni,dec,cen
Do Case
Case uni = 1 .And. dec#1
ctexto = "UN "
Case uni = 2 .And. dec#1
ctexto = "DOS "
Case uni = 3 .And. dec#1
ctexto = "TRES "
Case uni = 4 .And. dec#1
ctexto = "CUATRO "
Case uni = 5 .And. dec#1
ctexto = "CINCO "
Case uni = 6
ctexto = "SEIS "
Case uni = 7
ctexto = "SIETE "
Case uni = 8
ctexto = "OCHO "
Case uni = 9
ctexto = "NUEVE "
OtherWise
ctexto = ""
EndCase
Return ctexto
** Centenas
Procedure centena
Parameter uni,dec,cen
Do Case
Case cen=1 .And. (dec=0 .And. uni=0)
ctexto = "Cien "
Case cen=1 .And. (dec>0 .Or. uni>0)
ctexto = "CIENTO "
Case cen=2
ctexto = "DOSCIENTOS "
Case cen=3
ctexto = "TRESCIENTOS "
Case cen=4
ctexto = "CUATROCIENTOS "
Case cen=5
ctexto = "QUINIENTOS "
Case cen=6
ctexto = "SEISCIENTOS "
Case cen=7
ctexto = "SETECIENTOS "
Case cen=8
ctexto = "OCHOCIENTOS "
Case cen=9
ctexto = "NOVECIENTOS "
OtherWise
ctexto = ""
EndCase
Return ctexto
** Decenas
Procedure decenas
Parameter uni,dec,cen
Do Case
Case dec=1 .and. uni=0
ctexto = "DIEZ "
Case dec=1 .and. uni=1
ctexto = "ONCE "
Case dec=1 .and. uni=2
ctexto = "DOCE "
Case dec=1 .and. uni=3
ctexto = "TRECE "
Case dec=1 .and. uni=4
ctexto = "CATORCE "
Case dec=1 .and. uni=5
ctexto = "QUINCE "
Case dec=1 .and. (uni>5 .and. uni<10)
ctexto = "DIECI"
Case dec=2 .and. uni=0
ctexto = "VEINTE "
Case dec=2 .and. uni>0
ctexto = "VEINTI"
Case dec=3 .and. uni=0
ctexto = "TREINTA "
Case dec=3 .and. uni>0
ctexto = "TREINTA Y "
Case dec=4 .and. uni=0
ctexto = "CUARENTA "
Case dec=4 .and. uni>0
ctexto = "CUARENTA Y "
Case dec=5 .and. uni=0
ctexto = "CINCUENTA "
Case dec=5 .and. uni>0
ctexto = "CINCUENTA Y "
Case dec=6 .and. uni=0
ctexto = "SESENTA "
Case dec=6 .and. uni>0
ctexto = "SESENTA Y "
Case dec=7 .and. uni=0
ctexto = "SETENTA "
Case dec=7 .and. uni>0
ctexto = "SETENTA Y "
Case dec=8 .and. uni=0
ctexto = "OCHENTA "
Case dec=8 .and. uni>0
ctexto = "OCHENTA Y "
Case dec=9 .and. uni=0
ctexto = "NOVENTA "
Case dec=9 .and. uni>0
ctexto = "NOVENTA Y "
OtherWise
ctexto = ""
EndCase
Return ctexto
**--//



2 - En el formulario donde vas a presentar el resultado, coloca esta linea en el evento INIT() del form. (no se que diablos le pasa a esta maldita pagina pero cuando quieres borrar algo con backspace el cursor de escritura se pierde al final del carajo! jajajajajaja! )


SET PROCEDURE TO NToLetra.prg ADDITIVE


3 - Para efectos de ejemplo, pruébalo así como te diré, luego vos lo adecuas a algún evento o método espcifico. Inserta en el form 2 textos y 1 botón. Un text1 en donde cargaras la cantidad y un text2 donde aparecerá la cantidad en letras. En el evento click del botón, coloca un código como éste:


**--Monto a letras
THISFORM.Text2.Value = NToLetra(THISFORM.Text1.Value)
**--//
THISFORM.Refresh


**---



Ahora, solo guarda y ejecutá. Ingresa algún numero en el text1 y luego da click en el botón. La cantidad en letras debe aparecer!


Cualquier problema, duda o sugerencia... comenten! Todo para mejorar. Saludos!


Desde Managua, capital de Nicaragua!
Ravenn :D :D :D

Invertir el orden de los caracteres en una cadena - HOLA = ALOH

Hola. A veces necesitamos hacer cosas un poco fuera de lo normal, pero resulta muy interesante. ::)


Imaginemos que necesitamos invertir una cadena... por ejemplo, para validar algun tipo de numero serial, o algo asi (crear un artificio). Si tenemos la cadena HOLA y queremos invertir las letras para que queden ALOH... la forma de proceder es asi:


LOCAL lcRetValue, lnCounter, lcCadena

**--En éste caso, la cadena es "HOLA"
lcCadena = "HOLA"

**--
lcRetValue = ""

**--Iniciamos bucle
FOR lnCounter = LEN(lcCadena) TO 1 STEP -1

    lcRetValue = lcRetValue + SUBSTR(lcCadena, lnCounter, 1)


ENDFOR
**--//

**--Mostramos datos
MESSAGEBOX(lcRetValue)


Prueben. Cualquier duda o sugerencia para mejorar... es siempre bienvenida.


Un abrazo a todos.
Ravenn!
Desde Managua, Nicaragua!

Cómo evitar CONTROLES VACÍOS (TextBox, ComboBox, EditBox) en un formulario cuando son Obligatorios

Hola a todos. A veces necesitamos validar que, al momento de guardar un registro, algun control como un textbox, editbox, combobox no esten vacios porque es obligatorio que tengan al menos un dato. Por ejemplo: en una factura es incorrecto que el registro se guarde (al facturar un producto) sin el numero de factura. En un catalogo de productos... es incorrecto que se guarde un dato en blanco en la tabla para el campo nombreproducto.

Las validaciones son importantes para asegurar la integridad de los datos que se van a guardar en la base de datos, sea ésta la nativa de foxpro (.dbc) o si usamos otro motor de dase de datos compatible (FIREBIRD, SQL, etc..). Dichas validaciones deben realizarse SIEMPRE en la aplicacion, en el formulario desde el cual se originan. Es incorrecto que un dato de un campo vaya sin validar a la base de datos. ES INCORRECTO Y UNA MALA PRÁCTICA Y FILOSOFIA DE PROGRAMACIÓN.

Por tanto, requerimos en ocasiones... identificar aquellos controles por medio de los cuales ingresamos los datos para guardarlos en las tablas. Lo mas conveniente es colocar un codigo de validación que dispare un STOP o detenimiento del proceso de guardado antes que éste se consuma, es decir, se deben verificar que los datos que consideremos como OBLIGATORIOS o los mas IMPORTANTES sean cargados (digitados) correctamente en los controles antes del correr un proceso de insertado en las tablas.

Para tal fin, he aqui un pequeño código que ha resultado muy satisfactorio. Aclaro que éste no es de mi autoria. Recuerdo que alguien mas lo publico hace años en un foro, cuando yo recien aprendia. Lo tome, lo analice y lo usé. Ahora es un metodo de los mas comunes y usados en mis aplicaciones. Todo el crédito para el autor original, en dado caso si algún dia esa persona lee este post.

1 - Creamos un nuevo metodo en nuestro formulario en modo de diseño(Click en el menu de foxpro, click en el menu form, click en new method) con el nombre validarvacios. Luego, vamos a la pestaña methods de las propiedades del form y buscamos el metodo nuevo. Debe aparecer al final, en letras rojitas. Damos doble click y en el escribimos este codigo:

LOCAL lnObjecto, lcNombre, lctooltiptext, lcContenido, lcFoco


**--Evitar vacios
FOR lnObjecto = 1 TO THISFORM.OBJECTS.COUNT
 
  **--Si el objeto esta marcado con * en comment
  IF THISFORM.OBJECTS(lnObjecto).COMMENT = "*"
 
       **Obtenemos el nombre
    lcNombre = THISFORM.OBJECTS(lnObjecto).NAME
    **Obtenemos el tooltiptext
    lctooltiptext =  THISFORM.OBJECTS(lnObjecto).TOOLTIPTEXT
    **Obtenemos el valor para saber si esta vacio o no
    lcContenido = "ThisForm." + lcNombre + ".Value"
       
        **--Si el valor del objeto es vacio
        IF EMPTY(&lcContenido)
       
              **--
              MESSAGEBOX("Es necesario ingresar el dato " + "-" + lctooltiptext + "-", 16, "Error - Datos pendientes por ingresar")
           
             **--Regresamso el foco al objeto
             lcFoco = "ThisForm." + lcNombre + ".SetFocus"
              &lcFoco
              
        **--||
        RETURN
        **--||
           

        ENDIF
  ENDIF
ENDFOR
**--//


Luego, solo vamos a cada control que deseamos validar para que no tenga datos en blanco y escribimos un * en la propiedad comment. En la propiedad tooltiptext escribimos algun nombre que deseamos ver del dato para el mensaje que aparecerá si se dispara la validacion.

Prueben y me avisan.


Cualquier duda o comentario para mejorar es siempre bienvenido.

Pueden encontrarme en todoexpertos.com en la subcategoria de visualfoxpro, como el experto ravenn


Un abrazo!
Desde Managua, Nicaragua!
Ravenn :D :D :D

Cómo saber si un numero es entero o si tiene parte decimal

Hola a todos. A veces necesitamos conocer si un numero (por lo general, contenido en una variable) es entero o decimal. Aqui una solucion practica:


LOCAL lnNumero, lnExp

**--El numero en cuestion es 15.78 = DECIMAL
lnNumero = 15.78


**--Validamos para saber si es decimal o no
lnExp = lnNumero - INT(lnNumero)

**--Despues de restar la parte entera al numero,
**--quedará, de ser decimal, la parte despues del . decimal.
IF lnExp > 0

         **--Es decimal, porque el restante es mayor a 0
         **--Aqui procedemos a como deseemos

ENDIF
**--//


La cosa es tomar la expresion numerica, restar la parte entera y quedará el remanente decimal. Si ese remanente ES MAYOR a cero significa que si hay una parte decimal. Si es igual a cero, la expresion no tenia decimales... por tanto es completamente entero. Facil no?


Es solo una práctica solucion, no es la unica ni la mejor. Cualquier duda o comentario para mejorar es siempre bienvenido.

Un abrazo!
Desde Managua, Nicaragua!
Ravenn :D :D :D



Cómo obtener un numero al azar entre 1 y 1000

Hola a todos. Es un placer poder saludarlos. Hoy veremos como obtener un numero al azar entre 1 y 1000. 

Para tal fin, haremos uso de RAND(), el cual devuelve un numero aleatorio entre 0 y 1. La cosa va asi: 


LOCAL lnNumRand, lnNumeroAzar

**--Numero aleatorio
lnNumRand = RAND() * 1000

**--Tomamos la parte entera
lnNumeroAzar = INT(lnNumRand)

**--Mostramos resultado
MESSAGEBOX(lnNumeroAzar)
**--//



Con esto, tomamos solo la parte entera del numero que RAND() devuelve despues de multiplicarlo por 1000. Aqui, podemos decidir el rango hasta donde queremos calcular el numero. Si lo multiplicas por 10, sera entre 0 y 1. Si lo multiplicas por 100 el rango sera de 0 a 100... ya asi sucesivamente.


Bien... esta es solo una pequeña solucion. En el foro de todoexpertos.com han realizado algunas veces esta consulta y he aqui una forma de proceder. No es la única ni la mejor... pero esta bien.


Cualquier duda o comentario para mejorar, es siempre bienvenido.

Un abrazo!
Desde Managua, Nicaragua!
Ravenn :D :D :D

miércoles, 5 de febrero de 2014

Obtener la fecha en letras con Visual FoxPro: 'Miercoles, 5 de Febrero de 2014'

Hola a todos. Reciban un cordial saludo. A veces, queremos obtener alguna fecha determinada en letras, tal cual la forma: 'Miércoles, 5 de Febrero 2014'. Ésto es muy útil cuando queremos imprimir una validacion del día exacto en que se realiza alguna transacción, como un cheque, un recibo de caja, una factura, un boucher de deposito... o algo parecido.

Para tal fin, mostramos el siguiente código, en el cual el valor de la variable local tdate es la fecha que debe ser pasado como parametro en formato DATE(). Nosotros usamos el dia actual como ejemplo, pero si se requiere adecuarlo como una funcion especifica, basta con pasar el parametro tdate y retornar el valor.

Bien... aca el codigo:

**------------------------------------------
LOCAL tdate, lnday, lnndx, lnMonth, lnDay
**--
LOCAL lcDia, lcMes, lnAño, lcSufijo, lcFecha

tdate = DATE()

**--
lnndx = DAY(tdate)


lnMonth = MONTH(tDate)
lnDay = DOW(tDate)
lcSufijo = " de "
lnAño = YEAR(tdate)

**--Obtenemos el día
DO CASE
    **Domingo
    CASE lnDay = 1  
        lcDia = "Domingo"
    **Lunes
    CASE lnDay = 2
        lcDia = "Lunes"
    **Martes
    CASE lnDay = 3
        lcDia = "Martes"
    **Miercoles
    CASE lnDay = 4
        lcDia = "Miercoles"  
    **Jueves
    CASE lnDay = 5  
        lcDia = "Jueves"  
    **Viernes
    CASE lnDay = 6
        lcDia = "Viernes"  
    **Sábado
    CASE lnDay = 7
        lcDia = "Sábado"      

ENDCASE
**--//

**--Obtenemos el mes
DO CASE
    **Enero
    CASE lnMonth = 1
        lcMes = "Enero"
    **Febrero
    CASE lnMonth = 2
        lcMes = "Febrero"
    **Marzo
    CASE lnMonth = 3
        lcMes = "Marzo"
    **Abril
    CASE lnMonth = 4
        lcMes = "Abril"
    **Mayo
    CASE lnMonth = 5
        lcMes = "Mayo"
    **Junio
    CASE lnMonth = 6
        lcMes = "Junio"
    **Julio
    CASE lnMonth = 7
        lcMes = "Julio"
    **Agosto
    CASE lnMonth = 8
        lcMes = "Agosto"
    **Septiembre
    CASE lnMonth = 9
        lcMes = "Septiembre"
    **Octubre
    CASE lnMonth = 10
        lcMes = "Octubre"
    **Noviembre
    CASE lnMonth = 11
        lcMes = "Noviembre"
    **Diciembre
    CASE lnMonth = 12
        lcMes = "Diciembre"
ENDCASE

**--Armamos la fecha
lcFecha = lcDia + ", " + ALLTRIM(STR(lnndx) + lcSufijo + lcMes + lcSufijo + ALLTRIM(STR(lnAño)))

MESSAGEBOX(lcFecha)
**----------------------------------------------------------------------------------------------


Saludos. Cualquier duda, comentario o aportacion para mejorar es siempre bienvenido. 

Un abrazo! 
Desde Managua, Nicaragua! 
Ravenn :D :D :D 



viernes, 31 de enero de 2014

Saber si un cursor temporal o tabla .dbf está vacio.

Hola. Reciban cordiales saludos todos. Un pequeño tip, pero muy útil, es saber la forma para determinar si un cursor temporal o una tabla física (.dbf) está vacia totalmente. Muchas veces, para tal fin, contamos el numero de registros de las tablas con RECCOUNT() o usando la función COUNT(). Éstas son buenas opciones pero a veces, las funciones tienen ciertas condiciones o excepciones que dificultan obtener una certeza amplia sobre el numero de registros de dicha tabla.


Una forma práctica y rápida para saber si no hay ningun registro en la tabla o cursor es usar la Variable '_Tally'. Con un código como éste, podremos comprobarlo:


**--
SELECT tabla/cursor

IF _TALLY = 0

          **--Aqui el proceso si está vacio
          Messagebox("El cursor está vacio")


ENDIF
**--//


Bien... cualquier duda, sugerencia o comentario es siempre bienvenido.

Saludos!

Desde Managua, Nicaragua!
Ravenn :D :D :D

Redimensionar formulario (principal) según resolucion de pantalla.

Hola. Reciban un cordial saludo. A veces, queremos que un formulario, especialmente cuando es el principal de nuestras aplicaciones, se 'maximice' o re-dimensione según el 'ancho' y 'alto' de la pantalla del pc. Una forma practica de lograr 'estirar' el formulario sin tener que estar adecuando código cada vez que instalamos la aplicación en diferentes computadoras, es la siguiente:


En el evento INIT() del formulario, colocá un código como éste:


*--Dimensiones
THIS.Height = SYSMETRIC(22)
THIS.Width = SYSMETRIC(1)
**--
THISFORM.AutoCenter = .T.
**--
THISFORM.Top = 0
**--//


Luego, guardá los cambios y ejecutá el formulario.

Cualquier duda, sugerencia o comentario para mejorar es bienvenido!!!

Saludos!
Desde Managua, Nicaragua!
Ravenn :D :D :D

Evitar salir del control Text al presionar BackSpace (borrar)

Saludos. He estado un poco ausente... pero aqui otra vez con algunas nuevas entradas... nunca nos olvidamos de las personas que encuentran alguna utilidad en el blog.

Hoy proponemos una solución a una inquietud simple pero que aqueja a muchos programadores de Visual FoxPro: como evitar que el cursor de escritura salga del control text y regrese al control anterior text cuando borramos el contenido total del primer objeto al presionar BackSpace o tecla de borrado.

Agradezco mucho el aporte del usuario yondaime del dia 12 de mayo de 2016. Gracias a èl pude corregir un error en el retazo de codigo... agregando la "colita" THIS.SelStart = 0". Gracias a esta forma, se puede trabajar indistintamente del tipo de dato (sea numerico o caracter) del control... y se puede retornar eficientemente a la posicion inicial, es decir, que el valor del control sea = "" en el caso de tipo de dato texto ò = 0 en el tipo de datos numerico. Excelente aporte yondaime.. eres un capo! xDxD

**--
IF nKeyCode = 127 AND (THIS.SelStar = 0)
  NODEFAULT
ENDIF
**--//


Con ésto, cuando presiones 'BACKSPACE' el cursor de escritura no regresará al control text anterio, sino que permanece en el control actual.

Cualquier duda, sugerencia o comentario para mejorar es bienvenido siempre que no sea con fines ofensivos! :) :)

Saludos!
Desde Managua, Nicaragua!
Ravenn :D :D :D