Hola a todos,

estamos de vuelta, nuevamente,  tras un pequeño periodo de ausencia, motivado principalmente por temas de trabajo, aunque también os confieso, por asuntos relacionados con nuestra Comunidad. Aunque suene misterioso… ésto, dicho así, no tiene nada de misterio sino que hace referencia a la buena relación de muchos de los compañeros y amigos que colaboramos activamente y que nos lleva a proponer iniciativas y a compartir inquietudes para mejorar nuestra Comunidad. Estamos en ello y supongo que algún día veremos sus frutos. El hecho de que alguien “pierda” su tiempo pensando en cómo mejorar nuestro espacio y nuestros recursos ya es prometedor en si mismo.

De hecho, la tipología de esta entrada responde a una de esas inquietudes que me planteaba durante estos meses pasados y que me lleva a rescatar las lineas que hablaban de las cosas básicas, desde un punto de vista práctico y sencillo, como augura el título de “taller”, recuperando el espacio para aquellos contenidos  dirigidos a las personas que dan sus primeros pasos en el entorno. Y quizás por eso, no hablaban de las ultimas novedades sino de temas que afectan a nuestro trabajo diario.

Así que van a convivir unas y otras en el blog, compartiendo el espacio y mi tiempo. Creo que son cosas compatibles.

Estos primeros meses del año han traído algunas alegrías y también algunos sinsabores. Como alegrías cuento por ejemplo el que el grupo de facebook Delphi Solidario crezca y vaya tomando mas fuerza, como medio de compartir el día a día y las noticias de las últimas publicaciones, aspecto que es favorecido por la dinámica que aporta la red social y su inmediatez . Iniciamos el año con el objetivo de al menos llegar a 200 personas y ya superamos esa cifra. Y no dudo que durante todo este año quizas la doblemos (ehhhhh ¡veis que no perdí mi optimismo!) 🙂   Lo que mas cuesta es quizás ser constante y buscar cada mañana las novedades,  (aunque no sean en nuestra propia lengua) pero creo que vale la pena ese esfuerzo, aunque haya tenido como contrapartida que se ha visto reducido mi tiempo disponible para el blog.

También fue una alegría que viese la luz la última publicación en español sobre XE2, de la mano de F. Charte, autor de sobra conocido por todos nosotros y cuyo libro nos brinda una perspectiva amplia sobre la última versión de nuestra herramienta. Yo como muchos otros compañeros, me he sumado a quienes recomendaron su lectura y lo he hecho exhibiendo en varias partes de mis paginas la noticia de su publicación. El libro puede ser adquirido a través de Danysoft y como podéis ver en la foto, también me hice con uno de los ejemplares. No quise ser menos que mi amigo Germán y aquí tenéis la foto de rigor.

040420124521

¡Yo también voy a leer la Guía de Delphi!

En la parte negativa, seguro que ya os disteis cuenta que perdimos las páginas inestimables del Rinconcito Delphi, de nuestro amigo Jose Luis Freire,  que desde el 18 de Febrero cerró sus ventanas a la Red y nos dejó a muchos de nosotros la tristeza de ir perdiendo parte de nuestra historia como Comunidad. Quienes la han vivido, saben de lo que hablo y solo puedo decir en ese sentido que yo me he sentido especialmente triste por cuanto he colaborado con él en algunas de esas páginas que se escribieron, iniciativas que se llevaron a cabo (boletín de delphi, artículos añadidos procedentes de Sintesis, etc…) durante varios años. De momento reza el cartel de hasta aquí llegamos en su cabecera pero…

🙂

ya os he anticipado que el hecho de que exista un núcleo de personas que siguen apostando por la Comunidad es algo prometedor. Y tan pronto como sepa el final de esta historia y si tiene un final feliz, seréis los primeros en saberlo.

Lo dejamos ahí para centrarnos en nuestro taller, que aborda hoy un tema muy básico sobre mascaras de edición y rutinas de conversión de tipos.

