30 de junio de 2018

Acceder a objetos y su código dentro de contenedores

Muchas veces, mientras trabajamos con los Diseñadores de clases y formularios, repetimos acciones de búsqueda de controles que se encuentran debajo de uno o varios contenedores, o de búsqueda de código relativo a controles. En este escrito, resumo algunas de las técnicas que nos puedes ayudar a ganar productividad.

Acceder a objetos y su código dentro de contenedores

Hoy vamos a comentar sobre técnicas para lograr un acceso más rápido a controles dentro de contenedores y al código de métodos o eventos de estos controles.

Vamos a suponer que tenemos un contenedor (por ejemplo un formulario), que contiene, digamos un pageframe y en una de sus páginas otro contenedor con otro grupo de controles y etc etc. Cada vez que deseamos posicionarnos en un control determinado tenemos varias variantes, por cierto, todas tediosas.

Podemos, por ejemplo, acceder desde la ventana Propiedades. En la esquina superior existe un cuadro desplegable con todos los controles existentes en el formulario o clase en cuestión. Si nos desplazamos por esta lista desplegada nos podemos colocar directamente en el control deseado. Pero sucede que a veces, como en nuestro caso, todos los controles no caben en esta lista lo cual hace necesario buscar por la lista y luego posicionarnos en el control.

Otra opción es aprovechar las teclas de acceso directo que han sido programadas y vienen de forma nativa con VFP. Así tenemos que al trabajar con los contenedores, tales como el pageframe (marco de página) se puede modificar rápidamente el objeto dentro del contenedor utilizando Ctrl+Clic sobre el objeto. Esto "pasa por encima" del contenedor y permite tomar el control del objeto que está dentro. Si existen contenedores dentro de otros contenedores, puede utilizar Ctrl+Shift+Clic para profundizar aun más en el nivel. Por ejemplo, en nuestro caso, tenemos un control optiongroup en una página del pageframe, entonces podemos acceder a uno de los botones presionando Ctrl+Shift+Clic, y VFP pasa por encima de los controles pageframe, page y optiongroups y va directo al control optionbutton.

Pero la verdad es que, al menos yo, no suelo acordarme nunca de esto. Y he aquí que me he encontrado algo que me parece fenomenal en estos casos en que tenemos muchos controles y mucha herencia de contenedores. Se trata de un árbol de contenedores y es sacado de http://fox.wikis.com/wc.dll?Wiki~ContainerTree

Su autor, Burkhard Stiller, ha presentado un código que nos permite obtener un árbol para acceder directamente a cada objeto de nuestra clase o formulario. Es muy cómodo especialmente si tenemos gran cantidad de objetos o controles dentro de varios niveles de contenedores. Es preciso tener VFP 9.0 para poderlo aplicar.

Se trata sólo de copiar y pegar en un editor de programas y ejecutar siempre que nos sea necesario. Automáticamente rellena el árbol a partir del formulario o clase activa. Según el autor puede encontrar datos que estén dentro de un pageframe, que están a su vez, dentro de otro pageframe y a su vez dentro de otro pageframe, .... así sucesivamente ...De esta forma nos ilustra la profundidad de contenedores que tiene en cuenta. Todos los controles son tenidos en cuenta, sin importar si tiene el método SetFocus(). Se puede emplear en contenedores basados en SCX y VSC.

Veamos una imagen para irnos familiarizando. Como vemos, nos encontramos con un árbol en el que se encuentran todos los controles y los contenedores son nodos que se abren para dejar ver los controles contenidos. Podemos cerrar y abrir los nodos que no vamos a necesitar y de esta forma nuestra lista de controles no está abierta para todos aquellos que no vamos a necesitar.

Pero aun no terminan las bondades de este aporte, que como han dicho, puede ser candidato a formar parte del proyecto Sedna. Bueno, ¿qué más tiene? Pues es Anclable, lo que significa que lo podemos mantener abierto formando parte de nuestra configuración y así tenemos que al cerrar el formulario actual y abrir uno nuevo, bastaría con pararnos en la ficha FrmClassTree (que además, podemos renombrar), yo le he llamado Arbol_controles y lo tengo en mi ventana de comandos junto a las otras ventanas que me gusta mantener: la ventana Propiedades, la ventana Sesión de Datos y la ventana Vista de Documento

