Ir al contenido


Foto

Escalabilidad de formularios en aplicaciones Delphi


  • Por favor identifícate para responder
22 respuestas en este tema

#1 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 17 noviembre 2010 - 01:23

(I)

Pongo aquí un extenso artículo sobre la escalabilidad de formularios diseñados en Delphi. Esta es una característica de la clase TForm, vigente desde la primera versión de Delphi, que hasta la fecha no había puesto en práctica en mis aplicaciones y que ahora, debido a un cambio de equipo que me ha llevado a ver su funcionamiento, he tenido que investigar y adaptar. Este tema no está todo lo bien documentado que sería deseable en la ayuda de Delphi, por lo cual he empleado bastante tiempo en descifrar las claves de cómo está implementado, las cuales obedecen a una lógica algo peculiar que conviene conocer y que entraña cierta complejidad. Tampoco he localizado mucha información en la web, si acaso tres o cuatro foros donde se planteaban estas cuestiones y casi siempre sin llegar a una conclusión definitiva.

Puede que a algunos esto no os merezca mucha atención, pero estoy seguro de que en los próximos años, con la creciente proliferación de monitores de gran tamaño, la escalabilidad visual de nuestras aplicaciones será un requisito casi elemental.

Configuración de Windows

Me introduje en este tema hace unos meses cuando adquirí un equipo nuevo, que venía con Windows 7, y a su vez me hice con un monitor panorámico de 22 pulgadas, un pantallón con el que trabajar a gusto distribuyendo todas las ventanas laterales que trae Delphi sin quitar espacio para el formulario que se está diseñando. Pues bien, con una resolución de 1920 x 1080 pixels, se dispone de un gran espacio de trabajo, pero, a su vez, todo se ve bastante pequeño, me refiero a las letras, iconos del escritorio ... etc. Para verlo todo más grande, muchas veces optamos por disminuir la resolución de pantalla, pero en ese caso perdemos ancho o alto para "encajar" más ventanas, aparte de que esto nos resta superficie para ver/editar imágenes grandes y precisamente es algo que quería evitar. Windows 7 nos ofrece una forma rápida para conseguir verlo todo más grande (Windows Vista también, aunque de forma ligeramente diferente), si vamos a Pantalla en Panel de Control, veremos que se pueden ajustar los elementos del escritorio a un tamaño Más pequeño (100%), que es el predeterminado, a uno Mediano (125%) o a uno Más grande (150%) - (ver Nota 1). De esta forma, no perdemos la resolución del monitor y en cambio los iconos del escritorio, menús y barra de tareas, así como las fuentes asociadas, se ven a un tamaño que no cansa a los ojos. En Windows Vista hay que ir al Panel de Control - Apariencia y personalización y pulsar "Ajustar tamaños de fuentes (PPP)", pudiendo elegir entre Escala predeterminada (96 ppp) o Escala grande (120 ppp). En Windows XP, aunque esté más oculto y por lo tanto sea menos utilizado, se logra lo mismo accediendo a la ventana de Propiedades de Pantalla - Configuración - botón Opciones avanzadas - pestaña General - Configuración en puntos por pulgada. Windows anteriores como 2000, 98 o 95 logran algo de forma igual o parecida a XP, ofreciéndolo bajo la elección entre fuentes pequeñas o grandes.

Cuando alteramos estas opciones, Windows modifica el valor de unos parámetros de sistema utilizados para mostrar cualquier elemento en pantalla: LOGPIXELSX y LOGPIXELSY. Estos parámetros indican cuántos píxels corresponden a una pulgada en un dispositivo de salida, bien sea una impresora, un monitor ... etc, también llamados DPI (Dots Per Inch, en inglés), o PPP (Puntos Por Pulgada, en castellano). De esta forma, Windows no envía ninguna orden a la tarjeta gráfica para que reajuste la resolución, en cambio se altera el tamaño de las fuentes del sistema y deja a la decisión de cada aplicación la forma como responda ante estos parámetros, a los que en adelante me referiré como DPI. Esa facilidad de W7 para cambiar la apariencia del entorno de trabajo, recurriendo directamente a cambiar los DPI, es lo que hizo que me diera cuenta de la forma como estos parámetros afectaban a mis aplicaciones con Delphi.

Por ejemplo, en W7, si establecemos para la pantalla un tamaño Mediano (125%), se fijan los DPI del sistema a 120, y todos los elementos del escritorio crecen proporcionalmente, así como en otras aplicaciones donde los menús y fuentes aparecen más grandes. Si abrimos un archivo gráfico con un programa como MS Paint, por ejemplo, con los DPI a 120, veremos que los menús y barras de herramientas son algo más grandes, en cambio la imagen mostrada no aparece ampliada, sino que ocupa todos los píxels que permite la resolución del monitor, ya que ésta no ha cambiado. Una forma fácil de ver qué apariencia tendrá una aplicación según distintos DPI, sin necesidad de cambiar la configuración de Windows, es abrir el IExplorer y poner el zoom (abajo a la derecha) a 125%, que equivale a unos 120 DPI, y si lo ponemos a 150% equivale a 144 DPI.

Pero esto afecta también a los programas propios que hayamos desarrollado con Delphi. La apariencia, cuando abrí ciertos programas propios, no podía ser más desastrosa: letras enormes, paneles y botones que habían crecido de tamaño hacia derecha y abajo, empujando con ello a otros controles alineados ...etc, en cambio, el tamaño del formulario se mantenía igual, por lo que casi un tercio de los controles habían desaparecido al quedar desplazados y se mostraba una barra de scroll, o las dos. Puesto que, como explicaré más adelante, esto también afecta mientras trabajamos con el IDE de Delphi, y en mi caso quiero tener fijado mi entorno a tamaño mediano (120 DPI), he tenido que estudiar a fondo el asunto y darle una solución.

Hay que aclarar que Delphi sólo toma en consideración el parámetro LOGPIXELSY, es decir los Puntos Por Pulgada verticales, ignorando los horizontales. Como veremos más adelante, dicho parámetro se lee al arrancar la aplicación y se guarda en la propiedad PixelsPerInch del objeto Screen (sólo lectura), también en la propiedad del mismo nombre de las clases TForm y TFont (lectura-escritura). Como consecuencia de esto, Delphi no contempla la posibilidad de escalar formularios ni controles de forma distorsionada, donde se den diferentes DPI para cada dimensión, algo que aunque es permitido por muchos monitores, no se encuentra en la configuración de Windows (al menos no en las opciones de usuario).

Pongo aquí una rutina para averiguar dichos parámetros referentes a la pantalla:



delphi
  1. procedure GetSystemDPI(var HorizDPI, VertDPI: Integer);
  2. var
  3.   DC: HDC;
  4. begin
  5.   DC := GetDC(0);
  6.   try
  7.     HorizDPI := GetDeviceCaps(DC, LOGPIXELSX);
  8.     VertDPI := GetDeviceCaps(DC, LOGPIXELSY);
  9.   finally
  10.     ReleaseDC(0, DC);
  11.   end;
  12. end;



En adelante expongo mis averiguaciones y conclusiones sobre cómo las aplicaciones Delphi se adaptan a estas configuraciones de Windows.


____________________________________


Nota 1: En realidad es algo más complicado ya que Windows Vista y W7 tienen dos formas de hacer que los elementos crezcan o disminuyan de tamaño, sin afectar en ambos a la resolución de la pantalla. En una modifica el valor de los DPI, ésta es la que puede afectar a nuestras aplicaciones. En la segunda ignoro qué parámetros modifica, pero no crea ningún efecto secundario a nuestras aplicaciones. Además, tanto en Windows Vista, pulsando el botón "Configuración personalizada de ppp", como en Windows 7, si pulsamos la opción "Establecer tamaño de texto personalizado" (también en versiones anteriores se usa una opción parecida), podemos establecer los pixels por pulgada de forma manual y a nuestro gusto. Pues bien, si en Vista o W7 activamos la casilla "Usar ajuste de ppp con el estilo de Windows XP", entonces se alteran los DPI del sistema; si lo desactivamos Windows cambiará el tamaño pero sin modificar los DPI, por lo que nuestras aplicaciones no se verán afectadas.

Otra curiosidad es que, en Windows 7, si nos atenemos a las tres opciones que por defecto nos ofrece desde el Panel de Control - Pantalla (y que seguramente sean los que más utilicen la mayoría de usuarios), cuando estamos en el tamaño Más pequeño (100%), los DPI tiene un valor de 96, si cambiamos el tamaño a Mediano (125%) los DPI valen 120, y si cambiamos a Más grande (150%) los DPI valen ¡¡ 96 !! (al menos en mi máquina, claro). ¿Curioso este último? Pues sí, por lo visto Windows desactiva por defecto la casilla de "Usar ajuste de ppp con el estilo de Windows XP" para el tamaño Más grande; si activamos esa casilla manualmente, los DPI pasarán a valer 144.

Todos estos cambios, al aplicarlos, requieren que Windows cierre la sesión. En versiones antiguas (2000 y anteriores) a su vez se pide instalar las fuentes correspondientes a cada escala, por lo que si no las encuentra en el sistema pide el CD de instalación.

  • 2

#2 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 17 noviembre 2010 - 01:24

(II)

Delphi: Propiedades involucradas

Son varias las propiedades pertenecientes a la clase TForm (en realidad heredadas de TCustomForm) que tienen algo que ver en el asunto, aunque algunas a primera vista no lo parezcan. En primer lugar está, obviamente, la propiedad Scaled. Si creamos un formulario nuevo, podemos ver desde el ObjectInspector que su valor por defecto es TRUE, indicando que está listo para ser escalado si el entorno lo requiere. Puede que muchos nunca hayáis modificado esta propiedad, que además se combina con otra, PixelsPerInch (ver Nota 2), que es el equivalente a los DPI de Windows anteriormente mencionados. Aunque esta propiedad la podemos modificar en cualquier momento, tras cargarse un formulario, sea en diseño o en ejecución, toma el valor de los DPI del sistema (si queremos saber los DPI del sistema, para evitar errores lo más seguro es consultar la propiedad PixelsPerInch del objeto Screen, de sólo lectura, aunque esto sólo nos sirva en tiempo de ejecución).