Aunque me haya referido de forma conjunta en el título de la entrada, en principio no hay una correlación directa entre  EditMask y TFormatSettings. Veamos… Son cosas distintas: EditMask es una propiedad que hace referencia al editor de máscara para un determinado control de edición en la interfaz de nuestro usuario, y por tanto, establece qué caracteres serán validos entre los que va a proporcionar el usuario al escribir sobre la casilla de edición y cómo se disponen. Se podría decir que forzamos a que se ajusten a una regla previamente convenida y se busca facilitar su inserción, dado que existen parte de los caracteres que pueden ser omitidos, aunque figuren finalmente en el texto de la entrada. Es el caso por ejemplo de las barras de los formatos de fechas (__/__/___), que habitualmente se evita que  el usuario rellene, ganando en la comodidad de evitar su escritura, aun cuando figuren finalmente en la fecha. O el hecho de que garanticemos una única forma de escribir un DNI sin que el usuario se plantee si tiene que introducir el valor asociado intercalando puntos o sin ellos, con guión o sin guión, que puede ser motivo de que un mismo dato pueda ser escrito de forma distintas y generar exclusiones en las búsquedas o errores. Ese es el propósito de las máscaras.

Por el contrario, TFormatSettings es una estructura, un registro, que recoge información relacionada con el usuario y su localización (formato de fecha, separadores decimales y de millares, numero de decimales en uso, formato en cantidades negativas, moneda, etc) datos que son propios de la instalación del sistema y que se han configurado de acuerdo a las preferencias locales o del usuario.

TFormatSettings puede ser utilizada opcionalmente en algunas de las rutinas de conversión existentes en la unidad SysUtils, algo que nos ayudará en la tarea de convertir los tipos de dato fecha y decimal, a los que harán referencia muchas de nuestras máscaras. Esa es la razón que me ha hecho pensar en la conveniencia de que ambas compartieran esta entrada.

type TFormatSettings = record
	CurrencyFormat: Byte;
	NegCurrFormat: Byte;
	ThousandSeparator: Char;
	DecimalSeparator: Char;
	CurrencyDecimals: Byte;
	DateSeparator: Char;
	TimeSeparator: Char;
	ListSeparator: Char;
	CurrencyString: string;
	ShortDateFormat: string;
	LongDateFormat: string;
	TimeAMString: string;
	TimePMString: string;
	ShortTimeFormat: string;
	LongTimeFormat: string;
	ShortMonthNames: :TFormatSettings.:1;
	LongMonthNames: :TFormatSettings.:2;
	ShortDayNames: :TFormatSettings.:3;
	LongDayNames: :TFormatSettings.:4;
	TwoDigitYearCenturyWindow: Word;
     end;

En la parte superior, veis la estructura tal y como se define en Delphi. Los campos del registro son variados y en gran medida relacionados con la temporalidad y las distintas presentaciones de los formatos de fecha, aunque también contiene, como anticipábamos, campos relacionados con la presentación del formato decimal o de la moneda.

Ejemplos de posibles máscaras podrían ser “###0.00;1;_”, que permitiría insertar al usuario un numero de cuatro cifras y que nos muestre dos de sus decimales . O también podría ser la mascara de una cantidad monetaria (“#,##0.00 €;1;_”) que formatearía la entrada con un separador de millares,  un separador decimal y el símbolo de la moneda.

En cualquier caso, el guión nos dice que una vez introducidas por nuestro usuario, serán convertidas en un momento posterior a su tipo adecuado, que nos permitirá hacer las operaciones necesarias a efectos de obtener lo que sea. Así pues, en esa conversión  podríamos valernos de algunas funciones existentes en la unidad SysUtils, como StrToFloat( ) o TryStrToFloat( ) para convertir el texto, y ambas pueden utilizar la estructura TFormatSettings para efectuar correctamente dicha conversión.

Esa es la relacion que podemos observar entre la propiedad EditMask y TFormatSettings, que puede pasar inadvertida y ésto es una de las razones que me movió a escribir la entrada.

