24 septiembre 2007

Poner una ventana en primer plano

En este caso, el empleo dado a la función API setWindowPos ha sido para que la ventana de "splash" de inicio de una aplicación sea siempre visible, y no se quede en segundo plano mientras esta abierta, pero podría usarse para la típica aplicación que deseamos que siempre se quede en primer plano, usando dicha función con su ventana principal.

En primer lugar debemos declararla como función externa del siguiente modo:



Después, sobre la ventana de "splash", en el propio evento open podemos usar el siguiente código:



Si queréis información completa de la función SetWindowPos, la podéis encontrar en el MSDN de Microsoft. Respecto a los valores de las constantes los dejo aquí para mayor facilidad:



Como siempre, espero que os sea de utilidad. Hasta pronto.

09 julio 2007

Como clonar dw/ds

Desde luego, si hubiera caído antes en la posibilidad de usar esta opción para clonar dw o crear ds usando dw o viceversa, me hubiera ahorrado muchos quebraderos de cabeza, fijaros sino en la entrada de "Encabezados dinámicos. Con un par..." o en la de "Copiar "Retrieval Arguments" entre dw/ds", podrían haber cambiado mucho en el primer caso e incluso no ser necesario montar todo el tinglado de la copia de retrieval arguments, ya que esta solución, realiza un clonado completo, incluidos los argumentos...

El único matiz que hay que tener en cuenta, es que hay que usar sobre el objeto clonado la función setTransObject(Transaction) si queremos que tenga capacidad de trabajar contra la BD directamente.

En primer lugar tendremos que crear (si no usamos ya uno) un objeto de tipo (dw/ds) al que en nuestro ejemplo nada más crearlo lo guardaremos con el nombre "u_dw", es importante guardarlo para poder usar el nombre del objeto a continuación como parámetro de la función de clonado.

Creamos una función que en nuestro caso llamamos of_clonedw y que tendrá un parámetro, este le llamaremos adw_aux y el tipo corresponderá con el del objeto creado: "u_dw" y utilizaremos el siguiente código:



Lo que estamos haciendo en este código es almacenar en una variable de tipo blob la información del dw/ds, esto en realidad, si se inspecciona la variable, guarda la información del dw como si de un "psr" se tratase. Luego se comprueba si el argumento recibido es válido y en tal caso se realiza un acceptText para fijar el contenido del último campo editado (por si no perdió el foco) y por último se transfiere la información al dw deseado.

Como ejemplo de llamada, supongamos que estamos en una ventana con un dw_1 con datos y queremos copiar este sobre el dw_2:



Recordar que no se transfiere la información de la transacción asignada.

Espero que os permita quitaros muchos dolores de cabeza, y sino, al menos, que os facilite el trabajo. Un saludo para todos.

15 junio 2007

Prefijos estándar de PowerBuilder


En más de una ocasión utilizamos tipos, objetos, etc., que no son los habituales, y cuando pretendemos usar sus prefijos correspondientes andamos con dudas o perdidos, por eso, y con el objetivo de siempre, de tener centralizada la información que he necesitado en más de una ocasión y he tenido que buscar repetidas veces, he decidido poner aquí también la lista de prefijos y calificadores estándar de PowerBuilder:

Calificadores de alcance

Alcance Prefijo Ejemplo
Argument

a

al_NameId
Global

g

gs_Name
Instance

i

ii_Count
Local

l

ls_Foo
Sharedssi_Number

Prefijos de tipos de datos estándar

Tipo de dato Prefijo Ejemplo
Any a la_Raw
Blob blb ablb_Image
Boolean b lb_Exit
Character c lc_Name
Date d ld_BirthDate
DateTime dt ldt_Wreck
Decimal dec ldec_Salary
Double db gdb_OverTime
Integer i li_Count
Long l ll_RowCount
Real r sr_Illusion
String s ls_Bean
Time tm itm_MrWolf
Unsigned Integer ui lui_Handle
Unsigned Long ul gul_Ken

Prefijos de tipos de datos de objetos estándar