Para llamar a esta utilidad basta con ejecutar su programa; pero ... ¿qué tal si nos acordamos de IntelliSense? Pues, podemos agregar un registro de usuario a nuestra tabla FoxCode.DBF e indicar una abreviatura y el código o la llamada al prg, como nos parezca mejor. De esta forma, bastaría con escribir la abreviatura para activar nuestro árbol de controles para la sesión de trabajo actual.

¿Qué tal? Yo lo encuentro muy cómodo. ¿Y ... qué tal un acceso directo a los métodos o eventos de los controles contenidos? Pues también hay posibilidad de lograrlo y nos la brinda VFP de forma nativa.

Tenemos la posibilidad de acceder desde la ventana propiedades, ficha Métodos. Lo que sucede es que nos tendríamos que colocar primeramente en el control para que se muestren sus métodos. Otra variante es desde el diseñador de Formularios / Clases; pero ... nuevamente tenemos que localizar el control y luego el método asociado. Aquí la ventaja sobre seleccionarlo desde la ventana Propiedades es que los procedimientos que tienen código en la instancia actual se muestran al inicio para cada control y muchas veces, si no son muchos, no hay que desplazarse.

Una forma más o menos cómoda es recorrer con las teclas de Avance y Retroceso de páginas desde la ventana de códigos de los Métodos y eventos. Esto lo que va a hacer es que nos movamos por los distintos procedimientos que hemos creado para todos los controles. Eso puede ayudar; pero... lo que sí es verdaderamente productivo es emplear una herramienta de VFP a partir de 8.0 que se llama Vista del Documento. La podemos mantener activada todo el tiempo y es válida para programas de procedimientos o con los Diseñadores de Clases y Formularios.

En la figura hay un ejemplo donde los controles no tienen contenedores adicionales; pero trabaja de la misma forma si los controles están contenidos, independientemente de la complejidad de la herencia de contenedores. La vista de Documentos se puede mantener anclada a la ventana comandos y siempre activa, ya que VFP guarda la configuración al salir.

Pues hasta aquí algunas ideas para lograr acceso directo a controles y el código vinculado a ellos. Aplicando estas técnicas ganamos en productividad, tan necesaria para nuestros desarrollos.

Saludos,

Ana María Bisbé York

24 de junio de 2018

Conocer la fecha en que fue iniciado el servidor de SQLServer

Es posible saber cuándo fué iniciado el SQLServer (o MSDE), a travéz de VFP usando técnicas SPT (SQL Pass Through).

lcServer = "(local)"
TEXT TO lcConnString NOSHOW TEXTMERGE
[DRIVER=SQL Server;SERVER=< < lcServer > >;
DATABASE=tempdb;Network=DBMSSOCN;
Trusted_Connection=Yes]
ENDTEXT
lnHandle = SQLStringConnect(lcConnString)
IF lnHandle > 0
   TEXT TO lcQuery NOSHOW 
      SELECT crdate AS dFecha
        FROM master.dbo.sysdatabases
        WHERE name = 'tempdb' 
   ENDTEXT

   IF SQLExec(lnHandle,lcQuery,"cSQLServer") > 0
        Messagebox("Fecha de Inicio del Servidor:"+cSQLServer.dFecha)
   ELSE
       IF AERROR(laError) > 0
           Messagebox("Error al consultar fecha de Inico de SQLServer"+;
               CHR(13)+"Error:"+laError[2])
       ELSE
           Messagebox("Error inesperado...")
       ENDIF
   ENDIF
ELSE
   IF AERROR(laError) > 0
       Messagebox("Error al intentar conectar"+CHR(13)+;
              "Error:"+laError[2])
   ELSE
          Messagebox("Error inesperado al intentar conectar"+CHR(13)+;
                "Error:"+laError[2])
   ENDIF
ENDIF

Espero que les sea de utilidad.

Espartaco Palma Martínez

18 de junio de 2018

Manipular Ventanas desde VFP - Parte 2

En Esta Ocasion envio un Formulario que permite modificar el tamaño y la posicion de una ventana cualquiera desde VFP, y para variar por medio de APIs.