Pondré un ejemplo de por qué  me parece interesante compartir este comentario, especialmente con los compañeros que se inician en Delphi. Usemos nuestra imaginación e imaginemos un escenario muy sencillo, una interfaz que contiene un botón y un componente TMaskEdit, con una de las máscaras citadas anteriormente. Por ejemplo:  “###0.00;1;_”. El usuario introduce una cantidad cualquiera, 1234.56 y la pulsación del botón produce la ejecución de un proceso X que utiliza dicho valor.

procedure HazAlgoConLaCantidad(const MiCantidad: string);

Es de lógica, que en algún lugar de la implementación de esta función nos vamos a plantear la conversión de la cadena de texto a su tipo correcto, pudiendo usar las funciones StrToFloat( ) para ello. Hagamos algo como fvalor:= StrToFloat(maskinterfaz.Text); donde fValor es una variable de tipo Double y StrToFloat( ) la función que recibe como parámetro el texto de la entrada, formateado previamente por la mascara impuesta, y devuelve su valor convertido. Nos podemos sentir muy seguros y confiados. El usuario no va a poder introducir otra cosa distinta de la que nos marca la máscara y la conversión parece fiable. Nada puede pasar…

Y quizás lo peor que nos puede suceder es que esta conversión funcione  fruto de la casualidad de que el separador decimal usado en nuestra máscara coincida con el predefinido por el sistema, porque la función que nos ha permitido la conversión sí ha buscado este último para llevarla a cabo. Ese es el peor escenario y nuestro código queda expuesto potencialmente a sufrir un error de conversión de tipos que se puede dar en cualquier momento posterior en los equipos de nuestros usuarios. Quizás no valoramos que otros programas podrían interferir sobre la selección del punto decimal o de la coma como separador decimal. O quizás la localización de los usuarios de nuestra aplicación cambió, modificándose las preferencias de los sistemas en los que se ejecuta nuestro programa.

Estos comentarios pueden extrapolarse en gran medida a las conversiones de fecha también. Nuestra actitud debería ser  la de extremar la prudencia y no dar como supuesto algo que fácilmente puede cambiar.

El tipo TEditMask

Este tipo, representa la máscara que valida y formatea los caracteres de la entrada del usuario. Es un string o cadena que contiene 3 campos diferenciados, separados por puntos y comas (;). En primer lugar, expone la mascara en si misma (los caracteres válidos y las posiciones que ocupan). El segundo campo nos sirve para saber si las partes literales de la máscara van a ser guardadas o por el contrario se ignorarán. Y finalmente, el tercer campo nos informa sobre qué símbolo se mostrará en aquellos caracteres (opcionales o requeridos) de la entrada que no fueron rellenos y que el usuario puede dar valor; habitualmente se utiliza el guión bajo ( _ ). Es tan habitual que ya no damos importancia a las edición formateada de la fecha y los usuarios requieren cualquier facilidad que les facilite su trabajo. Aunque esa facilidad solo suponga que les evites la pulsación de una barra separadora. 🙂