Objeto Prefijo Ejemplo
Application app gapp_PBDelta
ArrayBounds ab lab_Bound
CheckBox cbx icbx_Male
ClassDefinition cldef lcldef_Object
ClassDefinitionObject cldefo lcdefo_ThatObject
CommandButton cb lcb_Cancel
Connection cn lcn_Known
ConnectionInfo cni lcni_ServerInfo
ConnectObject cno scno_ConObj
ContextInformation cxinfo lcxinfo_ObjContext
ContextKeyword cxk lcxk_TheKey
CPlusPlus cpp lcpp_CModule
Datastore ds lds_StockData
Datawindow dw ldw_Employee
DatawindowChild dwc ldwc_States
DragObject drg ldrg_ByTheHair
DrawObject drw ldrw_PaintBrush
DropDownListBox ddlb lddlb_States
DropDownPictureListBox ddplb lddplb_StatesWithFlags
dwObject dwo ldwo_Column
DynamicDescriptionArea dda ldda_Execute
DynamicStagingArea dsa ldsa_MyQuery
EditMask em lem_PhoneNo
EnumerationDefinition enum lenum_Cycle
EnumerationItemDefinition enumi ienumi_Item
Environment env lenv_System
Error err gerr_Snarl
ExtObject exto gexto_Outside
Function_Object fo lfo_Function
Graph gr lgr_Sales
GraphObject gro lgro_Line
GrAxis grx lgrx_Profit
GrDispAttr grda igrda_Value
GroupBox gb igb_Employee
HScrollBar hsb ihsb_Percent
iNet inet linet_Web
InternetResult ir lir_Page
Line li lli_Arrow
ListBox lb llb_States
ListView lv llv_Filenames
ListViewItem lvi llvi_Item
MailFileDescription mfd lmfd_Mail
MailMessage mm lmm_Mail
MailRecipient mr lmr_Mail
MailSession ms lms_Session
MDIClient mdi lmdi_Frame
Menu m lm_Menu
MenuCascade mc lmc_WaterFall
Message msg lmsg_Whisper
MultiLineEdit mle lmle_Text
NonVisualObject nvo lnvo_Invisible
OLEControl oc loc_Bulls
OLECustomControl occ locc_Dial
OLEObject oo loo_Ghost
OLEStorage ostg gostg_WordFile
OLEStream ostm lostm_River
OMControl omc iomc_Temp
OMCustomControl omcc lomcc_Dial
OMEmbeddedControl omec lomec_Micro
OMObject omo iomo_Bob
OMStorage omstg somstg_Drive
OMStream omstm lomstm_Mersey
Oval ov lov_Circle
PBtoCPPObject pb2cpp lpb2cpp_Quad
Picture p lp_Smile
PictureButton pb lpb_Click
PictureListBox plb lplb_Drives
Pipeline pl lpl_OilandGas
Powerobject po lpo_Source
ProfileCall prc lprc_Face
ProfileClass prcl lprcl_Room
ProfileLine prl iprl_Bob
ProfileRoutine prr lprr_Cat
Profiling pr lpr_Outline
RadioButton rb lrb_Male
Rectangle rec irec_Tum
RemoteObject ro iro_Employee
RichTextEdit rte lrte_Script
RoundRectange rr lrr_Lion
ScriptDefinition sdef lsdef_Mycode
Service srv lsrv_Charge
SimpleTypeDefinition std istd_MyType
SingleLineEdit sle lsle_Name
StaticText st lst_Prompt
Structure str lstr_Data
SystemFunctions sf lsf_Bay
Tab tab ltab_Strip
Timing tmg ltmg_Clock
TraceActivityNode tran ltran_Node
TraceBeginEnd trbe ltrbe_Start
TraceError tre ltre_Error
TraceFile trf itrf_TraceFile
TraceGarbageCollect trgc itrgc_Call
TraceLine trln strln_Rope
TraceObject tro ltro_Bob
TraceRoutine trr ltrr_Tiger
TraceSQL trsql ltrsql_MySQL
TraceTree trt ltrt_Birch
TraceTreeError trte ltrte_Err
TraceTreeGarbageCollect trtgc ltrtgc_Trash
TraceTreeLine trtl ltrtl_String
TraceTreeNode trtn ltrtn_Kilt
TraceTreeObject trto ltrto_Trace
TraceTreeRoutine trtr ltrtr_BobGrimmer
TraceTreeUser trtu ltrtu_User
TraceUser tru ltru_Blue
Transaction tr ltr_Trans
Transport tp ltp_Server
Treeview tv ltv_Directory
TreeviewItem tvi ltvi_Node
TypeDefinition typdef ltypdef_Details
UserObject uo luo_Control
VariableCardinalityDefinition vcd lvcd_Type
VariableDefinition vd lvd_Sore
VerticalScrollBar vsb lvsb_Amount
Window w w_PleaseWait
WindowObject wo lwo_WomanElement

Convención de nombres de clases