DEFINE CLASS cambia_ventanas AS form
 Height = 158
 Width = 351
 ShowWindow = 2
 DoCreate = .T.
 AutoCenter = .T.
 BorderStyle = 2
 Caption = "Manipulando Ventanas 2"
 TitleBar = 0
 Name = "Cambia_Ventanas"

 ADD OBJECT salir AS commandbutton WITH ;
  Top = 120, ;
  Left = 176, ;
  Height = 27, ;
  Width = 84, ;
  FontBold = .T., ;
  Caption = "Salir", ;
  TabIndex = 13, ;
  ForeColor = RGB(239,58,58), ;
  Name = "Salir"

 ADD OBJECT texto AS textbox WITH ;
  Height = 23, ;
  Left = 98, ;
  TabIndex = 3, ;
  Top = 36, ;
  Width = 240, ;
  Name = "texto"

 ADD OBJECT label1 AS label WITH ;
  AutoSize = .T., ;
  FontBold = .T., ;
  Caption = "Caption", ;
  Height = 17, ;
  Left = 14, ;
  Top = 39, ;
  Width = 45, ;
  TabIndex = 2, ;
  ForeColor = RGB(50,41,218), ;
  Name = "Label1"

 ADD OBJECT cx AS textbox WITH ;
  Alignment = 3, ;
  Value = 0, ;
  Format = "k", ;
  Height = 23, ;
  InputMask = "999,999", ;
  Left = 98, ;
  TabIndex = 5, ;
  Top = 60, ;
  Width = 72, ;
  Name = "cX"

 ADD OBJECT label2 AS label WITH ;
  AutoSize = .T., ;
  FontBold = .T., ;
  Caption = "Posicicion X", ;
  Height = 17, ;
  Left = 14, ;
  Top = 63, ;
  Width = 72, ;
  TabIndex = 4, ;
  ForeColor = RGB(50,41,218), ;
  Name = "Label2"

 ADD OBJECT cy AS textbox WITH ;
  Alignment = 3, ;
  Value = 0, ;
  Format = "k", ;
  Height = 23, ;
  InputMask = "999,999", ;
  Left = 266, ;
  TabIndex = 7, ;
  Top = 60, ;
  Width = 72, ;
  Name = "cY"

 ADD OBJECT label4 AS label WITH ;
  AutoSize = .T., ;
  FontBold = .T., ;
  Caption = "Posicicion Y", ;
  Height = 17, ;
  Left = 194, ;
  Top = 63, ;
  Width = 71, ;
  TabIndex = 6, ;
  ForeColor = RGB(50,41,218), ;
  Name = "Label4"

 ADD OBJECT ancho AS textbox WITH ;
  Alignment = 3, ;
  Value = 0, ;
  Format = "k", ;
  Height = 23, ;
  InputMask = "999,999", ;
  Left = 98, ;
  TabIndex = 9, ;
  Top = 86, ;
  Width = 72, ;
  Name = "ancho"

 ADD OBJECT label3 AS label WITH ;
  AutoSize = .T., ;
  FontBold = .T., ;
  Caption = "Ancho", ;
  Height = 17, ;
  Left = 14, ;
  Top = 89, ;
  Width = 38, ;
  TabIndex = 8, ;
  ForeColor = RGB(50,41,218), ;
  Name = "Label3"

 ADD OBJECT alto AS textbox WITH ;
  Alignment = 3, ;
  Value = 0, ;
  Format = "k", ;
  Height = 23, ;
  InputMask = "999,999", ;
  Left = 266, ;
  TabIndex = 11, ;
  Top = 86, ;
  Width = 72, ;
  Name = "alto"

 ADD OBJECT label5 AS label WITH ;
  AutoSize = .T., ;
  FontBold = .T., ;
  Caption = "Alto", ;
  Height = 17, ;
  Left = 194, ;
  Top = 89, ;
  Width = 24, ;
  TabIndex = 10, ;
  ForeColor = RGB(50,41,218), ;
  Name = "Label5"

 ADD OBJECT label6 AS label WITH ;
  FontBold = .T., ;
  FontSize = 12, ;
  Alignment = 2, ;
  BackStyle = 0, ;
  Caption = "Manipulando Ventanas 2", ;
  Height = 26, ;
  Left = 3, ;
  Top = 4, ;
  Width = 346, ;
  TabIndex = 1, ;
  ColorSource = 0, ;
  ForeColor = RGB(26,62,206), ;
  BackColor = (thisform.backcolor), ;
  Name = "Label6"

 ADD OBJECT cambiar AS commandbutton WITH ;
  Top = 120, ;
  Left = 90, ;
  Height = 27, ;
  Width = 84, ;
  FontBold = .T., ;
  Caption = "Cambiar", ;
  TabIndex = 12, ;
  ForeColor = RGB(239,58,58), ;
  Name = "Cambiar"

 PROCEDURE salir.Click
  thisform.Release
 ENDPROC

 PROCEDURE cambiar.Click
  Local handle As Long
  Declare Long MoveWindow In "user32" Long HWnd, Long x, Long Y, Long nWidth, Long nHeight, Long bRepaint
  Declare Long FindWindow In "User32" String Clase, String texto
  Declare long IsWindowEnabled IN "user32" long HWnd 
  With Thisform
   handle = FindWindow(.Null.,Alltrim(.texto.Value))
   If handle =0
    Wait Window "Ventana No Encontrada..."
    Return
   Endif
   MoveWindow(handle, .cX.Value, .cY.Value, .ancho.Value, .alto.Value, 1)
   IsWindowEnabled(handle)
  Endwith
 ENDPROC