El caso es que si Scaled está a TRUE (por defecto), al cargar nuestro formulario éste se escalará a los DPI que tenga Windows en ese momento, que suele ser 96 para una pantalla a tamaño normal, o 120 cuando escalamos a tamaño mediano. Por ejemplo, si diseñamos nuestro formulario habiendo un DPI = 96 en el sistema, cuando ejecutemos la aplicación en un sistema con DPI = 120, el formulario y todos los controles que contenga se escalarán igualmente, aumentando el tamaño y la letra. Y al revés, si los DPI que había en diseño son mayores que al ejecutar, lo controles menguarán de tamaño.

Pero resulta que, como pude observar, algunos formularios, aunque escalaban sus controles, no escalaban su tamaño, en cambio me mostraban una o dos barras de scroll y se ocultaban a primera vista algunos controles desplazados a derecha y abajo. Tras varias pruebas y quebraderos de cabeza descubrí que no sucedía en todos los formularios. Resumiendo, ese mal funcionamiento inicial se puede evitar, jugando con algunas combinaciones de propiedades que conviene saber:

AutoScroll: Cuando esta propiedad es FALSE, al escalarse el formulario se escalan igualmente sus dimensiones, por lo tanto no quedan ocultos ningún control y el resultado final es el deseado. En cambio, si vale TRUE, el formulario sólo escala los controles pero queda con su tamaño original, mostrando normalmente barras de scroll y creando un efecto desagradable. Esta propiedad guarda cierta relación con otra, BorderStyle, con la que tenemos que ir con cuidado. Si modificamos BorderStyle a un valor entre bsSizeable ó bsSizeToolWin, se modifica "sin previo aviso" AutoScroll a TRUE. Y al revés, si ponemos BorderStyle a un valor entre bsDialog, bsNone, bsSingle o bsToolWindow, se cambia AutoScroll a FALSE. El segundo caso no es preocupante para nuestros propósitos, pero sí el primero, por lo que mi consejo es asignar en primer lugar BorderStyle al valor deseado, y en segundo lugar pondremos AutoScroll a FALSE.

Realmente es algo más complejo pues también intervienen las barras de scroll del formulario (propiedades HorzScrollBar y VertScrollBar), de modo que si alguna de ellas tiene su propiedad Range distinta de 0, se sigue la misma pauta que si AutoScroll fuera TRUE. Si alguien tiene curiosidad por saber cómo hace Delphi para saber, en el momento de cargar el formulario, si debe escalar sus dimensiones, está explicado en la Nota 3.

Otra curiosidad es que, en un primer momento, pensé que este error del escalado de tamaño estaba corregido en Delphi 2010, ya que los formularios que creaba con esa versión se comportaban correctamente. Pero no, no se trataba de una corrección sino de algo más simple: en Delphi 2010, cuando creamos un formulario nuevo, se pone la propiedad AutoScroll inicialmente a FALSE, mientras que en versiones anteriores, como la 4, 5, 6 y 7 (desconozco en otras) esta propiedad por defecto vale TRUE. He comprobado que en todas BorderStyle es bsSizeable, salvo si heredamos el formulario de un Dialog, donde BorderStyle inicialmente se pone a bsDialog.

ParentFont: Esta propiedad, común a todos los descendientes de TControl, puede darnos problemas si la ponemos a TRUE en un formulario (por defecto es FALSE en la clase TForm). Más adelante, en el punto titulado "¿Cómo se escalan las fuentes?" explico las razones con más detalle.


¿Cómo escala Delphi un formulario?

Para acabar de enrevesarlo un poco más, la forma como Delphi escala los formularios cuando encuentra en el sistema unos DPI diferentes puede chocarnos al principio. Si fijamos la propiedad Scaled a TRUE y alteramos el valor de PixelsPerInch,  al ejecutar veremos que efectivamente el formulario se escala  consecuentemente, aunque en realidad no lo hace en la proporción que  imaginamos. Cabría esperar que para calcular las dimensiones de los  controles y tamaño de las fuentes se multiplicara por los DPI actuales -tomados de la propiedad Screen.PixelsPerInch- y se dividiera por los DPI originales (los que había en diseño) -tomados de la propiedad del formulario PixelsPerInch-.  Por ejemplo, si un formulario tiene 600 x 400 pixels de ancho y alto  respectivamente (tomaremos las propiedades cliente, ClientWidth y ClientHeight, que son las que realmente escala Delphi), diseñado a 96 dpi y ejecutado a 120 dpi, aplicando el  factor de escala nos quedaría:

ClientWidth  := 600 * 120 div 96 = 750
ClientHeight := 400 * 120 div 96 = 500

(**)

Pues  bien, en realidad no sucede así. Si lo probamos con un formulario que  tenga esas dimensiones veremos que en muchas ocasiones, una vez  escalado, las dimensiones son diferentes a esos valores. ¿Por qué? La  explicación es que Delphi no aplica directamente sobre el formulario  esos factores de escala. En cambio, sólo los aplica sobre la fuente del  formulario, y a continuación compara la altura del texto con dicha  fuente ( Canvas.TextHeight('0') ) con la altura que tenía con la  fuente en la fase de diseño. Este último valor se almacena  "clandestinamente" al guardar el formulario en una propiedad llamada TextHeight a la que no podemos acceder ni por código ni mediante el ObjectInspector, puesto que sólo está declarada en el método DefineProperties,  con el único fin de ser guardada en el stream. Sí que podemos averiguar  su valor si abrimos el fichero de recursos .DFM asociado al formulario,  pero desde luego no está accesible para nuestra aplicación. Pues bien,  una vez comparadas la altura del texto anterior con la actual, si son  diferentes, aplica la escala a todos los controles mediante el método ScaleControls, pasándole como multiplicador la altura del texto actual y como divisor el valor leido de TextHeight. Si por ejemplo, tenemos un fuente Arial Normal de tamaño 10 asignada a la propiedad Font  del formulario anterior, la altura del texto almacenada será de 16  mientras que la altura del texto una vez escalada a 120 dpi será igual a  17. El escalado sobre las dimensiones y controles del formulario, en  este caso, se realizará mediante dichos factores:

ClientWidth  := 600 * 17 div 16 = 638
ClientHeight := 400 * 17 div 16 = 425

Como  vemos, por efecto del redondeo (el tamaño de las fuentes se almacena en  números enteros, no reales, y no siempre se mantiene la proporción con los DPI), el resultado final de esta doble conversión es diferente al  que obteníamos aplicando los valores de DPI y PixelsPerInch, incluso bastante diferente (alrededor de 100 pixels en el ejemplo). Hay casos en que coincidirá, sí, pero será pura coincidencia.

Esta forma singular de realizarlo obedece a la finalidad genuina de los DPI  en Windows, que era la de redimensionar el entorno de trabajo de  acuerdo a las fuentes utilizadas, así que, aunque a mí me causó cierta  sorpresa al principio, ahora le veo su lógica. De todas formas, una de  las pegas de este comportamiento es que, si cambiamos el tipo o tamaño  de fuente de nuestro formulario, al ejecutar obtendremos un escalado  diferente para cada caso, tengamos en cuenta que no todas las fuentes  escalan igual, si son TrueType avanzan de uno en uno su propiedad Size,  otras van de dos en dos ... aparte de que la fuente que Delphi toma  como muestra es la del formulario, que no tiene por qué coincidir con la  del resto de controles y que son las que el usuario verá realmente.  Esta variabilidad nos puede plantear problemas tanto si queremos  controlar las dimensiones reales que tendrán nuestros formularios al  escalarse, como si queremos saber el factor de escala utilizado para  aplicarlo más tarde a la hora de crear o colocar controles en tiempo de  ejecución (téngase en cuenta que el escalado de un formulario se realiza  justo en el momento de cargarse, pero será tarea nuestra el aplicarlo a  futuros controles que añadamos por código).

(*) Delphi utiliza las propiedades ClientWidth y ClientHeight para  escalar un formulario (pero no en los controles donde opera sobre Width y Height), de esta manera se escala el area cliente que es la que  realmente contiene los controles y se garantiza que ninguno caerá fuera  de dicha area. No hay que perder de vista que el area total del  formulario engloba los bordes y la barra de título, que además cambian  de grosor dependiendo de la versión de Windows, de esa manera el  escalado no se ve afectado por según qué SO estemos usando.

(**)  Aunque para los ejemplos, por claridad he utilizado los operadores de  multiplicación (*) y de división (div), en realidad todos los escalados  se realizan mediante la función MulDiv. He detectado algún caso en que no devuelve el mismo resultado (como mucho hay una unidad de diferencia).

_____________________________________

Nota 2: El modo como la clase TCustomForm implementa estas dos propiedades es algo peculiar. La propiedad Scaled no se guarda en ninguna variable interna sino que "interpreta" el valor de la variable interna que hay asociada a la propiedad PixelsPerInch (una variable privada de tipo Integer llamada FPixelsPerInch). Cuando ponemos Scaled a FALSE, en realidad lo que hace es asignar 0 a dicha variable. Aunque de esto no nos damos cuenta puesto que, si a continuación leemos la propiedad PixelsPerInch, y ésta internamente vale 0, nos devuelve el valor extraído de la propiedad PixelsPerInch del objeto Screen (ese valor lo toma del sistema al arrancar la aplicación y es de sólo lectura). Si ponemos Scaled a TRUE, la variable interna FPixelsPerInch se resetea y toma el valor de la propiedad PixelsPerInch del objeto Screen; y, haciéndolo al revés, si asignamos cualquier valor superior a 36 a la propiedad PixelsPerInch, entonces Scaled pasa igualmente a ser TRUE.

Además, las propiedades Scaled o PixelsPerInch de un formulario sólo tienen sentido cuando se carga el formulario (es decir, al crearse y leer los datos del archivo de recursos). En ese momento, se comparan la propiedad PixelsPerInch con los DPI del sistema, y si son diferentes y Scaled es True, se realiza el escalado. A continuación, la propiedad PixelsPerInch toma el valor de los DPI del sistema. Esto último lo podemos comprobar fácilmente durante el diseño mirando el Object Inspector: si por ejemplo, tenemos un formulario con PixelsPerInch = 96, lo cerramos y cambiamos en Windows los DPI del sistema a 120, y volvemos a abrirlo desde Delphi, veremos que la propiedad PixelsPerInch ahora vale 120, sin que nosotros hallamos intervenido.