Clase Prefijo Ejemplo
menu m_ m_principal
Standard class user object n_ n_ds
Custom class user object n_cst_ n_cst_customer
Global structure s_ s_point
Visual user object u_ u_pb
Window w_ w_mdi

Otras consideraciones

Las funciones globales usan el prefijo gf_ y las funciones de objetos utilizan el prefijo of_
Las constantes no suelen tener prefijo y se escriben en mayúsculas.

12 febrero 2007

Encabezados dinamicos. Con un par...

Como poner un encabezado dinamico a un dw... en mi caso nunca imprimo directamente el dw, sino que paso la info. a un ds, le planto el encabezado dinámico y luego lo imprimo.

En este post solo aparece el código para poner el encabezado que deseo, si quereis ampliarlo para poder imprimir datawindows según lo he descrito podéis mirar el post anterior.

Aunque ya lo he comentado en otros post, como dato curioso, si os fijáis, sería el primer argumento de la función, que correspondería al dw o ds al que le pondremos el encabezado, pues resulta que el tipo en un powerObject, precisamente para poder aplicar el encabezado tanto a dw como a ds.

El resto de argumentos podrían eliminarse, todo depende del encabezado que deseemos, en mi caso para el argumento as_application le paso una cosntante que corresponde con el nombre de la aplicación para saber desde donde se ha impreso; para el argumento as_title, le paso el title de la ventana donde esta el dw que corresponde siempre con lo que muestra; y por último un argumento con un string para poner información en el pie de la impresión (privado, confidencial, borrador, etc.).

En siguiente ilustración representa una imagen de como quedaría... emborrone el logo por temas de confidencialidad, por cierto, si vuestro logo difiere en dimensiones, tal vez fuese necesario modificar alguna medida.


Y para finalizar aquí desempolvado y listo para usar el código responsable de añadido dinámico:


Se aceptan sugerencias y mejoras. Saludos.

Copiar "Retrieval Arguments" entre dw/ds

Si has intentando traspasar la información de un dw/ds y existían campos computados u otros elementos que hiciesen referencia a los "Retrieval Argument" (sin considerar la SQL), habrás podido observar que dicha información no se procesa de modo correcto, si son computados no aparecen y si por ejemplo un grupo se basaba en ellos tampoco aparecía el grupo.

Este problema no sucede solo al hacer copia de datos entre los dw/ds, también sucede con en método ShareData.

Hasta aquí podríamos pensar que habría que buscar mediante "describe" o "modify" las opciones necesarias para conseguir nuestros objetivos, traspasar la información de los "Retrieval Arguments", pues bien, casi, casi... resulta que podemos obtener los "Retrieval Arguments", pero no existe medio material de asignarlos a un dw o ds sin tener que recurrir al método retrieve, y en algunos casos esta operación puede ser pesada y costosa, y necesitaremos evitarla a toda costa, bueno, pues como siempre depende del objetivo final, en mi caso necesitaba conseguir que los computados funcionasen y que los grupos también lo hiciesen, así que si no tenía opción de asignar la información directamente al control, lo que hice fue modificar dinámicamente los controles afectados.

Por un lado tiré en parte con código de las PFCs pare recuperar los "Retrieval Arguments", ampliando la función para poder obtener además el valor asociado, y por otro lado, preparé una función que permitía modificar los controles que contenían información asociada a los argumentos.

Aquí os muestro una función que usaremos en nuestro código y esta sacada de las PFCs.


Ahora que ya tenemos esta función que nos facilitará la vida para este y otros códigos, vamos a recoger el dw/ds los argumentos ("Retrieval Arguments") que queremos traspasar.

Fijaros como detalle en la definición de esta función que en lugar de usar como tipo de argumento dw o ds e utilizado uno de tipo powerobject para que la misma función sirva para ambos casos y después, como consecuencia lógica, tengo que hacer las llamadas de modo dinámico:


Otra apreciación sobre la última función descrita es que solo se puede implementar sobre "Retrieval Arguments" que no sean de tipo array, pero esto no un impedimento para nuestros objetivos, ya que estos solo se puede usar en la sentencia SQL y nosotros los datos los traspasaremos con el método ShareData.

Ahora nos queda la parte en la que ponemos los "Retrieval Arguments" en el datawindow o datastore destino. Nota: en mi caso se trataba de imprimir un ds que podría proceder de un dw u otro ds, por eso el argumento utilizado es de este tipo, pero si queréis utilizarlo para ds o dw podéis aplicar el truco comentado de la función anterior.


