usuario tutorial tamaño sobre resolucion pantalla formularios formulario form español escala controles control automático ajuste ajustar agregar c# winforms gdi

c# - tutorial - Cómo obtener los márgenes exactos de texto utilizados por TextRenderer



formularios en c# (5)

System.Windows.Forms.TextRenderer.DrawText método System.Windows.Forms.TextRenderer.DrawText procesa el texto formateado con o sin relleno izquierdo y derecho dependiendo del valor del parámetro flags :

  • TextFormatFlags.NoPadding : ajusta el texto perfectamente en el cuadro delimitador,
  • TextFormatFlags.GlyphOverhangPadding : agrega algunos márgenes izquierdo y derecho,
  • TextFormatFlags.LeftAndRightPadding : agrega márgenes aún mayores.

Ahora, mi pregunta es ¿cómo puedo obtener la cantidad exacta de relleno (izquierda y derecha) agregada por DrawText al texto para un contexto de dispositivo determinado, cadena, fuente, etc.?

Busqué en .NET 4 con .NET Reflector y descubrí que TextRenderer calcula el "relleno sobresaliente" que es 1/6 de la altura de la fuente y luego multiplica este valor para calcular los márgenes izquierdo y derecho usando estos coeficientes:

  • izquierda 1.0, derecha 1.5 para TextFormatFlags.GlyphOverhangPadding ,
  • izquierda 2.0, derecha 2.5 para TextFormatFlags.LeftAndRightPadding .

Los valores resultantes se redondean y pasan a las funciones de API nativa DrawTextExA o DrawTextExW . Es difícil recrear este proceso porque la altura de la fuente no se toma de System.Drawing.Font sino de System.Windows.Forms.Internal.WindowsFont y estas clases devuelven valores diferentes para la misma fuente. Y muchas otras clases internas de BCL de System.Windows.Forms.Internal namespace están involucradas. Descompilarlos todos y reutilizar su código en mi aplicación no es una opción, ya que sería una dependencia seria de la implementación de .NET. Es por eso que necesito saber si hay alguna API pública en WinForms o al menos qué funciones de Windows puedo usar para obtener los valores de los márgenes izquierdo y derecho.

Nota: He intentado TextRenderer.MeasureText con y sin relleno y comparar los resultados, pero eso me dio solo la suma de los márgenes izquierdo y derecho y los necesito por separado.

Nota 2: En caso de que se pregunte por qué necesito esto: quiero dibujar una cadena con varias fuentes / colores. Eso implica llamar a DrawText una vez por cada subcadena con formato uniforme con la opción NoPadding (para que el texto no se propague), pero también quiero agregar el GlyphOverhangPadding manualmente al principio y al final de todo el texto multiformato.


El valor que necesita para calcular los márgenes izquierdo y derecho es TEXTMETRIC.tmHeight, que es posible obtener utilizando la API de Win32 .

Sin embargo, descubrí que tmHeight es solo una altura de línea de una fuente en píxeles, por lo que estos tres enfoques le darán el mismo valor (puede usar el que desee en su código):

int apiHeight = GetTextMetrics(graphics, font).tmHeight; int gdiHeight = TextRenderer.MeasureString(...).Height; int gdipHeight = (int)Math.Ceiling(font.GetHeight(graphics));

Para obtener los márgenes izquierdo y derecho, usamos el mismo código que TextRenderer bajo el capó:

private const float ItalicPaddingFactor = 0.5f; ... float overhangPadding = (gdiHeight / 6.0f); //NOTE: proper margins for TextFormatFlags.LeftAndRightPadding flag //int leftMargin = (int)Math.Ceiling(overhangPadding); //int rightMargin = (int)Math.Ceiling(overhangPadding * (2 + ItalicPaddingFactor)); //NOTE: proper margins for TextFormatFlags.GlyphOverhangPadding flag int leftMargin = (int)Math.Ceiling(overhangPadding); int rightMargin = (int)Math.Ceiling(overhangPadding * (1 + ItalicPaddingFactor)); Size sizeOverhangPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.GlyphOverhangPadding); Size sizeNoPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.NoPadding); int overallPadding = (sizeOverhangPadding.Width - sizeNoPadding.Width);

Ahora puedes comprobar eso fácilmente

(leftMargin + rightMargin) == overallPadding

Sólo para tener en cuenta:

Necesitaba resolver este problema para implementar la función "Aspectos destacados de la búsqueda" en un control basado en ListView que usa la representación de texto GDI:

Funciona de maravilla :)