Por lo tanto, cualquier asignación de estas propiedades en tiempo de ejecución no alterará para nada la apariencia de nuestro formulario, ni se desencadenará ningún evento ni se escalará el mismo. Sólo tiene sentido alterarlas en tiempo de diseño para la próxima vez que sea cargado.


Nota 3: Delphi, a la hora de guardar las dimensiones de un formulario, sigue dos mecanismos diferentes. Si la propiedad AutoScroll está a True, o si alguna de sus barras de scroll (propiedades HorzScrollBar y VertScrollBar) tiene un rango diferente de 0 (propiedad Range), entonces almacena en el archivo .DFM las propiedades Width y Height, es decir anchura y altura del formulario entero. En caso contrario se almacenan las propiedades ClientWidth y ClientHeight, es decir anchura y altura del area cliente. Resulta que, cuando se carga el formulario, al asignar las propiedades ClientWidth y ClientHeight, los métodos encargados de dicha asignación modifican una variable interna llamada ScaleFlags, indicando que cada dimensión está pendiente de ser escalada, pero no sucede lo mismo con los métodos encargados de asignar las propiedades Width y Height. De esta manera los formularios, cuando se escalan, en el primer caso no modifican su tamaño, y en el segundo caso sí.
  • 0

#3 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 17 noviembre 2010 - 01:26

(III)

¿Cuándo escala Delphi un formulario?

La clase TForm es la encargada de administrar este proceso: un formulario se escala automáticamente al cargarse, es decir, una vez creado y tras el proceso de lectura de las propiedades del stream (fichero .dfm), concretamente en el interior del método ReadState. Si la propiedad Scaled está a TRUE y los DPI del sistema son diferentes al valor de la propiedad PixelsPerInch, entonces se escala la fuente y se compara la altura del texto con la propiedad TextHeight recién leída, y si son diferentes se escalan todos los controles y el tamaño del formulario (esto último si se cumplen las condiciones que hemos visto en un punto anterior).

Hay que recalcar que este proceso de escalado se produce tanto: en ejecución, al cargar la aplicación si el formulario estaba en la lista de los "auto create forms" o cuando lo creemos explícitamente por código; como en diseño cuando abrimos el formulario desde el IDE (esto lo explico más adelante).

Aparte de esto, la clase TForm hereda de TWinControl un método público de escalado llamado ScaleBy, declarado de la siguiente manera: procedure ScaleBy(M, D: Integer); cuyo parámetro M es el factor multiplicador, y D el divisor. Internamente este método llama a otro protegido y virtual llamado ChangeScale, propio de la clase TControl, que es el que se encarga realmente de escalar el tamaño y fuente del control, y con idénticos parámetros, y que en la clase TWinControl se sobreescribe para, a su vez, escalar los controles hijos, y en el caso de TForm se sobreescribe para un fin similar más alguna acción añadida.

De esta manera, si queremos escalar un formulario por código, con una simple llamada a su método ScaleBy lograremos escalarlo a él y a los controles que contiene (esto lo hace de forma recursiva hasta escalarlos todos). Una puntualización, en Delphi 4 y 5, si escalamos por nuestra cuenta usando el método ScaleBy, tendremos problemas con aquellos controles que tengan en su propiedad Anchors algún valor como akRight, akBottom. Simplemente dichos controles se posicionan mal sobre su control padre. A partir de Delphi 6 esto parece solucionado.


¿Qué propiedades escala Delphi en un control?

Si revisamos la VCL, concretamente el método ChangeScale de la clase TControl, veremos que Delphi escala, por defecto, tanto las dimensiones de un control, propiedades Width y Height, como su posición respecto al control padre, propiedades Left y Top, asimismo la propiedad Font. Una carencia de esto es que no se escalan otras propiedades relacionadas con el tamaño, como Constraints. ¿Qué sucede si he fijado en un control o en un formulario, por ejemplo un valor Constraints.MaxHeight = 200 y al escalarse necesita sobrepasar dicho valor? Curiosamente si el control desciende de TWinControl le importa un pepino la propiedad Constraints y la ignora por completo (un misterio), escalándose sin restricciones, pero no sucede lo mismo con los otros descendientes de TControl, que sí se ven limitados. Lo normal es que cuando establecemos una propiedad como Constraints estemos pensando en valores absolutos, pixels, y no reparemos en que dicho control o formulario pueda necesitar visualizarse a otra escala. Esto en realidad es un error de Delphi, que persiste hasta Delphi 7, incluído. Afortunadamente en Delphi 2010 (y espero que en algunas versiones anteriores también), esto ya está corregido, y se escala la propiedad Constraints así como Margins.

Expongo una relación de controles que sobreescriben el método ChangeScale para escalar alguna propiedad más, mirando la VCL de Delphi 7 y 2010 (no ha cambiado nada de una a otra):

TScrollingWinControl y TCustomForm: Escalan igualmente las barras de scroll (propiedades HorizScrollBar y VertScrollBar).
TCustomListView: Escala el ancho de cada columna.
TToolBar: Curiosamente se sobreescribe dicho método para no hacer nada, evitando incluso llamar al método heredado, ya que se entiende que las toolbars no deben poderse escalar.

Como se ve, muy pocos controles toman medidas adicionales, craso error. Sobre todo he echado en falta un tratamiento en la clase TDBGrid, cuya propiedad Columns no atiende al escalado cuando tiene columnas persistentes, mostrando el texto con el tamaño original. Tampoco en Delphi 2010 está corregido. Más abajo, en la Nota 4 pongo un código para escalar los TDBGrid "a mano".


¿Cómo se escalan las fuentes?

El escalado de las fuentes, por sus características, merece una mención aparte, ya que está condicionado por la peculiar manera de establecer el tamaño de las mismas. Si miramos las propiedades de un objeto TFont, por ejemplo desde el ObjectInspector, veremos que hay dos propiedades que sirven indistintamente para este propósito: Size y Height. Si cambiamos una, la otra también verá alterado su valor, de forma directamente proporcional aunque con distinto signo. Por norma solemos manejar la propiedad Size porque nos parece más intuitiva, se expresa en números positivos y es la que vemos en los cuadros de diálogo de fuentes en la mayoría de programas, sobre todos en editores de texto. Pero en realidad, en Delphi es la propiedad Height la que almacena el valor único que determina el tamaño de la fuente, y la que se guarda en el fichero .dfm. No tiene sentido, por tanto, almacenar ambas, y además, como veremos a continuación, la propiedad Size es variable y depende del entorno en que se utilice.

Si miramos el código fuente de la clase TFont (unit Graphics.pas) veremos que cuando leemos la propiedad Size (método privado GetSize), devuelve el resultado de aplicar una fórmula donde interviene el valor de Height, una constante (72) y ¡¡ sorpresa !!, una propiedad pública llamada PixelsPerInch (esta propiedad es de lectura-escritura y se inicializa en el constructor de TFont tomando el valor de los DPI del sistema). Y cuando asignamos un valor a Size lo que hace es modificar el valor de Height aplicando la inversa de la fórmula anterior. Es decir, Size no sólo está ligada a Height sino a los DPI del dispositivo en el que actúe dicha fuente. Como resultado de esta manera de funcionar, nos podemos llevar una sorpresa como la siguiente: una fuente cuya propiedad Size valía 8 bajo 96 dpi, al pasar a un entorno de 120 dpi su propiedad Size vale 7 (este hecho lo podemos comprobar fácilmente abriendo un programa en Delphi bajo distintas DPI y comparando la propiedad Font de cualquier control). En cambio, como era de esperar, su propiedad Height permanece invariable en ambos entornos. También al revés, puede sucedernos que, por efecto del redondeo, dos fuentes con distinta propiedad Height retornen el mismo valor en Size: se puede comprobar colocando dos controles, ambos con fuente Arial, en un entorno de 96 DPI, poniendo en uno Height = -13 y en el otro igual a -14, ambos devolverán Size = 8.

Implementación de la propiedad Size en la clase TFont:


delphi
  1. interface
  2.  
  3.   TFont = class(TGraphicsObject)
  4.     (...)
  5.   published
  6.     (...)
  7.     property Size: Integer read GetSize write SetSize stored False;
  8.     (...)
  9.   end;
  10.  
  11. implementation
  12.  
  13. (...)
  14.  
  15. function TFont.GetSize: Integer;
  16. begin
  17.   Result := -MulDiv(Height, 72, FPixelsPerInch);
  18. end;
  19.  
  20. procedure TFont.SetSize(Value: Integer);
  21. begin
  22.   Height := -MulDiv(Value, FPixelsPerInch, 72);
  23. end;



En otras palabras, la propiedad Size de la clase TFont expresa un valor relativo. ¿Por qué esta relatividad? Pues para favorecer la proporcionalidad en el dibujado de fuentes en distintos dispositivos, evitando al programador precisamente el tener que lidiar con escalados de fuentes de uno a otro. Con un ejemplo se entiende mejor: supongamos que estamos escribiendo sobre un control Memo en una pantalla a 96 DPI, con una fuente de cualquier tipo y Size = 12, y queremos enviar ese texto con la misma letra y tamaño a la impresora, que pongamos imprime a 720 DPI; si decidimos hacerlo escribiendo directamente sobre el Canvas de un objeto Printer, antes de dibujar el texto procederemos a asignar la fuente ¿no? Pues bien, una simple sentencia como "Printer.Canvas.Font := Memo1.Font" será suficiente. Esto que parece obvio, funciona gracias al siguiente mecanismo: cuando asignamos un objeto TFont a otro, perteneciente por ejemplo a un componente / control como en este caso, esta asignación se suele realizar mediante la llamada al método Assign de la clase TFont, el cual tras copiar todas las propiedades de una fuente a otra, compara si ambas tienen el mismo valor en PixelsPerInch y si no es así lo que hace es asignar la propiedad Size. Esta asignación provoca que la propiedad Height de la fuente destino tome un valor distinto al de la fuente de origen, pues se calcula a partir de unos DPI diferentes (ver método SetSize en el código anterior). En nuestro ejemplo, al detectar distintos valores en la propiedad PixelsPerInch de cada fuente, se copiaría el valor 12 tomado de Size de la fuente del Memo a la propiedad Size de la fuente del Canvas de la impresora, calculándose internamente el valor de Height para la fuente de la impresora. Si comparamos entonces las propiedades Height de una y otra fuente veremos valores muy dispares, aunque Size valga lo mismo en ambas. Y si lanzamos la impresión y comparamos veremos que efectivamente el tamaño "aparente" de la letra sobre el papel es igual -o muy semejante- al que vemos en pantalla, que es lo que se persigue.