Ya solo queda ver un ejemplo en el que se vea claramente como utilizar las funciones descritas hasta el momento. Ya comente que en mi caso utilizo este tinglado para añadir dinámicamente una cabecera standard a todos mis dw o ds en el momento de imprimirlos, para lo cual utilizo una función:


Espero haber solucionado más de un quebradero de cabeza. Hasta pronto.

08 enero 2007

VerticalScrollPosition y HorizontalScrollPosition

Extendiendo el "Post" anterior podemos añadir el mensaje 276 para el control del desplazamiento horizontal. Para disponer de un control detallado de los posibles desplazamientos, las constantes que podríamos utilizar son:


De todos modos, al final he optado por otra solución en la que utilizo describe y modify sobre VerticalScrollPosition, HorizontalScrollPosition y Horizontal2ScrollPosition, ya que en mi caso, me interesa desplazarme a una posición en la que se visualiza un objeto en concreto, y me permitiría el control horizontal de las dos áreas visibles del dw si esta activado el atributo HSplitScroll. Así que la parte del código que controla el error queda del siguiente modo (tras controlar el error y notificarlo):


¿Qué tal se portaron los Reyes?

05 enero 2007

Scroll en un datawindow con PowerScript

Tras un error controlado necesité poner el foco en el control que el usuario tiene que corregir, pero trabajando con un formulario de tipo freeform, con una fila que ocupa más de una página, si el control no esta visible, aunque reciba el foco no se desplaza el dw, por lo que es necesario desplazarlo manualmente desde código. Mandando un mensaje al control podremos hacer scroll en los dw desde el código:


Felices Reyes

04 enero 2007

Convertir a números cardinales

En más de una ocasión me he visto en la necesidad de convertir el contenido de una variable numérica a un string con el número en cardinal representado con texto, así que en adelante si me sucede ya sólo tendré que llegarme hasta aquí. En esta ocasión principalmente encontrareis el código sin muchos comentarios como otras veces, deciros que es mejorable, que se puede hacer más genérica y tratar el género, la moneda para trabajar con distintas divisas, las apócopes, etc. En mi caso la he centrado en el Euro con sus correspondientes céntimos, si cambia la moneda que usáis y también tiene fracción, solo tendréis que cambiar los literales correspondientes. La declaración de la función sería: string f_numberToCardinal(decimal adc_value) y el código quedaría así:


Algo que es interesante es el uso de la recursividad para resolver el
tema de los céntimos... ¿Qué os parece? Bueno, para no salirme de la tónica... espero que también os sirva a vosotros también.

30 noviembre 2006

Números contenidos en otros

Si habéis trabajado con las APIs u otros lenguajes de programación conoceréis que determinados parámetros de muchas funciones están compuestos por la suma de más de un número, de tal modo que dicho número posteriormente permite conocer cuales son los que le componen. Para empezar, los números utilizados para conseguir la suma deben ser el doble que el anterior o si se representan en binario solo los que tengan el primer bit a uno:
  1          1
  2         10
  4        100
  8       1000
 16      10000
 32     100000
 64    1000000
128   10000000

Según lo anterior, un parámetro que contenga el número 145 estará compuesto por los números 128 + 16 + 1, otros entornos tienen su propia función para determinar si un número esta contenido en otro (por decirlo de algún modo), y no es el caso de PowerBuilder, por lo que tendremos que crearnos nuestra propia función para determinar esto, y así poder utilizar este tipo de números para trabajar con Apis o para nuestro uso directo. Según el ejemplo anterior el 145 en binario sería 10010001, de tal modo que es fácil determinar si el número decimal 16 (en binario 10000) esta dentro del 145, pasando ambos a binario y comprobando si la posición (de derecha a izquierda) del bit a 1 del número buscado, también es 1 en el número en el que se busca:

10010001 número en el que se busca
00010000 número buscado

Con la lógica seguida hasta este punto ya solo queda pasar ambos número a binario, por lo que, de modo elegante, sería una función para la operación de conversión y otra para la comparación del bit:



Convertir en binario - string xx_aBinario(ulong aul_numero):



Comparación del bit - contieneNumero(ulong aul_numeroContenedor,ulong aul_numeroContenido):



