http://doughennig.blogspot.com/2008/06/using-microsoft-date-and-time-picker.html
Autor: Doug Hennig
Traducido por: Luis María Guayán
He usado el control ActiveX Microsoft Date and Time Picker (DTPicker). Ayer tropecé con un tema interesante. En primer lugar, algunos antecedentes.
El control tiene cuatro modos diferentes de ingreso de datos, a través de la propiedad
Format
: fecha corta, fecha larga, hora, y personalizado. Lo bueno de los tres primeros, es que utilizan automáticamente los ajustes de fecha y hora de la Configuración Regional en el Panel de control, por lo que no tiene que preocuparse por cuestiones de localización. La cuarta, se utiliza para los formatos personalizados. El formato de hora está bien si sólo desea la hora, pero si quiere mostrar tanto la fecha y la hora, tiene que definir la propiedad
Format
a 3 para personalizarlo, luego, establezca la propiedad CustomFormat
al formato deseado. Por ejemplo, "MM/dd/yyy HH:mm:ss" utiliza dos dígitos para la mayoría de los valores y los cuatro dígitos del año (sí, cuatro, aunque la cadena de formato utiliza tres). Sin embargo, aquí está la cuestión: ¿Cómo saber qué formato usar? El usuario puede estar utilizando MM/DD/AAAA, DD/MM/AAAA, o cualquier otra variedad de formatos. Afortunadamente, VFP tiene varias funciones que retornan los formatos de fechas.
SET("DATE")
retorna valores como MDY o American para el formato MM/DD/AAAA, DMY o BRITISH para el formato DD/MM/AAAA, y así sucesivamente. SET("MARK")
retorna el carácter que separa las partes de la fecha (como por ejemplo "/" o "-").Tengo una subclase del control DTPicker llamada SFDatePicker (en realidad, es un contenedor que contiene un DTPicker) que ofrece funcionalidades adicionales, incluyendo el soporte a fechas vacías, datos obligatorios, como así también el control de formato. El control de formato se maneja a través de la propiedad personalizada
lDateTime
; el valor por omisión es .F.
que significa que sólo la fecha aparece, mientras que .T.
significa que se mostrará la fecha y la hora conjuntamente.Suelte uno en un formulario, establezca la propiedad
cControlSource
a el control fuente que desea, configure lDateTime
a .T.
si desea la fecha y la hora, y listo. El siguiente código en el método
SetCustomFormat
de SFDatePicker, que se llama en los métodos Init
y Assiggn
de lDateTime
(en caso de que cambie la propiedad por programación), es necesario establecer CustomFormat
: with This lcFormat = set('DATE') if lcFormat <> 'SHORT' or .lDateTime .oleDTPicker.Object.Format = 3 lcMark = set('MARK') do case case inlist(lcFormat, 'AMERICAN', 'MDY', 'USA', 'SHORT') lcCustomFormat = 'MM' + lcMark + 'dd' + lcMark + 'yyy' case inlist(lcFormat, 'BRITISH', 'DMY', 'FRENCH', ; 'GERMAN', 'ITALIAN') lcCustomFormat = 'dd' + lcMark + 'MM' + lcMark + 'yyy' case inlist(lcFormat, 'JAPAN', 'YMD', 'TAIWAN', 'ANSI') lcCustomFormat = 'yyy' + lcMark + 'MM' + lcMark + 'dd' endcase if .lDateTime lcCustomFormat = lcCustomFormat + ' HH:mm:ss' endif .lDateTime .oleDTPicker.CustomFormat = lcCustomFormat endif lcFormat <> 'SHORT' ... endwithParece que este código se ocupa de los diferentes formatos de fecha adecuadamente, incluído el caracter de separación de la fecha, por lo que ¿cuál es el problema? Es muy sutil: Si utiliza
SET SYSFORMATS ON
, o sea que debe respetar la configuración regional del usuario, SET("DATE")
retorna "SHORT". Este código asume que las fechas "cortas" son tratados como MDY.Ahora el problema es cómo determinar cuál es el formato de fecha del usuario actual.Imaginé que una función de la API de Windows se ocuparía de esto, y encontré algunas posibilidades en MSDN, pero parece que estas funciones no pueden ser llamadas directamente desde VFP, ya que requieren funciones de llamada. Entonces, tomé el planteamiento de la fuerza bruta:
if lcFormat = 'SHORT' ldDate = date(2008, 1, 3) lcDate = dtoc(ldDate) lnPos1 = at('8', lcDate) lnPos2 = at('1', lcDate) lnPos3 = at('3', lcDate) do case case lnPos1 < lnPos2 and lnPos2 < lnPos3 lcFormat = 'YMD' case lnPos1 > lnPos2 and lnPos2 > lnPos3 lcFormat = 'DMY' case lnPos1 > lnPos3 and lnPos3 > lnPos2 lcFormat = 'MDY' endcase endif lcFormat = 'SHORT'Este código utiliza 01/03/2008 (en formato MDY) como una fecha, lo convierte en una cadena (que respeta la configuración regional de los usuarios), entonces de acuerdo al orden de los digitos para el mes, día y año, establecemos en consecuencia
lcFormat
. Feo, sí, pero funciona, así que adelante con ella hasta que una solución más elegante esté disponible.
Actualización: Imaginé que había una forma mejor de hacer esto. Mientras miraba el código de la linda clase ctl32_datepicker de Carlos Alloatti, me encontré que el uso
SET('DATE',1)
. Buscando en el archivo de ayuda de VFP, descubrí que esta era exactamente la función que yo necesito. No puedo creer que no conocia esto (o mas probable) que lo olvide. De esta manera ahrora el código es mas sencillo y limpio: if lcFormat = 'SHORT' lnDate = set('DATE', 1) do case case lnDate = 0 lcFormat = 'MDY' case lnDate = 1 lcFormat = 'DMY' otherwise lcFormat = 'YMD' endcase endif lcFormat = 'SHORT'Si eres curioso sobre cómo las otras características trabajan, aquí están los detalles.
Soporte a ControlSource: Como he mencionado anteriormente, cControlSource es una propiedad personalizada que contiene el nombre del ControlSource si así se desea. También hay una propiedad Value, por lo que el control puede estar ligado otros controles. Refresh actualiza Value desde el ControlSource:
if not empty(This.cControlSource) This.Value = evaluate(This.cControlSource) endif not empty(This.cControlSource)El evento Change del DTPicker, que se dispara cuando el usuario cambia la fecha y/u hora, plantea un evento personalizado DateChanged del contenedores. DateChanged actualiza el ControlSource, lo que podría ser un campo en un cursor u otra cosa, como una propiedad de un objeto:
with This lcAlias = juststem(.cControlSource) lcField = justext(.cControlSource) do case case empty(.cControlSource) case used(lcAlias) replace &lcField with .Value in (lcAlias) otherwise store .Value to (.cControlSource) endcase endwithValue_Access obtiene el valor desde DTPicker, cambiando a un Fecha, si una FechaHora no es necesario:
luValue = This.oleDTPicker.Object.Value if not This.lDateTime luValue = ttod(luValue) endif not This.lDateTime return luValueSoporte de Fecha en blanco: Si ha trabajado con el control DTPicker, sabe que no le gustan las fechas en blanco. Así, el código en Value_Assign utiliza la fecha y hora actual en ese caso. Además, ya que DTPicker espera que su propiedad Value tome un valor de fecha y hora, tenemos que manejar el paso a fecha:
lparameters tuValue local luValue with This do case case empty(tuValue) luValue = datetime() case vartype(tuValue) = 'D' luValue = dtot(tuValue) case vartype(tuValue) = 'T' luValue = tuValue otherwise luValue = .NULL. endcase if not isnull(luValue) try .oleDTPicker.Object.Value = luValue if not .CalledFromThisClass() raiseevent(This, 'DateChanged') endif not .CalledFromThisClass() catch endtry endif not isnull(luValue) endwith