Aunque podemos usar cualquier carácter en la máscara, algunos tienen un significado especial.

  • ! –  Puede ir presente y de ser así, los caracteres opcionales situados al inicio que no se rellenaron, se omitirán del texto que muestra el resultado. Si está presente, los considerará, aunque ignoraria los posteriores no rellenos.  Este es quizás el carácter especial que queda mas oscuro y entiendo que es porque depende de que el segundo campo sea cero (que no guarde literales). Os pido que nos detengamos un poco mas en su explicación y lo veamos con un ejemplo.
    Suponemos una máscara en la que se disponen cuatro caracteres opcionales, 2 literales y el carácter !  “!(aaaa);0;_“. Y suponemos que nuestro usuario tecleó el carácter “2” en la posición 4, lo cual supone que rellenó solo uno de los caracteres opcionales. Es decir, la entrada muestra “(__2_ )” pero ha procesado el valor resultante como “2 ” (de longitud 2). Es decir, ignoró los literales y de los opcionales se quedó aquellos no rellenados posteriores al carácter tecleado por el usuario.
    Sin embargo, si consideráramos la máscara   “(aaaa);0;_” con el mismo input por parte de nuestro usuario, el resultado de su proceso seria distinto. Seguiría mostrando en nuestro interfaz  “(__2_ )” pero el valor resultante sería ”  2″ (de longitud 3). Es decir, consideraría los espacios no introducidos por el usuario previos al carácter e ignoraría los posteriores (el carácter opcional no relleno).
  • > –  Si el carácter > aparece en la máscara, los caracteres que le siguen serán convertidos a mayúsculas hasta el final de la máscara o sea encontrado el carácter <.
  • < – Consideramos de forma similar a la opción anterior pero en este caso los caracteres son convertidos a minúsculas hasta que el caracter > es encontrado o nos situamos al final de la mascara
  • <>  -Lo usamos si queremos que no sea considerado el caso y se formatee la entrada tal y como la escribió el usuario.
  •  Este caracter nos permite usar los carácteres especiales como literales por lo que cualquier carácter especial precedido de se convierte en un literal.
  • L – El carácter L requiere un carácter alfabético en la posición en que aparece en la máscara. Admitiría los existentes entre [A-Z]  y[ a-z].
  • l  – El carácter l (L minúscula) permite igualmente un carácter alfabético pero en este caso es opcional.
  • A – El carácter A requiere un caracter alfanumérico en la posición en que aparece en la máscara, por lo que ademas de los caracteres alfabéticos ([A-Z], [a-z]) se amplía a los numéricos, [0-9].
  • a – El mismo caso que el anterior pero es opcional.
  • C – Permite un carácter arbitrario en la posición de la máscara.
  • c  – Permite un carácter arbitrario pero es opcional por lo que no es requerido.
  • 0 – El carácter 0 requiere un carácter numérico en la posición.
  • 9 – El carácter 9 permite un numero pero no lo requiere.
  • # – A diferencia del anterior, además de un carácter numérico permite los signos (+/-) siendo igualmente opcionales.
  • :   – Es el separador de horas, minutos y segundos en los formatos de tiempo y si es diferente del predefinido por el sistema, éste es usado en su lugar.
  • /  – Al igual que el anterior, el caracter / se usa para separar meses, días y años en las fechas. De igual forma, si difiere del predefinido por nuestro sistema, éste ultimo es considerado en su lugar.
  • ;  – El carácter especial ; es usado internamente para separar los campos de la máscara. Hacíamos referencia a estos campos al inicio de esta sección al hablar de la máscara en si misma, la posibilidad de guardar los literales o el carácter que consideramos como “blanco” (los que no han sido rellenos por el usuario).
  • _ – Finalmente, el carácter _ permite insertar automáticamente los espacios en el texto.