ENDDEFINE

Saludos.

Jorge Mota

11 de junio de 2018

Manipular ventanas desde VFP - Parte 1

En esta ocasión envío un pequeño programa que nos permite habilitar /deshabilitar cualquier ventana buscándola por medio de su Caption, y también podemos cambiar el Caption de cualquier ventana!!!

Al Deshabilitar una ventana no podremos dar Click ni escribir nada en esa ventana, ni siquiera restaurarla o maximizarla, cerrarla, moverla, minimizarla, etc. (útil si queremos que no puedan cerrar X ventana mientras corremos un proceso)

Solo hay que tener cuidado cuando busquen la ventana, tiene que ir exactamente igual que como aparece en el título de la misma, mayúsculas y minúsculas.

Aquí el código:

Public oFormulario
oFormulario=Newobject("Ventanas")
oFormulario.Show
Return

Define Class Ventanas As Form
 Top = 118
 Left = 121
 Height = 177
 Width = 465
 DoCreate = .T.
 Caption = "Manipulando Ventanas desde VFP"
 Name = "Manipula_Ventanas"

 Add Object deshabilita As CommandButton With ;
  Top = 136, ;
  Left = 24, ;
  Height = 27, ;
  Width = 84, ;
  Caption = "Deshabilitar", ;
  TabIndex = 5, ;
  Name = "deshabilita"

 Add Object Titulo_ventana As TextBox With ;
  BackStyle = 1, ;
  Height = 23, ;
  Left = 24, ;
  TabIndex = 2, ;
  Top = 30, ;
  Width = 420, ;
  Name = "Titulo_ventana"

 Add Object Habilitar As CommandButton With ;
  Top = 136, ;
  Left = 108, ;
  Height = 27, ;
  Width = 84, ;
  Caption = "Habilitar", ;
  TabIndex = 6, ;
  Name = "Habilitar"

 Add Object label1 As Label With ;
  AutoSize = .T., ;
  FontBold = .T., ;
  BackStyle = 0, ;
  Caption = "Titulo de La Ventana ", ;
  Height = 17, ;
  Left = 24, ;
  Top = 6, ;
  Width = 120, ;
  TabIndex = 1, ;
  Name = "Label1"

 Add Object Nuevo_Titulo As TextBox With ;
  BackStyle = 1, ;
  Height = 23, ;
  Left = 24, ;
  TabIndex = 3, ;
  Top = 77, ;
  Width = 420, ;
  Name = "Nuevo_Titulo"

 Add Object Cambiar As CommandButton With ;
  Top = 136, ;
  Left = 192, ;
  Height = 27, ;
  Width = 84, ;
  Caption = "Cambiar", ;
  TabIndex = 7, ;
  Name = "Cambiar"

 Add Object Estado As Label With ;
  AutoSize = .T., ;
  BackStyle = 0, ;
  Caption = "Estado de la Ventana:", ;
  Height = 17, ;
  Left = 24, ;
  Top = 112, ;
  Width = 122, ;
  TabIndex = 4, ;
  Name = "Estado",;
  Tag ="Estado de la Ventana:"

 Add Object label3 As Label With ;
  AutoSize = .T., ;
  FontBold = .T., ;
  BackStyle = 0, ;
  Caption = "Nuevo Titulo para la Ventana ", ;
  Height = 17, ;
  Left = 24, ;
  Top = 58, ;
  Width = 166, ;
  TabIndex = 1, ;
  Name = "Label3"

 Procedure Load
  Declare Long IsWindowEnabled In "user32" Long handle
  Declare Long EnableWindow In "user32" Long handle, Long fEnable
  Declare Integer FindWindow In WIN32API String cNULL, String cWinName
  Declare Long SetWindowText In "user32" Long handel, String lpString
 Endproc

 Procedure deshabilita.Click
  Local Estado, retval As Long, handle As Long
  handle = FindWindow(.Null.,Alltrim(Thisform.Titulo_ventana.Value))
  If handle=0 Or Empty(Thisform.Titulo_ventana.Text)
   Wait Window 'Ventana no Encontrada'
   Return
  Endif
  retval = EnableWindow(handle, 0)
  Estado= IsWindowEnabled(handle)
  If Estado=0
   Thisform.Estado.Caption =Alltrim(Thisform.Estado.Tag)+' Deshabilitada'
  Else
   Thisform.Estado.Caption =Alltrim(Thisform.Estado.Tag)+' Habilitada'
  Endif
 Endproc

 Procedure Habilitar.Click
  Local Estado, retval As Long, handle As Long
  handle = FindWindow(.Null.,Alltrim(Thisform.Titulo_ventana.Value))
  If handle=0 Or Empty(Thisform.Titulo_ventana.Text)
   Wait Window 'Ventana no Encontrada'
   Return
  Endif
  retval = EnableWindow(handle, 1)
  Estado= IsWindowEnabled(handle)
  If Estado=0
   Thisform.Estado.Caption =Alltrim(Thisform.Estado.Tag)+' Deshabilitada'
  Else
   Thisform.Estado.Caption =Alltrim(Thisform.Estado.Tag)+' Habilitada'
  Endif
 Endproc

 Procedure Cambiar.Click
  Local Estado, retval As Long, handle As Long
  handle = FindWindow(.Null.,Alltrim(Thisform.Titulo_ventana.Value))
  If handle=0
   Wait Window 'Ventana no Encontrada'
   Return
  Endif
  If Empty(Thisform.Nuevo_Titulo.Text) Or Empty(Thisform.Titulo_ventana.Text)
   Wait Window 'Debe escribir un Caption valido'
   Return
  Endif
  SetWindowText(handle, Alltrim(Thisform.Nuevo_Titulo.Text))
  Estado= IsWindowEnabled(handle)
  If Estado=0
   Thisform.Estado.Caption =Alltrim(Thisform.Estado.Tag)+' Deshabilitada'
  Else
   Thisform.Estado.Caption =Alltrim(Thisform.Estado.Tag)+' Habilitada'
  Endif
 Endproc