De cara al escalado de formularios y controles, esta peculiaridad de las fuentes tiene alguna implicación:

- Al escalar una fuente, tarea que nos tocara hacer si programamos controles propios, o si por desgracia tenemos que escalar "a mano" alguna fuente extra de componentes cuyo autor no hizo bien sus deberes, en mi opinión se debería operar sobre la propiedad Height en vez de sobre Size, ya que no siempre devuelven el mismo resultado, y Height ofrece resultados previsibles no dependientes del entorno. Si alguien tiene dudas de estas diferencias que haga la siguiente prueba: colocar sobre un formulario dos etiquetas (TLabel) ambas con letra Arial normal y Height = -11 (especifico la propiedad Height porque es constante; si estamos trabajando con Delphi a 96 dpi, Size valdrá 8, y bajo 120 dpi valdrá 7). Finalmente lanzamos un procedimiento que escale ambas multiplicando por 10 y dividiendo por 8, pero en una actuando sobre la propiedad Size y en la otra actuando sobre Height:

Label1.Font.Size := MulDiv(Label1.Font.Size, 10, 8);
Label2.Font.Height := MulDiv(Label2.Font.Height, 10, 8);

El anterior código arroja diferentes resultados para cada fuente, incluso hay disparidad de resultados dependiendo de si lo ejecutamos bajo Windows con diferentes DPI. Pongo aquí los resultados:

En un entorno de DPI = 96

Label1      ANTES      DESPUES
  Height =  -11        -13
      Size =      8        10

Label2      ANTES      DESPUES
  Height =  -11        -14
      Size =      8        11


En un entorno de DPI = 120

Label1      ANTES      DESPUES
  Height =  -11        -15
      Size =      7          9

Label2      ANTES      DESPUES
  Height =  -11        -14
      Size =      7          8

Como podemos apreciar, la propiedad Height, antes de escalar, mantiene el mismo valor en ambos entornos (96 o 120 dpi), no así Size; y, una vez escalado, si hemos operado con Height (Label2), su valor resultante es el mismo en ambos entornos, mientras que al operar con Size (Label1), obtenemos valores dispares en ambas propiedades.

En este sentido, Delphi no se decanta por un método exclusivamente, sino que mantiene una dualidad: opera con Height al escalar la fuente del formulario para determinar la escala a aplicar (si Scaled es TRUE), como se explicó en un apartado anterior, pero a la hora de escalar la fuente de cada control, en el método ChangeScale, opera sobre la propiedad Size. Y si escalamos a "a mano" un formulario llamando al método ScaleBy, éste llama al método ChangeScale de la clase TForm que también opera con Size. Bajo mi punto de vista esta dualidad es un error, y de hecho he observado un efecto negativo causado por esta forma de proceder: en algunos formularios escalados, hay controles que, teniendo el mismo tipo y tamaño de fuente, escalan la fuente de manera diferente. ¿Por qué? Pues porque aun teniendo la misma fuente, en unos controles la propiedad ParentFont es TRUE, por lo que heredan la fuente del formulario la cual se escaló usando Height, mientras que aquellos cuya propiedad ParentFont es False la escalan usando Size. Y ambas, siendo originalmente idénticas, adoptan un tamaño diferente al escalarse.

Además, comparando las letras de las aplicaciones hechas con Delphi, una vez escaladas, con las letras del entorno de Windows, noto que Windows escala a un tamaño algo mayor, lo cual me hace pensar que quizás hubiera sido mejor adoptar el criterio de operar con Height. Pero, en fin, por compatibilidad con Delphi, viendo que mayoritariamente se opera con Size, lo más recomendable quizás sea, cuando nos toque hacerlo, escalar las fuentes operando también con Size.


ParentFont: Esta propiedad perteneciente a cualquier control, aunque no todos la publiquen, puede causarnos algún quebradero de cabeza a la hora del escalado. Si vemos el código fuente del método ChangeScale tal como está implementado en la clase TControl (unit Controls.pas), veremos que si ParentFont es TRUE, entonces la fuente no se escala. Esto tiene su lógica, pues se entiende que se toma prestada la fuente del control padre (Parent) que ya se encargará de escalarla y si éste también tiene ParentFont a TRUE, entonces será su control padre y así hasta llegar a un control que no herede su fuente. Esta forma de actuar, como ya muchos sabemos, nos da la flexibilidad de cambiar de golpe la fuente a un conjunto de controles hijos, si tienen dicha propiedad a TRUE, cambiando solamente la fuente del control padre. También tiene otra implicación y es que Delphi, cuando un control tiene ParentFont a TRUE, se ahorra escribir en el stream (fichero .dfm) toda la información relativa a la fuente. Pues bien, esto tiene dos implicaciones de cara al escalado:

1) Escalar un control aisladamente puede no dar el resultado apetecible si ParentFont es TRUE pues la fuente se queda sin escalar, ya que hereda la del control padre (no escalado). Aunque lo normal es escalar a nivel de formulario entero y no hacerlo con un control suelto, hay que tener esto en cuenta.

2) Cuando es el formulario quien tiene ParentFont a TRUE, ¿de dónde toma la fuente si no tiene un control padre por encima? En ese caso lo que hace es crear una fuente temporal, la cual se inicializa con los valores básicos (cada versión de Delphi establece los suyos propios) y a continuación asigna sus propiedades a la fuente del formulario. Pero la principal diferencia con el resto de controles es que mientras un control adapta automáticamente su fuente a la de su control padre cuando ponemos ParentControl a TRUE, el formulario no cambia su fuente, dejando la que ya había; sólo actualiza la fuente la siguiente vez que lo cargamos (ya sea al ejecutarse la aplicación o si lo volvemos a abrir desde el IDE). Esto plantea un problema que no me ha sido fácil de descubrir y que trataré de explicar de la mejor manera posible y que tiene que ver con el mecanismo que sigue la clase TForm a la hora de escalarse, como expliqué en un punto anterior. Según ese mecanismo, al cargarse el formulario, se compara el valor de la altura de la fuente almacenada en el stream con la altura de la fuente tras aplicarle el escalado de los DPI. Pues bien, si un formulario tiene ParentFont a TRUE, entonces no queda rastro en el .dfm de la fuente que tenía durante el diseño (Delphi no la almacena), por lo que se le asigna una por defecto, la cual probablemente sea diferente a la que había en diseño, en tipo y en tamaño. Si Scaled está a TRUE, entonces si ambas fuentes coinciden no habrá problemas, pero en el resto de casos se producirán unos desajustes poco agradables, tales como un formulario diminuto o gigante, que no guarda relación proporcional con los DPI del sistema. Se da incluso un caso curioso y es que sin cambiar los DPI de Windows, al ejecutar la aplicación el formulario se escalará igualmente: el simple hecho de que no coincida la altura del texto almacenada con la calculada es suficiente razón para que se escale. Y peor aún, si volvemos a abrir el IDE de Delphi nos encontraremos con un formulario escalado de forma caprichosa, aunque sin haber cambiado la configuración de Windows. Este comportamiento potencialmente "peligroso" perdura hasta Delphi 2010, incluido.

Por otra parte, poner la propiedad ParentFont del formulario a TRUE hace que el código que expongo más adelante para escalar "a mano", así como el componente TFormAutoScaler que también adjunto al final, se encuentren con el mismo problema y no funcionen correctamente. Mi consejo sólo puede ser dejar ParentFont a FALSE.

A decir verdad, esta clase de problemas de escalado también podrían producirse si la fuente empleada en nuestros formularios no se encontrara en el sistema donde se ejecute nuestra aplicación, aunque resulte menos probable (de ahí que se aconseja, al menos para el formulario, usar fuentes de uso corriente).
  • 0

#4 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 17 noviembre 2010 - 01:28

(IV)


Escalabilidad durante el diseño

Como he anticipado  antes, señalo otro aspecto que puede darnos algún que otro dolor de  cabeza. La escalabilidad no sólo nos afecta al ejecutar nuestras  aplicaciones sino también cuando entramos al IDE de Delphi para  diseñarlas, puesto que Delphi aplica igualmente ese escalado a los  formularios que carguemos en diseño. Por ejemplo, si cambiamos los DPI  del sistema a 120 y abrimos Delphi, veremos los menús y algunas  ventanas propias de Delphi con un aspecto mayor al habitual, esto es lo  normal y afecta al IDE pero ... ¡¡ PELIGRO !! igualmente los formularios  que estemos diseñando y sus controles se escalarán (si la propiedad Scaled está a TRUE, claro). Este escalado sólo sucederá si el proyecto fue modificado la última vez bajo unos DPI  diferentes, por supuesto, como me sucedió a mí al pasar los proyectos  creados en una máquina a 96 dpi a mi nueva máquina adaptada a 120 dpi.  Según gustos de cada cual, esto podría romper algunos esquemas de  trabajo, si tenemos unos tamaños fijos establecidos para cada tipo de  control. El caso es que, una vez escalados, si guardamos nuestro  proyecto, se guardará en el archivo .DFM las nuevas dimensiones de cada control. Uno puede pensar que no pasa nada, ya que al volver a un DPI  original los controles se reescalaran nuevamente a su tamaño original,  pero como he dicho antes los redondeos nos pueden jugar malas pasadas en estos casos y si diseñamos repetidamente sobre DPIs  diferentes, posiblemente las dimensiones de nuestros formularios y controles queden algo distorsionadas. Lo ideal sería que siempre diseñáramos bajo el mismo entorno, con los mismos DPI del  sistema; pero esto no siempre se puede conseguir, mucho menos en equipos de trabajo donde cada cual puede tener unas preferencias de escritorio diferentes.

