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