En la dockwiki de Embarcadero existe un ejemplo sobre trabajo con máscaras que podeis consultar: http://docwiki.embarcadero.com/CodeSamples/en/EditMask_(Delphi) pero creo que son bastante intuitivos los que aparecen en el editor. El tipo de construcciones que podemos considerar no pueden lógicamente tener la complejidad que nos dan las expresiones Regulares (http://www.delphibasico.com/?s=expresiones+regulares), tema que ya abordamos anteriormente en varias entradas. Son reglas sencillas de interpretar y conocer, si son comparadas con la potencia que nos ofrecen la validación de texto usando expresiones.
El principal exponente o representante del uso de las mascaras de edición no puede ser otro que un componente que cuente con las capacidades de edición y que además extienda las propias del uso referido. Es nuestro TMaskEdit, existente desde siempre en la paleta Additional de nuestra colección de componentes.  La propiedad EditMask (de tipo TEditMask), también está disponible en la clase TField por lo que las mascaras son susceptibles de ser usadas para validar la entrada en los campos de datos. Se supone que nuestro interfaz en ese caso contaría con un componente como  TDBEdit, que puede recibir la entrada de nuestro usuario para validar el texto introducido.
Sea como sea, la perdida del foco en la edición, dispara el proceso de validación del texto introducido por el usuario de forma que se evalúe si responde a la máscara requerida. Si es correcto, seguimos nuestro camino. Y si no lo es, se genera una excepción donde advertimos que el texto no la cumple, retornando el foco al control de edición, de forma que pueda ser corregido por el usuario.
Hablamos de aspectos tan básicos que creo recordar el editor en tiempo de diseño en el ide, en todas las versiones que he conocido y no cambió desde entonces. Luce este aspecto en Delphi 2010, que es el entorno en el que he hecho el código que acompaña esta entrada.
editor1
La sencillez de contar con un patrón que valide las entradas de texto, es algo que normalmente no valoramos o no le damos importancia. Es mas, leemos opiniones un tanto apresuradas sobre éste editor, del tipo “no lo uses…, es diabolico…, etc” en respuesta  de preguntas de foros, proponiendo alternativas extravagantes. Mi amigo Juan Antonio me hubiera dicho eso de que no lo vuelvas a inventar si ya está inventado. Simplemente úsalo de forma correcta.

 Consideración sobre TFormatSettings

Ya hemos visto la estructura de este registro y conocemos que información guarda. El detalle lo podeis encontrar también en la docwiki de Embarcadero si accedeis al siguiente enlace:

..delphivclwin32/SysUtils_TFormatSettings.html

 En la página se nos muestran varias ideas importantes. Que es una estructura de datos que contiene información local que puede ser usada en rutinas de formateo. Que cada miembro de ella es equivalente a una variable global con el mismo nombre. Y finalmente, que esta estructura puede ser usada en sustitución de la generada por el contexto, que puede no ser segura.
A menudo recibimos sugerencias de usar estas variables globales que se definen en la unidad SysUtils. como podría ser DecimalSeparator, para cambiar el valor de éste caracter pero como podeis ver por la documentación, estos atributos globales están marcados como deprecated y se desaconseja el uso. En su lugar deberíamos usar los campos de la estructura TFormatSettings. Lo podéis leer en:
Su uso es sencillo. Declara una variable del tipo e invoca una función que rellene sus campos de forma que podamos obtener esa información. Hablamos de GetLocaleFormatSettings.
Esta función recibe como parámetros, un identificador basado sobre el Windows Locale ID y un segundo parametro que va a albergar la referencia a la estructura, de forma que al finalizar la ejecución de la función podemos acceder a nuestro registro y evaluar sus campos de acuerdo a nuestras necesidades: saber que carácter sirve de separación decimal, el separador de los millares, los formatos de fecha, etc…
En el ejemplo que veremos a continuación, he invocado la función en la creación del formulario para rellenar la estructura,
GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, fset);
donde LOCALE_SYSTEM_DEFAULT es una constante que nos permite obtener los valores de nuestro sistema, los que tenemos por defecto. Y  fset, una variable de tipo TFormatSettings.  En las paginas de Zarko Gajic también podeis ampliar información sobre la existencia de mas constantes, propias de cada pais, etc…

Resumiendo…

Todo lo comentado hasta ahora tendría poco sentido si la estructura no fuera usada ampliamente en muchas de las rutinas de conversión. Siguiendo con el ejemplo que nos ocupa ponemos nuestra vista en la funcion TryStrToFloat
sobrecargada para que pueda ser usada con varios parámetros de tipo distintos y según distintas necesidades. Devuelve True si la conversión se efectuó con éxito y false si no lo tuvo. Y en el parámetro de salida (out) encontramos el valor, resultado de ese proceso de conversión de la cadena.
function TryStrToFloat(const S: string; out Value: Extended): Boolean; overload;
function TryStrToFloat(const S: string; out Value: Extended; const FormatSettings: TFormatSettings): Boolean; overload;
function TryStrToFloat(const S: string; out Value: Double): Boolean; overload;
function TryStrToFloat(const S: string; out Value: Double; const FormatSettings: TFormatSettings): Boolean; overload;
function TryStrToFloat(const S: string; out Value: Single): Boolean; overload;
function TryStrToFloat(const S: string; out Value: Single; const FormatSettings: TFormatSettings): Boolean; overload;