Debido a este último punto, puede que para muchos la mejor opción sea fijar la propiedad Scaled a False en todos los formularios. Esto, por cierto, debe hacerse con precaución si abrimos Delphi con DPI  diferentes a las originales, puesto que al cargar el formulario, éste  se escalará previamente a cualquier cambio que queramos hacer. La  solución pasa en este caso por abrir y modificar directamente el archivo .DFM.

Para hacer esto de manera rápida pongo los fuentes, como  explico al final en el ANEXO 1, de una sencilla utilidad que permite  cambiar la propiedad Scaled (también PixelsPerInch y AutoScroll) de golpe a todos los formularios de nuestro proyecto desde fuera de Delphi, accediendo a los dicheros .DFM.

____________________

El trabajar un mismo proyecto bajo diferentes entornos (distintos DPI) también nos puede dar alguna sorpresa cuando añadimos un formulario nuevo, tanto si dejamos su propiedad Scaled a TRUE como si la cambiamos a FALSE, y está relacionado con la fuente del mismo. Veamos un ejemplo:

Si bajo 96 dpi creamos un formulario nuevo, Delphi le asigna la fuente por defecto, que, en la versión 7 y anteriores, es la MS Sans Serif, de Size = 8. Si lo creamos bajo 120 dpi le asigna la misma fuente con el mismo valor de 8 en la propiedad Size. Pero si nos fijamos en la propiedad Height, la que a mi juicio importa de verdad, bajo 96 dpi vale -11 y bajo 120 dpi vale -13. Cabría esperar que al abrir el proyecto bajo el otro entorno, estando Scaled a TRUE en el formulario, la fuente adoptaría el tamaño escalado equivalente. Pues NO. El formulario creado bajo 96 dpi escala su fuente a un Height = -14 al abrirlo bajo 120 dpi, y al revés, el formulario creado bajo 120 dpi al abrirlo bajo 96 dpi escala Height a -10; es decir, ninguno de los valores resultantes coincide con los que se obtenían en cada entorno al crear un formulario nuevo.

Este desajuste no tiene nada que ver con la forma de escalar las fuentes que comenté en el capítulo anterior, es decir, no depende de si se opera con Height o con Size (la fuente del formulario, de hecho, se escala operando con Height) sino simplemente con los redondeos que se aplican al trabajar con números enteros, siendo uno de los efectos negativos de aplicar un escalado en un sentido y luego en el inverso. Como consecuencia, tenemos dos formularios en el mismo proyecto con dos fuentes de distinto tamaño (aunque a veces se puedan dibujar iguales), lo cual dependiendo del escalado que se aplique al ejecutarlas dará unos escalados diferentes (en unos casos puede coincidir y en otros no); si además tenemos en cuenta que la forma como se escala la fuente del formulario es la que determina los factores de escala para todos sus controles y para su tamaño, nos podemos encontrar con dos formularios que parecen idénticos en diseño y aparecen diferentes al ejecutarlos.

Y en el caso de que cambiemos la propiedad Scaled a FALSE, no se darán esas conversiones al abrir el proyecto con Delphi, pero sencillamente tendremos formularios con letras de distinto tamaño dependiendo de bajo qué entorno se crearon, y si decidimos escalarlos por código darán las mismas inconsistencias.

Una medida a tomar para evitar estos defectos es tener escrito en una nota o tablilla, el valor de la propiedad Height para la fuente que usemos con asiduidad, para cada DPI diferente. Y al crear un formulario nuevo asignar el valor a dicha propiedad, olvidando el valor de Size, que como ya hemos visto es engañoso. Esto es mejor hacerlo nada más crear el formulario, antes de insertar controles.

Como ejemplo, si trabajamos con Delphi 7 o anteriores, teniendo en cuenta que la fuente nueva aplicada por Delphi suele ser MS Sans Serif de Size = 8, debemos asegurarnos de que bajo 96 dpi, Height valga -11, y bajo 120 dpi valga -14 (Delphi asigna -13 para este último caso).


______________________________________________



Hasta aquí he expuesto la forma de funcionar y las posibilidades que nos ofrece Delphi para afrontar este asunto, así como los posibles inconvenientes. Ahora expongo algunas alternativas posibles, a elegir según las necesidades y gustos de cada cual.

Soluciones y alternativas

Dependiendo de los DPI del entorno en que trabajemos, y también de los posibles escenarios donde se vayan a ejecutar nuestras aplicaciones, debemos estar preparados y para ello tenemos que elegir entre las dos opciones lógicas:

OPCIÓN A: Cambiar la propiedad Scaled a FALSE, cambio que, por norma general, tendremos que realizar en cada uno de los formularios de nuestra aplicación. De esta forma, al ejecutar la aplicación, independientemente de los DPI que tenga asignado Windows, nuestros controles y fuentes tendrán el mismo tamaño que cuando los diseñamos, evitándonos complicaciones de diseño. Esta opción puede ser la única válida cuando tenemos un formulario donde el tamaño de ciertos controles se establecen en valores absolutos (nº de pixels fijo) y donde un escalado puede distorsionar totalmente la apariencia deseada. El inconveniente de esta técnica es que nuestras aplicaciones no se corresponderán con las preferencias visuales del usuario en caso de que haya elegido un tamaño distinto, quedando en ocasiones más pequeñas -o más grandes- que el entorno de trabajo, y seguramente también sea un hándicap para quien quiera cumplir con los requisitos ideales de una aplicación Windows :-).

OPCIÓN B: Dejar la propiedad Scaled a TRUE, dejando que el tamaño de los controles y fuentes se adapten a las preferencias del usuario. Esto, sin embargo, requiere un trabajo de programación extra, y tendremos que hacer pruebas para ver el resultado con distintos escalados. Esta opción debe tener en cuenta las propiedades necesarias para que el formulario también cambie de tamaño, no dejando controles ocultos, como expliqué más arriba (propiedad AutoScroll).

Esta es la opción predeterminada de Delphi, y como posibles pegas las que he señalado con anterioridad: los formularios también se escalan durante la fase de diseño, y el escalado se realiza respecto a la fuente, lo cual ofrece cierta variabilidad de tamaños. Si optamos por esta solución y queremos saber la escala que ha sido aplicada por Delphi, podemos seguir el siguiente truco, que consiste en sobreescribir el método ChangeScale del formulario, el cual nos da dicha información en los parámetros M y D. Ahí podemos aprovechar para escalar elementos de ciertos controles que no escalen correctamente, o bien almacenar dichos valores para tenerlos en cuenta si más adelante creamos algún control por nuestra cuenta y queremos escalarlo:



delphi
  1.   TMainForm = class(TForm)
  2.     (...)
  3.   protected
  4.     procedure ChangeScale(M, D: Integer);  override;
  5.     (...)
  6.   end;
  7.  
  8. implementation
  9.  
  10. (...)
  11.  
  12. procedure TForm1.ChangeScale(M, D: Integer);
  13. var
  14.   i : Integer;
  15. begin
  16.   inherited ChangeScale(M, D);
  17. // aquí lanzamos el escalado de los controles que interesen, aplicando M y D.
  18. // O bien almacenamos M y D en sendas variables para cuando haga falta escalar
  19. // controles creados "al vuelo" con posterioridad.
  20.  
  21. // Ejemplo de ajustado de ciertas propiedades de un control TPageControl y de un
  22. // TStatusBar, ignoradas por Delphi
  23.   PageControl1.TabWidth := MulDiv(PageControl1.TabWidth, M, D);
  24.   PageControl1.TabHeight := MulDiv(PageControl1.TabHeight, M, D);
  25.   for i:=0 to StatusBar1.Panels.Count - 1 do
  26.     with StatusBar.Panels do Width := MulDiv(Width, M, D);
  27.  
  28. // Ejemplo de escalado de un TStringGrid (ancho de columnas y alto de filas)
  29.   for i:=0 to StringGrid1.ColCount - 1 do
  30.     StringGrid1.ColWidths[i] := MulDiv(StringGrid1.ColWidths[i], M, D);
  31.   for i:=0 to StringGrid1.RowCount - 1 do
  32.     StringGrid1.RowHeights[i] := MulDiv(StringGrid1.RowHeights[i], M, D);
  33. // escalamos también el alto por defecto por si añadimos más filas en adelante
  34.   StringGrid1.DefaultRowHeight := MulDiv(StringGrid1.DefaultRowHeight, M, D);
  35. // Opcionalmente podemos escalar el ancho de las columnas, si no venían fijadas
  36. // desde diseño
  37. //  StringGrid1.DefaultColWidth := MulDiv(StringGrid1.DefaultColWidth, M, D);
  38. end;

También podemos optar por una solución intermedia:

OPCIÓN AB:
Ésta es un híbrido de la A y la B, que consiste en dejar por norma general la propiedad Scaled a FALSE en todos los formularios, a fin de evitar principalmente los inconvenientes que se dan durante el diseño, y si se desea que en tiempo de ejecución se escalen nuestras aplicaciones al gusto del usuario, esto lo haremos por código. Tenemos el problema de que no hay forma de saber qué DPI había en tiempo de diseño para compararla con la de ejecución. ¿Por qué no hay forma de saberlo? Pues porque Delphi, al crear cada formulario, ya realiza dicha comparación y a continuación asigna el valor de los DPI del sistema a la propiedad PixelsPerInch, no quedando rastro del valor que en diseño tenía dicha propiedad (ver NOTA 2 en un apartado anterior). Puesto que desconocemos ese valor, propongo dos alternativas, en la primera tomamos como premisa un valor de PixelsPerInch = 96, y en la segunda hacemos uso de un componente que sí conoce los DPI que había en diseño:

=>[/]
  OPCIÓN AB-1: Es más imperfecta y hay que usarla con precaución, pero quizás interese a quien no le guste añadir más componentes a la paleta. Consiste en llamar desde el evento OnCreate de cada formulario a un procedimiento que se encarga de escalar a la vez los controles y el tamaño del formulario si es necesario. Esta alternativa funcionará correctamente si, además de tener la propiedad Scaled a FALSE, el formulario mantiene durante el diseño las dimensiones "normales", entendiendo como normales las que tendría si fuese diseñado con el DPI por defecto de 96. De hecho, si miramos el código veremos que se emplea dicho valor, a piñón fijo, operando junto con la propiedad PixelsPerInch del objeto Screen. Para escalar empleamos el método ScaledBy:



