2010-12-13 19 views
27

System.Windows.Forms.TextRenderer.DrawText metodo rende formattati di testo con o senza sinistra e imbottitura destra a seconda del valore del parametro flags:come ottenere i margini di testo esatto utilizzati da TextRenderer

  • TextFormatFlags.NoPadding - si inserisce il testo saldamente il rettangolo di selezione ,
  • TextFormatFlags.GlyphOverhangPadding - aggiunge alcuni margini sinistro e destro,
  • TextFormatFlags.LeftAndRightPadding - aggiunge margini ancora più grandi.

Ora, la mia domanda è come posso ottenere l'esatta quantità di imbottitura (sinistra e destra) aggiunto di DrawText al testo per un dato contesto di periferica, string, font, ecc?

Ho scavato in .NET 4 con .NET Reflector e ho trovato che TextRenderer calcola "overhang padding" che è 1/6 dell'altezza del font e quindi moltiplica questo valore per calcolare i margini sinistro e destro usando questi coefficienti:

  • sinistra 1.0, 1.5 per giusta TextFormatFlags.GlyphOverhangPadding,
  • sinistra 2.0, 2.5 per giusta TextFormatFlags.LeftAndRightPadding.

I valori risultanti vengono arrotondati per eccesso e passati alle funzioni API native DrawTextExA o DrawTextExW. È difficile ricreare questo processo perché l'altezza del font non è presa da System.Drawing.Font ma da System.Windows.Forms.Internal.WindowsFont e queste classi restituiscono valori diversi per lo stesso carattere. E sono coinvolte molte altre classi BCL interne dallo spazio dei nomi . Decompilare tutti e riutilizzare il codice nella mia app non è un'opzione, perché sarebbe una seria dipendenza dell'implementazione .NET. Ecco perché ho bisogno di sapere se c'è qualche API pubblica in WinForms o almeno quali funzioni di Windows posso usare per ottenere i valori dei margini sinistro e destro.


Nota: ho cercato di TextRenderer.MeasureText con o senza imbottitura e confronta i risultati, ma che mi ha dato solo la somma dei margini sinistro e destro e ho bisogno di loro separatamente.


Nota 2: Nel caso in cui ti chiedi perché ho bisogno di questo: io voglio disegnare una stringa con più tipi di carattere/colori. Ciò implica chiamare il DrawText una volta per ogni sottostringa con formattazione uniforme con l'opzione NoPadding (in modo che il testo non si diffonda) ma voglio anche aggiungere manualmente il normale GlyphOverhangPadding all'inizio e alla fine dell'intero testo multi-formato.

+0

+1 per l'analisi completa :-). Ho anche avuto questo problema e i valori di padding sinistro che hai annusato lo hanno risolto per me - grazie. – Fedearne

risposta

1

questo potrebbe sembrare (molto) grezzo, ma questa è l'unica implementazione nativa mi viene in mente: DrawText pareggi a un IDeviceContext, che viene realizzato tramite Graphics. Ora, siamo in grado di trarre vantaggio da questo con il seguente codice:

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

Con il nuovo Bitmap si può andare pixel per pixel e le considero da qualche condizione. Ancora una volta, questo metodo è molto grezzo e dispendioso, ma almeno è nativo ....

Questo non è testato, ma basa sulle seguenti fonti:

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

Ho fatto qualcosa di simile qualche anno fa, per evidenziare i risultati della ricerca (il modello di ricerca appare in bol d ecc.). La mia implementazione era in DevExpress, quindi il codice potrebbe non essere rilevante. Se pensi che sia utile, posso copiarlo, ho solo bisogno di trovare quella implementazione.

In System.Windows.Forms, la classe da utilizzare sarebbe Graphics. Ha un metodo MeasureCharacterRanges() che accetta uno StringFormat (inizia con GenericTypographic e passa da lì). È molto più appropriato di TextRenderer per visualizzare una stringa completa concatenando parti con stili, caratteri o pennelli diversi.

Sei andato molto più lontano di me con la misurazione di imbottitura effettiva. I controlli di DevExpress ti davano il rettangolo di delimitazione del testo per iniziare, così è stato fatto per me.

Here's un articolo di Pierre Arnaud che mi è venuto in mente su Google, che riguarda quest'area. Sfortunatamente il collegamento GDI + "Dettagli Gori" non funziona.

Cheers,
Jonno

2

Questa risposta è un estratto da qui - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

Se avete mai desiderato un Label o TextBox in Windows Form che esegue un po 'più come sul web, allora si Probabilmente abbiamo capito che non esiste un modo intuitivo per fare in modo che Label o TextBox regolino automaticamente la sua altezza per adattarsi al testo che contiene. Anche se potrebbe non essere intuitivo, non è assolutamente impossibile.

In questo esempio, userò un TextBox (si potrebbe facilmente usare un'etichetta) che è agganciato alla parte superiore di un modulo. Per utilizzare questo, aggiungi aTextBox chiamato MyTextBox al modulo e imposta Dock su DockStyle.Top. Collegare l'evento Resize di TextBox a questo gestore di eventi.

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; 
} 

Se si desidera ridimensionare l'un'etichetta o TextBox che non è ancorata (per esempio, uno che è in una FlowLayoutPanel o altro pannello, o semplicemente inserito nel form), quindi è in grado di gestire Ridimensiona del form anche invece, e basta modificare direttamente le proprietà del Controllo.

+0

Nessun font di proprietà su un TextBox http://msdn.microsoft.com/en-us/library/system.windows.controls.textbox%28v=vs.110%29.aspx – user969153

0

La correzione è quello di calcolare quello che MeasureText sta per aggiungere:

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); 

Calcoliamo la larghezza di una spazio e la larghezza di due spazi. Entrambi hanno la larghezza extra aggiunta, quindi possiamo estrapolare la larghezza extra che viene aggiunta. La larghezza aggiunta apparentemente è sempre la stessa, quindi sottrarre extra da ogni larghezza restituita da MeasureText fornisce i risultati previsti.

5

Il valore necessario per calcolare i margini sinistro e destro è TEXTMETRIC.tmHeight, che è possibile ottenere utilizzando Win32 API.

Tuttavia, ho trovato che tmHeight è solo una line-height di un tipo di carattere in pixel, in modo da questi tre approcci vi darà lo stesso valore (è possibile utilizzare qualsiasi che ti piace nel codice):

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

per ottenere i margini destro e sinistro, si usa lo stesso codice come fa TextRenderer sotto il cofano:

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); 

Ora si può facilmente verificare che

(leftMargin + rightMargin) == overallPadding 

Solo per notare:

avevo bisogno di risolvere questo problema al fine di attuare "Cercare Highlight" caratteristica in un ListView-based control che utilizza GDI rendering del testo:

enter image description here

funziona come un fascino :)