Enddefine

Saludos.

Jorge Mota

4 de junio de 2018

Engancharse a los Eventos de Un Objeto Cualquiera

Este código permite por ejemplo ejecutar código en el evento Moved del _Screen y en el evento Resize... ... también permite "Engancharse" cualquier otro objeto de VFP, siempre y cuando sea nativo de Visual FoxPro. Al querer colgarme al evento Activate del _Screen, a veces da Error.

Para colgarse a un cuadro de texto podemos definir

OBJETO = 'THISFORM.TEXTO1'

Y podríamos sobreescribir el evento Valid.

OBJETO = '_SCREEN'
_SCREEN.ADDOBJECT('HOOK_1', '_GANCHO')

DEFINE CLASS _GANCHO AS CUSTOM
 OBJEVALUADO = EVAL(OBJETO)

 PROCEDURE OBJEVALUADO.MOVED
  IF THIS.WINDOWSTATE = 0
   IF (THIS.LEFT < 0) OR (THIS.TOP < 0)
    THIS.AUTOCENTER=.T.
   ENDIF
  ENDIF  
 ENDPROC
 
 PROCEDURE OBJEVALUADO.RESIZE
  ACTIVATE SCREEN
  IF THIS.WINDOWSTATE = 1
   THIS.CAPTION = 'Minimizado'
  ENDIF
  IF THIS.WINDOWSTATE = 2
   THIS.CAPTION = 'Microsoft Visual FoxPro'
  ENDIF
  IF THIS.WINDOWSTATE = 0
   THIS.CAPTION = 'Normal'
   THIS.AUTOCENTER = .T.
  ENDIF
 ENDPROC

ENDDEFINE

Jorge Mota