delphi
  1. // Precaución: sólo debe llamarse una vez, puesto que toma como base unos DPI fijos, 96,
  2. // que una vez aplicados ya no sirven de base para el siguiente escalado
  3. procedure EscalaFormulario(AForm: TForm; DPI: Integer; UseFontScale: Boolean = TRUE);
  4. const
  5.   BaseDPI = 96;
  6. var
  7.   W, H, M, D, OldHeight, SclHeight : Integer;
  8.   CR : TRect;
  9. begin
  10. // Si estaba previamente escalado salimos del procedimiento. Damos por sentado
  11. // que esta rutina se llama en el evento OnCreate del formulario y que si Scaled
  12. // es True es porque estaba así en el .dfm y por consiguiente se acaba de escalar
  13.   if AForm.Scaled then EXIT;
  14. // De inicio el multiplicador (M) será igual a DPI y el divisor (D) lo fijamos
  15. // al valor de BaseDPI, que es 96
  16.   M := DPI;
  17.   D := BaseDPI;
  18.   AForm.DisableAlign;
  19.   try
  20.   {  Si UseFontScale es True, las alturas del texto serán los factores de escala.
  21.     Almacenamos en D la altura anterior, luego escalamos la fuente del formulario
  22.     con los DPI recibidos y asignamos a M la altura actual. SclHeight memoriza
  23.     la propiedad Height de la fuente escalada por si luego hubiera que devolverla
  24.     a ese valor, consiguiendo de esa forma el mismo escalado que realiza TForm.
  25.     Finalmente restituimos el tamaño de la fuente a su valor anterior al escalado
  26.     (OldHeight) puesto que el método ScaleBy ya se encargará más tarde de volverla
  27.     a escalar    }
  28.     if UseFontScale then
  29.     begin
  30.       OldHeight := AForm.Font.Height;
  31.       D := AForm.Canvas.TextHeight('0');
  32.       AForm.Font.Height := MulDiv(AForm.Font.Height, DPI, BaseDPI);
  33.       SclHeight := AForm.Font.Height; // memorizamos este valor para más tarde
  34.       M := AForm.Canvas.TextHeight('0');
  35.       AForm.Font.Height := OldHeight;  // devolvemos el tamaño original a la fuente
  36.     end;
  37.     // Si multiplicador y divisor son diferentes ...
  38.     if M <> D then
  39.       with AForm do
  40.       begin
  41.         ScaleBy(M, D);
  42.     { La siguiente sentencia, si se habilita, deja la fuente del formulario
  43.       idéntica a la clase TForm cuando Scaled es True, es decir, basándose en
  44.       la fuente escalada operando con su propiedad Height en vez de sobre Size
  45.       (curiosamente ScaleBy escala respecto a Size). Sin embargo, está
  46.       deshabilitada porque dicho escalado a veces produce una letra de formulario
  47.       algo más grande que el resto y no es proporcional con el escalado aplicado
  48.       a las dimensiones de aquellos controles que heredan la fuente del
  49.       formulario (porque tienen ParentFont a True)        }
  50.  
  51.     // if UseFontScale then AForm.Font.Height := SclHeight;
  52.  
  53.     // si AutoScroll es False, el método ScaleBy ya se encarga de redimensionar
  54.     // el formulario. En caso contrario lo hacemos mediante el código siguiente:
  55.         if AutoScroll then
  56.         begin
  57.       // Es importante redimensionar partiendo del area cliente (ClientRect) que es
  58.       // la que realmente alberga los controles
  59.           CR := ClientRect;
  60.       // Para obtener el nuevo ancho (W) restamos en primer lugar del ancho
  61.       // global (Width) al ancho cliente (CR.Right), así tenemos el trozo de borde,
  62.       // que puede variar según la versión de Windows. Luego le sumamos el ancho
  63.       // cliente ya escalado.
  64.           W := Width - CR.Right + MulDiv(CR.Right, M, D);
  65.       // Para obtener el nuevo alto (H) restamos en primer lugar del alto
  66.       // global (Height) al alto cliente (CR.Bottom), así tenemos el trozo de borde
  67.       // más barra de título, que puede variar según la versión de Windows. Luego
  68.       // le sumamos el alto cliente ya escalado.
  69.           H := Height - CR.Bottom + MulDiv(CR.Bottom, M, D);
  70.       // Finalmente asignamos las nuevas dimensiones
  71.           SetBounds(Left, Top, W, H);
  72.         end;
  73.       end;
  74.   finally
  75.     AForm.EnableAlign;
  76.   end;
  77. end;
  78.  
  79. procedure TForm1.FormCreate(Sender: TObject);
  80. begin
  81.   EscalaFormulario(Self, Screen.PixelsPerInch);
  82. end;





=>[/]
  OPCIÓN AB-2: Consiste en colocar un componente creado para la ocasión (TFormAutoScaler), que hace todo el trabajo de escalado por sí solo. La ventaja de usar un componente es que éste sí puede conocer los DPI y la altura del texto (TextHeight) con la fuente que había durante el diseño, pues almacena dichos valores en el fichero .DFM, siguiendo el mismo método que la clase TForm. De esta forma, al crearse el formulario, compara los DPI de antes y después y toma la decisión oportuna. Este componente (ver ANEXO 2 al final) es muy sencillo de utilizar, basta con soltarlo sobre el formulario, y ya viene con las propiedades necesarias por defecto.
  • 0

#5 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 17 noviembre 2010 - 01:30

(V)


Recomendaciones para un buen escalado

Tanto si dejamos que la clase TForm escale nuestros formularios, dejando Scaled a TRUE, como si lo hacemos por código o usando un componente para ello, es conveniente tener en cuenta algunos consejos para que el resultado final sea el deseado y no obtengamos formularios caóticos. Pongo aquí algunas recomendaciones a tener en cuenta, incluyendo algunas resumidas que Borland ofrecía hace años:

* La decisión sobre la escalabilidad o no de los formularios de nuestra aplicación conviene tomarla pronto, antes de ponerse a colocar controles.

* Se aconseja usar fuentes TrueType, por aquello de que ofrecen un mejor aspecto al escalarse. Aunque en Delphi 2010 la fuente por defecto ya es la Tahoma, en la versión 7.0 y anteriores la fuente por defecto es la MS Sans Serif, que al aumentar de tamaño aparece demasiado pixelada; es mejor usar por ejemplo la Arial, que se dibuja mucho mejor, y además podemos asegurar que está en un 99'9 % de las máquinas con Windows. Las fuentes TrueType, además, aumentan o disminuyen su tamaño de unidad en unidad (cuando cambiamos la propiedad Size), mientras que las otras, como MS Sans Serif, por norma general sólo cambian a ciertos intervalos, normalmente a números pares, lo cual cuando se escala un formulario crea ciertos desajustes entre el tamaño de un control y el texto mostrado.

* Es aconsejable además, antes de empezar a colocar controles, fijar la fuente del formulario pues será la que por defecto heredará cada control que coloquemos luego. Si vamos a asignar fuentes diferentes dentro del mismo formulario, es recomendable que la fuente del formulario sea lo más parecida posible a la de la mayoría de los controles que contiene, ya que al escalarse se aplicará la proporción de dicha fuente sobre todas las demás: si usamos fuentes muy dispares, cada una crecerá a su manera (por ejemplo, he llegado a ver diferencias importantes cuando mezclo letras Arial con MS Sans Serif).

* Incluso dentro de un mismo control, si dispone de varias fuentes, como sucede con un TDBGrid donde podemos fijar distintas fuentes para cada columna, conviene que la fuente del control, propiedad Font, sea al menos igual de grande y del mismo tipo que la mayor fuente empleada en las columnas, para que escale el alto de cada fila a un tamaño suficiente para que ningún texto quede cortado.

* Para hacer nuestras aplicaciones más homogéneas, conviene que todos los formularios compartan la misma fuente base (propiedad Font de un TForm), así no escalará cada uno de forma diferente.

* Para cumplir con el punto anterior, es recomendable o bien trabajar siempre bajo el mismo entorno (mismos DPIs) o si esto no es posible, tener en cuenta los distintos tamaños de fuente (propiedad Height de TFont) que Delphi asigna por defecto a un formulario nuevo, y que variarán bajo diferentes DPI, tal como se explicó en el capítulo sobre Escalabilidad durante el diseño.

* Conviene dejar la propiedad ParentFont de un formulario SIEMPRE a FALSE. Como expliqué en el apartado dedicado a ¿Cómo se escalan las fuentes?, es causa de problemas y además no le veo ningún sentido cambiarla a TRUE.

* Si colocamos imágenes, conviene activar su propiedad Stretch (o una que cumpla la misma función), que hace que la imagen se ajuste al tamaño del control utilizado, ya sea un TImage u otro similar, así al escalarse no aparecerá sólo parcialmente, o con zonas vacías.

* Conviene dejar algún espacio entre controles (controles no alineados, claro), ya que al escalarse, puede darse el caso de que alguno se superponga sobre otro, aunque sólo sea un pixel, creando un mal efecto.

* Para evitar mareos, lo ideal sería no dejar controles "sueltos" y tenerlos todos alineados. Todos los descendientes de TControl disponen de la propiedad Align, aunque inexplicablemente no todos la publican, me refiero a versiones antiguas ya que al menos en Delphi 2010 en la mayoría ya está corregido. Uno de los casos más lamentables se da en los botones (clases TButton, TBitBtn, TSpeedButton ...). Aunque sea algo engorroso, si usamos Delphi 7 o anteriores, no sería mala idea derivar un componente por cada clase de botón, sólo para publicar la propiedad Align y poderlos alinear, al escalarse quedaría perfecto (la propiedad Anchors da problemas en versiones anteriores a la 6).