Existe una versión que considera el uso de la estructura y otra que usa los valores por defecto del sistema, en cada uno de los pares.
Estuve pensando durante estos días cómo preparar un pequeño ejemplo que permitiera ver claramente todos estos comentarios que hemos compartido. En las lineas inferiores podeis descargar el pequeño ejemplo que ilustra el tema. El interfaz os muestra  dos componentes TMaskEdit con mascaras que tienen invertido el punto decimal. En la parte superior se nos indica los valores actuales del sistema.
Y el selector que aparece a la derecha del formulario, nos permite seleccionar cual separador decimal vamos a usar.
Así pues, la acción asociada a los botones convertir fallarán o tendrán éxito en la medida en que la máscara se aplica correctamente sobre la estructura y coincide con la selección. El código es muy sencillo y lo podeis extrapolar a cualquier otra referencia de la estructuras: formatos de fechas, etx… Tanto si enfocamos el problema desde el punto de vista de la edición como de la visualización del formato, puesto que las rutinas de conversión a formato cadena también van a admitirnos el uso de la estructura de información TFormatSettings.
El ejemplo se ha compilado con Delphi 2010.
test1
unit UTestMascara;

interface

uses
  Windows, StdCtrls, Controls, Mask, Classes, Forms, Sysutils, ExtCtrls;

type
  TTestMascara = class(TForm)
    mskEd1: TMaskEdit;
    mskEd2: TMaskEdit;
    bnConvertir1: TButton;
    bnConvertir2: TButton;
    lbValor: TLabel;
    lbDisplay: TLabel;
    rgpSelector: TRadioGroup;
    lbMascara1: TLabel;
    lbMascara2: TLabel;
    Label1: TLabel;
    Label2: TLabel;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure bnConvertir1Click(Sender: TObject);
    procedure bnConvertir2Click(Sender: TObject);
    procedure rgpSelectorClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    fset: TFormatSettings;
  public
    { Public declarations }
  end;

var
  TestMascara: TTestMascara;

implementation

{$R *.dfm}

procedure TTestMascara.bnConvertir1Click(Sender: TObject);
var
  f: Double;
  s: String;
begin
  f := 0.0;
  s := mskEd1.Text;

  if TryStrToFloat(s, f, fset) then
    lbDisplay.Caption := Format('Correcta la mascara1: %n ', [f])
  else
    lbDisplay.Caption := 'Genera una excepción la máscara 1';
end;

procedure TTestMascara.bnConvertir2Click(Sender: TObject);
var
  f: Double;
  s: String;
begin
  f := 0.0;
  s := mskEd2.Text;

  if TryStrToFloat(s, f, fset) then
    lbDisplay.Caption := Format('Correcta la mascara2: %n ', [f])
  else
    lbDisplay.Caption := 'Genera una excepción la máscara 2';
end;

procedure TTestMascara.Button1Click(Sender: TObject);
begin
  Close;
end;

procedure TTestMascara.FormCreate(Sender: TObject);
const
  KInfo = 'El valor local de %n, usa [%s] para separar miles' +
    'y [%s] en el punto decimal';
var
  f: Double;
  sepmil, sepdec: Char;
begin
  GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, fset);

  sepmil := fset.ThousandSeparator;
  sepdec := fset.DecimalSeparator;

  case sepdec of
    ',': rgpSelector.ItemIndex := 0;
    '.': rgpSelector.ItemIndex := 1;
  else
    rgpSelector.ItemIndex := -1;
  end;

  // elegimos un numero cualquiera para hacer el test
  f := 1234.56;

  lbValor.Caption := Format(KInfo, [f, sepmil, sepdec]);