Esta respuesta es un extracto de aquí: http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

Si alguna vez ha deseado un Label o TextBox en Windows Forms que se desempeñe un poco más como en la web, entonces probablemente haya descubierto que no hay una forma intuitiva de hacer que Label o TextBox ajuste automáticamente su altura para que se ajuste al texto que contiene. . Si bien puede no ser intuitivo, definitivamente no es imposible.

En este ejemplo, usaré un TextBox (podría usar fácilmente una Etiqueta) que está acoplado a la parte superior de un formulario. Para usar esto, agregue un TextBox llamado MyTextBox al formulario, y establezca Dock en DockStyle.Top. Conecte el evento Resize del TextBox a este controlador de eventos.

private void MyTextBox_Resize( object sender, EventArgs e ) { // Grab a reference to the TextBox TextBox tb = sender as TextBox; // Figure out how much space is used for borders, etc. int chromeHeight = tb.Height - tb.ClientSize.Height; // Create a proposed size that is very tall, but exact in width. Size proposedSize = new Size( tb.ClientSize.Width, int.MaxValue ); // Measure the text using the TextRenderer Size textSize = TextRenderer.MeasureText( tb.Text, tb.Font, proposedSize, TextFormatFlags.TextBoxControl | TextFormatFlags.WordBreak ); // Adjust the height to include the text height, chrome height, // and vertical margin tb.Height = chromeHeight + textSize.Height + tb.Margin.Vertical; }

Si desea cambiar el tamaño de una Etiqueta o Cuadro de texto que no está acoplado (por ejemplo, uno que se encuentra en un Panel de Flujo de Flechas u otro Panel, o simplemente se coloca en el formulario), puede manejar el cambio de tamaño del formulario incluso en su lugar, y simplemente modificar el Control de las propiedades directamente.


Esto puede parecer (muy) burdo, pero esta es la única implementación nativa que puedo imaginar: DrawText dibuja en un IDeviceContext , que es implementado por Graphics . Ahora, podemos aprovechar eso con el siguiente código:

Bitmap bmp = new Bitmap(....); Graphics graphic = Graphics.FromImage(bmp); textRenderer.DrawText(graphic,....); graphic.Dispose();

Con el nuevo Bitmap , puede ir píxel por píxel y contarlos por alguna condición. Nuevamente, este método es muy crudo y derrochador, pero al menos es nativo ...

Esto no se prueba, pero se basa en las siguientes fuentes:

http://msdn.microsoft.com/en-us/library/4ftkekek.aspx

http://msdn.microsoft.com/en-us/library/system.drawing.idevicecontext.aspx

http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx

http://www.pcreview.co.uk/forums/graphics-bitmap-t1399954.html


Hice algo similar hace unos años, para resaltar los resultados de búsqueda (el patrón de búsqueda aparece en negrita, etc.). Mi implementación estaba en DevExpress, por lo que el código podría no ser relevante. Si crees que es de utilidad puedo copiarlo, solo necesito encontrar esa implementación.

En System.Windows.Forms , la clase a usar sería Graphics . Tiene un método MeasureCharacterRanges() que acepta un StringFormat (comienza con GenericTypographic y continúa desde allí). Es mucho más apropiado que TextRenderer para mostrar una cadena completa al encadenar partes con diferentes estilos, fuentes o pinceles.

Has ido mucho más lejos que yo con la medición del relleno real. Los controles de DevExpress te dieron el rectángulo delimitador del texto para comenzar, así que eso se hizo para mí.

Here''s un artículo de Pierre Arnaud que se me ocurrió en Google, que toca esta área. Desafortunadamente, el enlace de GDI + "Detalles de Gory" está roto.

Aclamaciones,
Jonno


La solución es calcular lo que MeasureText va a agregar:

var formatFlags FormatFlags = TextFormatFlags.NoPadding | TextFormatFlags.SingleLine; int largeWidth = TextRenderer.MeasureText( " ", font, new Size(int.MaxValue, int.MaxValue), formatFlags ).Width; int smallWidth = TextRenderer.MeasureText( " ", font, new Size(int.MaxValue, int.MaxValue), formatFlags ).Width; int extra = smallWidth - (largeWidth - smallWidth);

Calculamos el ancho de un espacio y el ancho de dos espacios. Ambos tienen el ancho adicional agregado, por lo que podemos extrapolar el ancho adicional que se agrega. Aparentemente, el ancho agregado siempre es el mismo, por lo que al restar extra de cada ancho devuelto por MeasureText obtienen los resultados esperados.