* Fijar la propiedad AutoScroll del formulario a FALSE, para que el formulario no muestre esas molestas barras de desplazamiento que, según Borland, dan a entender que el formulario no ha cambiado de tamaño pero sí su contenido. Obviamente, lo deseable es que el tamaño del formulario se escale al igual que los controles que contiene. Pero hay otra razón más sibilina para fijar esta propiedad a FALSE, y tiene que ver con las distintas apariencias que los sucesivos Windows dan a los formularios: si vemos la evolución de los formularios desde versiones antiguas, como Windows 95 y 98, pasando por Windows 2000 y XP hasta llegar a Vista y Windows 7, veremos que cambian el ancho de los bordes, así como el alto del área destinada a la barra de título. Aunque esto no tenga que ver con la escalabilidad, sí conviene evitar que por unos escasos pixels de menos, aparezca una barra de scroll lateral que a la vez fuerce a mostrar otra horizontal.

* Fijar la propiedad Position del formulario a un valor distinto de poDesigned (esto me parece recomendable en todos los casos).

* En las etiquetas (TLabel y descendientes) que estén alineados a izquierda o derecha (Align = alLeft/ alRight), poner su propiedad AutoSize a TRUE, para que el tamaño se ajuste al tipo de fuente escalado. A su vez, en todos los controles que muestren un texto, dejar un espacio libre de al menos un 25% de su tamaño, para que quepa el texto cuando la fuente se escale. Esto último es especialmente necesario en los controles TCheckBox y TRadioButton, que no disponen de una propiedad AutoSize y que con facilidad se muestran con el texto cortado al escalar. Si el control es multilinea, dejar una linea en blanco en la parte inferior por el mismo motivo.

* En ciertos controles, como TMemo o TDBLookupComboBox, en que sólo se muestran lineas de texto completas, conviene dejar un espacio vertical extra para el caso de que la fuente crezca de tamaño y evitar que el control aparezca vacío.

* Hay que establecer con cuidado las propiedades Constraints, pues se basan en valores absolutos (pixels) y si cambian los DPI del sistema pueden limitar el tamaño de un control que tuviera necesidad de crecer más de lo que le hemos fijado. Además, si las aplicamos a un formulario puede que impidamos que crezca proporcionalmente, y si sus controles se escalan los más seguro es que algunos queden ocultos. Si miramos el código fuente de Delphi 2010 veremos que han introducido una mejora en el escalado de la clase TControl (método ChangeScale), ya que al escalar un control también se escalan propiedades que afectan al tamaño, como Constraints, y otras ya propias de dicha versión como Margins o Padding, por lo que este punto más bien debe tenerse en cuenta en versiones de Delphi más antiguas.

* En caso de que implementemos código para ajustar el tamaño de ciertos controles, o columnas de una rejilla, por ejemplo, deberemos considerar en nuestras operaciones el factor de escala aplicable. Esto plantea la pega de que, para realizar las conversiones pertinentes, sólo conocemos los DPI actuales y no los que había en diseño. En el ANEXO 2, al final, adjunto un componente que entre otras cosas "recuerda" algunas propiedades que había en diseño, y que nos pueden servir para compararlas con las de ejecución a la hora de hacer un escalado por código. También podemos recurrir al truco, cuyo código está en el capítulo II (ver OPCION B), de sobreescribir el método ChangeScale del formulario y ahí averiguar qué escala se ha aplicado.

* El punto anterior también debe tenerse en cuenta en caso de que creemos controles en tiempo de ejecución, puesto que estos por defecto no aparecerán escalados (si Scaled es TRUE, Delphi escala los controles al cargar el formulario, pero no afecta a los que creemos nosotros luego por código).

* Los desarrolladores de componentes deberían sobreescribir el método ChangeScalede la clase TControl para habilitar que sus controles escalen todos sus elementos adecuadamente, que para eso está tal método virtual. Según he leído por ahí, algunos componentes de terceros no se adaptan correctamente cuando se hace un escalado. También sucede con componentes que trae Delphi de fábrica, como he comprobado con el control TDBGrid, que no escala la fuente ni el ancho de su objeto Columns, quedando el texto y ancho de columnas con su tamaño original, creando un mal efecto respecto al resto de controles. Esto sólo sucede cuando añadimos de forma persistente -durante el diseño normalmente- columnas a la propiedad Columns, pues cada columna posee ya su propiedad Font; en caso contrario las columnas toman la fuente heredada de la rejilla y sí que aparecen escaladas, aunque no así el título de las mismas. En Delphi 2010 tampoco está corregido, quizás se piense que no es un error propiamente. Si miramos la VCL veremos que en ese sentido los creadores de Delphi han sido bastante perezosos, ya que sólo escalan la fuente (objeto Font) en la clase ancestral TControl, y en el resto de clases derivadas no toman ninguna medida, incluso cuando contengan otros objetos de tipo TFont susceptibles de ser escalados. Abajo, en la Nota 4 pongo el código que estoy empleando para escalar el texto y ancho de las columnas de los TDBGrid, que es uno de los controles más utilizamos por la mayoría. Otro ejemplo de controles que no implementan completamente el escalado son los TTabControl y los TPageControl cuyas propiedades TabHeight y TabWidth, si han sido fijadas a un valor distinto de 0, cortarán el texto al mostrarse con la fuente escalada a mayor, pues mantienen su tamaño original. O también con el ancho (propiedad Width) de los paneles de un TStatusBar, que no se modifica al escalar, y así unos cuantos más ... Esto nos obligará en muchos casos a acometer estos ajustes por nuestra cuenta, conociendo, claro, los factores de escala a aplicar, un buen sitio es sobreescribiendo el método ChangeScale del formulario, que nos suministra dichos valores (ver ejemplo en el capítulo anterior, en OPCION B).

* He detectado un comportamiento anómalo del escalado en formularios heredados. Si un formulario tiene su propiedad Scaled a TRUE pero hereda de uno que tiene su propiedad Scaled a FALSE, entonces no se escala ninguno de los dos. Esto es un bug de Delphi, no corregido ni siquiera en la versión 2010, que me imagino se debe a cierta dificultad con el sistema de lectura del stream en formularios heredados que hace que se cargue en primer lugar algunas propiedades del formulario ancestro que "engañan" al formulario heredado. Seguramente esto de escalar un formulario y otro no, se considera una situación demasiado "exótica" como para prestarle tiempo en resolverlo.

* IMPORTANTE: Cuando trabajamos bajo unos DPI de sistema determinados, los cuadros de mensaje que trae Delphi de fábrica, como ShowMessage, MessageDlg o InputQuery, se mostrarán escalados a dichos DPI no pudiendo hacer nada para evitarlo (Delphi fabrica dichos cuadros de mensajes a partir de la clase TForm, cuya propiedad Scaled está a TRUE por defecto). Naturalmente, si nuestra aplicación no está escalada, cuando los invoquemos resaltarán más sobre el resto. Igualmente sucede con la barra de título de cada formulario, el mismo título y los botones de dicha barra, como los de Maximizar, Minimizar .. etc. Pasa también con los diálogos standard de Windows que se invocan cuando usamos los componentes de la paleta "Dialogs", tales como OpenDialog, SaveDialog, FontDialog ... etc, y que aparecerán escalados según los DPI de Windows. También los cuadrados que se dibujan en los controles TCheckBox y TCheckListBox, o los círculos de TRadioButton y TRadioGroup se dibujan con un tamaño diferente dependiendo de los DPI de Windows, por lo que si no escalamos un formulario que contenga alguno de estos controles, dichos dibujos desentonarán con el resto. Otro tanto se puede decir de las imágenes que utilicemos en botones (propiedad Glyph) y menús, muchas de las cuales se concentran en controles TImageList; conviene tenerlo en cuenta, e incluso disponer de varios juegos de imágenes con distintos tamaños, para asignar unos u otros según la escala aplicada.

* Y viceversa respecto a la consideración anterior, si decidimos aplicar un escalado personalizado, independiente del entorno, los elementos mencionados aparecerán con un tamaño diferente al resto.

* Borland también advertía del peligro de escalar repetidamente los formularios/controles de mayor a menor y viceversa, ya que, al ser conversiones a números enteros y efectuar un redondeo, hay un factor de error que normalmente hace que no se vuelva al tamaño original. Esto, que parece que sólo puede suceder si lo programamos adrede, también sucede involuntariamente cuando cargamos nuestros proyectos en Delphi bajo DPI diferentes, como me sucede a mí actualmente.

________________________________________


Nota 4: Pongo aquí el código que estoy utilizando para escalar el texto de los controles TDBGrid. Sólo se escalan los objetos Font y Title.Font, y la propiedad Width, de cada columna (objetos TColumn) de la propiedad Columns, en caso de que éstas no compartan la misma fuente que el TDBGrid, la cual ya debe estar escalada (para saber esto se comparan el Handle de cada fuente). Se escala también la propiedad TitleFont del TDBGrid para que los títulos no desentonen con el resto del texto. Los parámetros siguen la misma notación que utiliza Delphi en sus métodos de escalado (ScaleBy, ChangeScale ...), donde M es el factor multiplicador, y D el divisor.

Este procedimiento deberemos llamarlo en el evento OnCreate del formulario, o bien, si usamos el componente (ANEXO 2) que adjunto, interceptando su evento AfterScale.



delphi
  1. procedure ScaleDBGridText(AGrid: TDBGrid; M, D: Integer);
  2. var
  3.   i : Integer;
  4.   AColumn : TColumn;
  5.  
  6.   procedure ScaleFont(AFont: TFont);
  7.   begin
  8.   // La comparación de los Handles sirve para verificar que la
  9.   // fuente no es la misma que la del Control, la cual ya habrá sido escalada
  10.     if AFont.Handle <> AGrid.Font.Handle then
  11.       AFont.Size := MulDiv(AFont.Size, M, D)
  12.   end;
  13.  
  14. begin
  15.   if M = D then EXIT;
  16.   for i:=0 to AGrid.Columns.Count - 1 do
  17.   begin
  18.     AColumn := AGrid.Columns[i];
  19.     ScaleFont(AColumn.Font);
  20.     ScaleFont(AColumn.Title.Font);
  21.     AColumn.Width := MulDiv(AColumn.Width, M, D);
  22.   end;
  23.   ScaleFont(AGrid.TitleFont);
  24. end;

[/i]
  • 0

#6 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 17 noviembre 2010 - 01:31

(VI)