end;

procedure TTestMascara.rgpSelectorClick(Sender: TObject);
begin
  case rgpSelector.ItemIndex of
    { , } 0: fset.DecimalSeparator := ',';
    { . } 1: fset.DecimalSeparator := '.';
  end;
end;

end.

Descargar código fuente

Concluimos

Quedaron en el tintero algunos interrogantes pero son menores en mi opinión. No me gustaria finalizar sin hacer mención a uno de ellos, que es el separador de millares y que su presencia harían que fallaran las rutinas de conversión, dado que no pueden ser usados en funciones como TryStrToFloat( ) o similares. Lo podeis leer en la misma documentación:

..delphivclwin32/SysUtils_TryStrToFloat@string@Double.html

 Thousand separators and currency symbols are not allowed in the string. If S doesn’t contain a valid value, TryStrToFloat returns Default.  

Por lo que puestos a usar esa máscara, previamente a la conversión deberíamos extraer y eliminar el/los carácteres no válidos, de forma que no se genere la excepción. Esta representación numérica podría contener varios separadores de milllares, por lo que, suponiendo que fuera el punto decimal los sustituimos todos por una cadena vacía. Yo he usado aqui StringReplace pero hay algunas más disponibles.

StringReplace(MaskEdit1.Text, ‘.’, ”, [rfReplaceAll]);

Nos despedimos aquí. Espero que este taller práctico sea de vuestro agrado y os invito a que participéis activamente en nuestra comunidad y sumemos en el empeño de que sea mas útil y solidaria cada día.

 


		

6 comentarios sobre “Taller práctico – EditMask y TFormatSettings

  1. Muchas gracias Salvador. Como siempre, todo muy interesante, pero, especialmente para mí algunas cosas que van a ayudarme a hacer ciertos cambios (a mejor) en mis proyectos.

    Gracias de nuevo.

    1. Hola:

      🙂

      No me des las gracias. Me alegro de que puedan ayudar a alguién. En todo caso soy yo quien te agradezco el comentario. Es bueno recibirlos (cualquier feedback siempre es bien recibido) y alegra saber que existen compañeros que valoran el esfuerzo de mantener activo el blog.
      Un saludo,
      Salvador

  2. Ya sabes que el ser humano tiene mucho de mezquino. También de todo lo contrario, como has demostrado tú mismo todos estos años. Para mí (y estoy seguro de que para muchos otros también, lo digamos o no lo digamos) eres una institución en este mundo, y, hace muchos años que se te lee. ¡Y ojalá sean muchos más!

  3. Esta bastante bueno tu ejemplo..
    Pero en mi caso no estoy usando un EditMask1 sino un Edit1 que lo estoy Llamando EdtPrecio con lo limito a ingresar Números y Uno (1) carácter (bien sea “,” ó “.” para el separador decimal)… Gracias a tu aporte complete el script que tome de otro lugar compartido por otro blogero…

    Asi lo realice yo:

    unit UnitLaboratorios;

    interface

    uses

    type
    TFormLaboratorios = class(TForm)

    EdtPrecio: TEdit;

    procedure EdtPrecioKeyPress(Sender: TObject; var Key: Char);

    private
    { Private declarations }
    fset: TFormatSettings;
    public
    { Public declarations }

    end;

    procedure TFormLaboratorios.EdtPrecioKeyPress(Sender: TObject; var Key: Char);
    var
    sepdec: Char;
    begin
    GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, fset);
    sepdec := fset.DecimalSeparator;
    if (pos(sepdec,(sender as TEdit).Text)=0) then
    begin
    if not (key in [‘0’..’9′,sepdec,#8]) then key:=#0;
    end
    else if not (key in [‘0’..’9′,#8]) then key:=#0;
    EdtPrecio.ShowHint:=true;
    end;

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s