2010-12-13 25 views
27

System.Windows.Forms.TextRenderer.DrawText método representa el texto formateado con o sin la izquierda y relleno derecho en función del valor de la flags parámetro:cómo obtener los márgenes del texto exacto utilizado por TextRenderer

  • TextFormatFlags.NoPadding - Se ajusta el texto firmemente en el cuadro delimitador ,
  • TextFormatFlags.GlyphOverhangPadding - agrega algunos márgenes izquierdos y derechos,
  • TextFormatFlags.LeftAndRightPadding - agrega márgenes aún más grandes.

Ahora, mi pregunta es cómo puedo obtener la cantidad exacta de acolchado (izquierda y derecha) Añadido por DrawText al texto para un contexto determinado dispositivo, cuerda, fuente etc?

He excavado en .NET 4 con .NET Reflector y encontré que TextRenderer calcula el "overhang padding" 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, a la derecha 1.5 para TextFormatFlags.GlyphOverhangPadding,
  • izquierda 2.0, a la derecha 2.5 para TextFormatFlags.LeftAndRightPadding.

Los valores resultantes se redondean hacia arriba y se pasan a las funciones de API nativas DrawTextExA o DrawTextExW. Es difícil recrear este proceso porque la altura de la fuente no se toma desde System.Drawing.Font sino desde System.Windows.Forms.Internal.WindowsFont y estas clases devuelven valores diferentes para la misma fuente. Y muchas otras clases BCL internas del espacio de nombres System.Windows.Forms.Internal están involucradas. Descompilarlos a todos y reutilizar su código en mi aplicación no es una opción, porque eso sería una seria dependencia 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 tratado de TextRenderer.MeasureText con y sin relleno y comparar los resultados, sino que me dio sólo la suma de los márgenes izquierdo y derecho y que los necesitan por separado.


Nota 2: En caso de que se preguntan por qué necesito esto: quiero dibujar una cadena con múltiples fuentes/colores. Eso implica llamar al DrawText una vez por cada subcadena formateada uniformemente con la opción NoPadding (para que el texto no se extienda) pero también quiero agregar manualmente el GlyphOverhangPadding normal al principio y al final de todo el texto multiformato.

+0

+1 para el análisis completo :-). También tuve este problema y los valores de la almohadilla izquierda que olisqueaste lo resolvieron para mí, gracias. – Fedearne

Respuesta

1

Esto puede parecer crudo (muy), pero esta es la única aplicación nativa que se me ocurre: DrawText llega a su IDeviceContext, que se realiza mediante Graphics. Ahora, podemos tomar ventaja de que, con el siguiente código:

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

Con el nuevo Bitmap se puede ir pixel por pixel y contarlos por alguna condición. Una vez más, este método es muy crudo y derrochador, pero al menos es nativo ...

Esto no se ha probado, pero 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

1

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

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

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

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

Cheers,
Jonno

2

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

Si alguna vez has querido una etiqueta o un cuadro de texto en formularios de Windows que realiza un poco más como en la web, entonces usted Probablemente descubrí que no hay una forma intuitiva de hacer que una etiqueta o un cuadro de texto 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 una etiqueta tan fácilmente) que está acoplado a la parte superior de un formulario. Para usar esto, agregue un cuadro de texto llamado MyTextBox al formulario y configure el Dock a 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 un cuadro de texto que no está atracado (por ejemplo, uno que está en una FlowLayoutPanel u otro panel, o simplemente coloca en el formulario), entonces usted puede manejar Cambiar el tamaño del formulario incluso en su lugar, y simplemente modifique las propiedades del control directamente.

+0

No hay una fuente de propiedad en un TextBox http://msdn.microsoft.com/en-us/library/system.windows.controls.textbox%28v=vs.110%29.aspx – user969153

0

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

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 la anchura de un espacio y la anchura de dos espacios. Ambos tienen el ancho adicional agregado, por lo que podemos extrapolar el ancho adicional que se agrega. El ancho agregado aparentemente siempre es el mismo, por lo que restar extra de cada ancho devuelto por MeasureText da los resultados esperados.

5

El valor que necesita para calcular los márgenes izquierdo y derecho es TEXTMETRIC.tmHeight, que se puede obtener usando Win32 API.

Sin embargo, he encontrado que tmHeight es sólo una altura de línea de una fuente en píxeles, por lo que estos tres enfoques le dará el mismo valor (que puede elegirse el que te gusta 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 márgenes izquierdo y derecho, se utiliza el mismo código que hace 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 puede comprobar fácilmente que

(leftMargin + rightMargin) == overallPadding 

para notar:

que necesitaba para resolver este problema con el fin de poner en práctica "Buscar Resalte" característica en una ListView-based control que utiliza GDI representación de texto:

enter image description here

funciona como un encanto :)

Cuestiones relacionadas