Este es un método como otro cualquiera, hay muchos otros, recuerdo que antes de conocer el tema de los bit en los binarios me dedicaba a montar un bucle donde obtenía el número mayor doble del anterior partiendo desde uno y que fuera inferior al numero contenedor, el resultado era el mayor contenido, después al restar al contenedor el número mayor contenido, repetía la operación y obtenía así cada uno de los contenidos en la suma, claro esta que en cuanto aparecía el que buscaba salia, en caso contrario indicaba que no estaba contenido... y esto sigue, si os dais una vuelta por las PFCs encontrareis otro método.

Como siempre, espero que os sirva.

30 octubre 2006

Trabajar con años y meses comerciales

He podido ver en más de un programa como se trabaja con formulas para cálculos de interés con años y meses comerciales (en adelante calendario comercial) y luego para realizar operaciones entre fechas o liquidaciones se utiliza el calendario natural, por lo que al final no corresponden los resultados finales. La cuestión de todo el descabale se basa en que se utilizan funciones convencionales para calcular la diferencia entre dos fechas, cuando en realidad este tipo de formulas no funcionan, ya que asignan a cada mes su día final correcto según el calendario natural, pero el comercial, siempre debe tener 30 días, tanto si es febrero, como bisiesto o tiene 31 días.

Para evitar esto desarrollé un método que determina diferencia entre fechas utilizando el calendario comercial y con un par de funciones extras.

El método utiliza como argumentos iniciales dos fechas (desde y hasta), que son entre las que se tiene que calcular la diferencia, por otro lado, aparece otro parámetro de tipo boleano que permite indicar si hay que realizar ajuste por día de inicio o final de mes de la fecha hasta... no me parece claro como lo explique, así que un ejemplo: Si el día de la fecha "desde" es superior al día de la fecha "hasta" y el día de la fecha "hasta" es final de mes, se realiza un ajuste para que cuadre el resultado en meses. Ahora con fechas y números reales, si a una fecha inicial 30/12/2006 le sumamos dos meses (que no 60 días) obtenemos una fecha real final 28/02/2007, si descomponemos ambas fechas pasando a días cada una de sus partes y obtenemos la diferencia, nos arroja en este caso un resultado de 58, sin embargo en este caso para cálculos comerciales, me puede interesar que nos de los 60 días correspondientes a los 2 meses comerciales, ya que el fecha final es la que es porque no existe 30/02/xxxx. Este caso se detecta comprobando que la fecha "hasta" es un final de mes y que el día de la fecha "desde" es superior al día de la fecha "hasta", para lo que habría que indicar que se tengan en cuenta los días de diferencia entre ambas fechas en el resultado final.

Tener en cuenta si en nuestras cuentas de días se deben considerar los dos días pasados como argumentos o solo uno ellos. Fijaros que en el primer caso del 15/xx/xxxx al 15/xx+1/xxxx transcurren 31 días y no 30. Para considerar este último comentario en el algoritmo resultante solo tendríais que añadir, o no, una unidad al total resultante. Todo esto queda del siguiente modo:



Como siempre, espero que os sea de utilidad al resto.

29 septiembre 2006

Crear objetos de Forma dinámica en un UserObject

Este post se origina con la respuesta que he dado en un foro de Sybase a este tema. Me parece interesante dejarlo aquí para que no se quede en el tintero, luego es muy difícil localizar temas antiguos en los foros.

La problemática surge de que en PB no se puede añadir un objeto, creado de modo dinámico, sobre un objeto de usuario visual. Sin embargo esto si se puede hacer sobre una ventana, así que utilizaremos esta opción para lograr lo que PB no nos permite hacer directamente.

Como se puede resolver... pues muy sencillo, hacemos el CREATE del objeto que queremos poner dinámicamente, le asignamos false a la propiedad visible y utilizamos la función OpenUserObject para pegarlo sobre la propia ventana donde se encuentra nuestro objeto visual, como el objeto esta invisible no se ve, después cambiamos mediante una función de API el padre del objeto creado dinámicamente y le asignamos como nuevo padre nuestro objeto visual y para terminar y antes de cambiar el atributo de visible, asignamos las propiedades que queremos al objeto en cuestión... Así se pierde mucho uno, por lo que lo mostraré con código. Reutilizaré el del post con las modificaciones necesarias para que funcione correctamente. Para poneros en antecedentes se trata de un objeto visual de usuario que se utilizará de barra de herramientas:

VARIABLES DE INSTANCIA DEL UO_BARRA_BOTONES:



DECLARACIÓN DE FUNCIÓN EXTERNA:



CONSTRUCTOR DEL UO_BARRA_BOTONES:



MÉTODO ADD:



Espero que os sirva a todos.