Hasta aquí lo que he podido averiguar y aprender sobre este asunto, espero que os sea de utilidad, y bienvenidas sean todo tipo de sugerencias o correcciones, así como reportes referentes a otras versiones diferentes a las que he utilizado en mis pruebas (Delphi 4, 5, 6, 7 y 2010). Asimismo, agradeceré cualquier reporte de bug o sugerencias para la utilidad y componente que adjunto.

Saludos.


___________________________________

ANEXO 1: Adjunto los fuentes de una utilidad (CambiaScaled.zip), realizada en Delphi 4 (compatible con versiones superiores), que permite cambiar rápidamente y de golpe la escalabilidad de todos los formularios de nuestras aplicaciones, accediendo a los ficheros .DFM y alterando, o bien la propiedad Scaled o bien la propiedad PixelsPerInch, o bien AutoScroll. De esta forma nos evitamos la tediosa labor de editar uno a uno cada formulario, aparte del peligro que supone abrir un formulario en modo diseño bajo unos DPI diferentes y que se nos trastoquen el tamaño de todos los controles. ¡ADVERTENCIA importante! Esta utilidad modifica los ficheros .DFM guardados como texto, pero también permite, opcionalmente, modificar los que estén guardados en binario; sin embargo, esta última opción hay que seleccionarla con cuidado, ya que utiliza las funciones de Delphi ObjectResourceToText y ObjectTextToResource, que se encargan de pasar a texto y luego a binario, declaradas en la unit Classes.pas y que varían de una versión a otra, por lo que debemos compilar la utilidad en la misma versión de Delphi que se utilizó para guardar los .DFM que queramos modificar. De lo contrario, muy posiblemente se produzcan errores a la hora de abrir dichos ficheros desde Delphi. ACLARACIÓN: Hasta Delphi 4 los archivos .DFM se guardaban exclusivamente en formato binario, a partir de Delphi 5 se puede elegir la manera de almacenarse (accediendo al menú contextual del formulario, opción "Text DFM").

Esta aplicación ha sido compilada y probada en Delphi 4, 5, 6, 7 y 2010, sobre ficheros .DFM tanto en modo texto como en modo binario, sin dar ningún problema. No obstante, es responsabilidad de cada cual el usarla debidamente, y aconsejo hacer previamente una copia de seguridad de los ficheros .DFM afectados "por si las moscas". Además se ofrecen sólo los fuentes para que se compile para la versión adecuada en cada caso y que cada uno haga las modificaciones a su gusto. Su uso es intuitivo, seleccionamos en la parte de arriba la carpeta donde residen nuestros ficheros, en la lista de abajo aparecerán todos los archivos .DFM que hay en dicha carpeta, marcamos los que queremos modificar, y en la parte de la derecha elegimos la propiedad a cambiar (normalmente sólo nos hará falta cambiar la propiedad Scaled a False), luego pulsamos el botón Aplicar, y listo.


ANEXO 2: Adjunto también el componente TFormAutoScaler que, una vez instalado (por defecto se coloca en la paleta "Samples"), basta con dejarlo caer en un formulario para que se encargue de redimensionarlo cuando los DPI lo requieran, pudiéndose elegir si escalar/redimensionar el formulario en tiempo de ejecución, de diseño, en ambos o en ninguno (aconsejo sólo en ejecución). El componente ha sido creado con Delphi 4, y probado también en las versiones 5, 6, 7 y 2010, supongo que es perfectamente compilable en el resto de versiones. Aunque en un principio este componente estaba pensado para escalarse únicamente a los DPI que tuviera el sistema, más tarde le habilité la propiedad RuntimeDPI, que permite establecer a qué DPI queremos que se escale un formularo al ejecutarse, lo cual nos da mayor flexibilidad de cara a personalizaciones visuales para cada usuario y permite hacer pruebas de cómo se verá el formulario escalado sin necesidad de ir cambiando la configuración de Windows. Lo he aplicado ya a algunas aplicaciones mías con buen resultado. Funciona correctamente en formularios heredados, por lo que basta con colocar un componente en el formulario ancestro y ya afecta a todos sus descendientes. También dispone de una propiedad UseFontScale, por defecto a True, que determina si el escalado se hará como Delphi, respecto a la fuente, o si es False respecto a los DPI directamente.

En el archivo FormAutoScaler.txt viene explicado detalladamente su funcionamiento y cómo instalarlo, le acompaña también un fichero de recursos, FormAutoScaler.res, con el icono que se mostrará en la paleta.

Archivos adjuntos


  • 0

#7 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 17 noviembre 2010 - 01:59

¡Caramba!, le he dado una leída por encimita y vaya que suena muy interesante, me daré tiempo de leerlo completito. Menudo documento te has publicado amigo Andrés.

Muchas gracias (y)

Salud OS
  • 0

#8 Cron

Cron

    Member

  • Visitante
  • PipPip
  • 41 mensajes
  • LocationCD DE MEXICO

Escrito 17 noviembre 2010 - 02:03

Gracias Andrés

Algo así me estaba faltando  (y)


Saludos

#9 Kipow

Kipow

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 228 mensajes
  • LocationGuatemala

Escrito 17 noviembre 2010 - 04:06

Promete promete, vamos a darle una leida mas despacio para ver si podes aprovechar esta gran informacion. Muchas gracias.
  • 0

#10 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 18 noviembre 2010 - 04:45

¡Caramba!, le he dado una leída por encimita y vaya que suena muy interesante, me daré tiempo de leerlo completito. Menudo documento te has publicado amigo Andrés.

Muchas gracias (y)

Salud OS


Gracias Andrés

Algo así me estaba faltando  (y) 


Saludos


Promete promete, vamos a darle una leida mas despacio para ver si podes aprovechar esta gran informacion. Muchas gracias.


De nada, amigos, llevo ya meses mareado con este tema. Fui reuniendo bastante información de aquí y allá, haciendo multitud de pruebas y llevándome sorpresas una tras otra, así que decidí escribir todo lo recopilado de forma ordenada. Disculpad el rollazo  |-) |-) (y)
  • 0

#11 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 18 noviembre 2010 - 05:37

Hola andrés,

He estado leyéndolo y se vé interesante.  (y) Tengo que darme más tiempo y tranquilidad para prestarle más atención, si puedo esta noche le doy una leída más profunda.

Gracias por compartirlo.  :)

Saludos,
  • 0

#12 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 18 noviembre 2010 - 07:15

Felicidades Andrés, parece muy interesante, has tocado muchos temas y hay que leerlo con calma. Gracias por tanto esfuerzo.

Saludos.
  • 0

#13 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 18 noviembre 2010 - 11:39

Hostia !! interesantíssimo documento, me lo he leído completito aunque un poco rápido al estar en el trabajo, ya me lo leo con más calma ;), felicidades y gracias por publicarlo con nosotros (y).

Saludos.
  • 0

#14 Ayla

Ayla

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 98 mensajes

Escrito 19 noviembre 2010 - 03:23


De rollazo nada, esta muy bien explicado.

Es interesante el tema  (y)
  • 0

#15 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 19 noviembre 2010 - 05:32

Gracias por vuestros comentarios y a aquellos que habéis leído todo el artículo de arriba a abajo, tenéis un viaje pagado a Hawaii, hay que tener valor y ganas, jeje  :D :D :D

Saludos

PD: Ayla, celebro verte por aquí, un abrazo  :)
  • 0

#16 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 19 noviembre 2010 - 07:56

Buenas,

Muy bien explicado todo amigo, felicidades por tan buen artículo, nos servirá a muchos para entender más el tema

Nos leemos

  • 0

#17 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 30 noviembre 2010 - 02:20

Hola.

He añadido un capítulo importante, el dedicado a "¿cómo se escalan las fuentes?", así como algunas consideraciones más a la hora de escalar, y algunas correcciones al texto. La semana pasada me encontré con alguna sorpresa más, principalmente la relacionada con la propiedad ParentFont del formulario, que es problemática.

Para los que hayan descargado código, aviso de que he corregido algún bug del componente TFormAutoScaler, he añadido más propiedades en la utilidad "CambiaScaled" que modifica los archivos .DFM, y también en la rutina que he puesto para escalar el formulario, que antes no escalaba según la fuente. También en la rutina para escalar el texto de los TDBGrid, ahora es más eficiente a la hora de detectar las fuentes que no han sido escaladas.

Creo haber llegado al final del mecanismo seguido por Delphi para escalar controles y formularios, hasta ahora aún me quedaban algunas dudas ya que al escalar por código no me "calcaba" exactamente el escalado original de la clase TForm (cuando dejamos Scaled a True), ahora ya sé por qué sucedía y está resuelto, aunque he detectado alguna anomalía en el comportamiento original de Delphi, que se puede leer en las explicaciones. Espero no llevarme ninguna sorpresa más (o mejor me callo por si las moscas  :grin: ).

Saludos
  • 0

#18 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 28 septiembre 2012 - 03:58

Recién me doy con este artículo, lo he leído medio rápido pero ya lo voy a analizar mejor.
La verdad es que he tenido algunos problemas en ciertos sistemas con esto de la escalabilidad y nunca pude encontrarle la vuelta. con lo que aquí se ha expuesto veo que puede cambiar mi perspectiva y solucionar varios temas al respecto.
Desde ya que el artículo está genial y felicito a su autor por haberse tomado el doble trabajo, el de redactar y, mas importante, investigar sobre el tema.
Tengo una advertencia al momento de escribir que dice que este artículo no tiene respuestas desde hace mas de 120 días pero igual contesto y espero que le llegue a Andrés, mis felicitaciones.

Santiago.
  • 0

#19 wvillalbazo

wvillalbazo

    Newbie

  • Miembros
  • Pip
  • 1 mensajes

Escrito 16 abril 2013 - 05:04

Andres Muchas Gracias por este Gran Aporte. Sinceramente Muchos dolores de cabeza y quebraderos
quedaran resueltos, Saludos. En Hora Buena.
  • 0

#20 LuKas

LuKas

    Newbie

  • Miembros
  • Pip
  • 7 mensajes

Escrito 21 febrero 2016 - 09:58

Excelente trabajo,funciona perfecto. :ap:


  • 0




IP.Board spam blocked by CleanTalk.