2010-12-13 8 views
27

System.Windows.Forms.TextRenderer.DrawText Methode Text mit oder ohne links und rechts padding je nach dem Wert des flags Parameter formatiert rendert:Wie die genauen Textränder von TextRenderer- gewöhnen

  • TextFormatFlags.NoPadding - passt den Text fest in den Begrenzungsrahmen
  • TextFormatFlags.GlyphOverhangPadding - fügt einige linke und rechte Ränder hinzu,
  • TextFormatFlags.LeftAndRightPadding - fügt sogar größere Ränder hinzu.

Nun meine Frage ist wie kann ich die genaue Höhe der Polsterung (links und rechts) erhalten hinzugefügt von DrawText dem Text für einen bestimmten Gerätekontext, string, Schriftart usw.?

Ich habe in .NET 4 mit .NET Reflector gegraben und festgestellt, dass TextRenderer "Überhang Polsterung" berechnet, die 1/6 der Schrifthöhe ist und multipliziert dann diesen Wert, um linke und rechte Ränder mit diesen Koeffizienten zu berechnen:

  • 1,0 links, rechts 1,5 für TextFormatFlags.GlyphOverhangPadding,
  • 2.0, rechts für TextFormatFlags.LeftAndRightPadding 2,5 belassen.

Die resultierenden Werte sind abgerundet und an den DrawTextExA oder DrawTextExW native API-Funktionen. Es ist schwierig, diesen Prozess neu zu erstellen, da die Höhe der Schrift nicht von System.Drawing.Font, sondern von System.Windows.Forms.Internal.WindowsFont stammt und diese Klassen unterschiedliche Werte für die gleiche Schriftart zurückgeben. Und viele andere interne BCL-Klassen aus dem Namespace System.Windows.Forms.Internal sind beteiligt. Sie alle zu kompilieren und ihren Code in meiner App wiederzuverwenden, ist keine Option, da dies eine ernsthafte Abhängigkeit von der .NET-Implementierung wäre. Deshalb muss ich wissen, ob es eine öffentliche API in WinForms gibt oder zumindest welche Windows-Funktionen ich verwenden kann, um die Werte der linken und rechten Ränder zu erhalten.


Hinweis: Ich habe versucht, zu TextRenderer.MeasureText mit und ohne Polsterung und die Ergebnisse zu vergleichen, aber das gab mir nur die Summe der linken und rechten Rand und ich brauche sie getrennt.


Anmerkung 2: Falls Sie sich fragen, warum ich das brauchen: Ich möchte eine Zeichenfolge zeichnen mit mehreren Schriften/Farben. Das beinhaltet Aufruf DrawText einmal für jeden einheitlich formatierten Teilstring mit NoPadding Option (so dass der Text nicht verbreitet), aber ich möchte auch manuell normal GlyphOverhangPadding ganz am Anfang und Ende des gesamten Multi-Format-Text hinzufügen.

+0

+1 für die umfassende Analyse :-). Ich hatte auch dieses Problem und die Links-Padding-Werte, die Sie geschnüffelt haben, lösten es für mich - danke. – Fedearne

Antwort

1

Dies könnte (sehr) Rohöl erscheinen, aber dies ist die einzige native Implementierung ich denken kann: DrawText stützt sich auf ein IDeviceContext, die von Graphics umgesetzt wird. Jetzt können wir einen Vorteil aus, dass mit dem folgenden Code nehmen:

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

Mit den neuen Bitmap Sie Pixel für Pixel gehen und sie durch eine Bedingung zählen. Auch diese Methode ist sehr grob und verschwenderisch, aber zumindest ist es native ....

Dies ist nicht getestet, sondern basiert auf den folgenden Quellen:

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

ich etwas Ähnliches vor ein paar Jahren gemacht haben, Suchergebnisse hervorheben (Suchmuster erscheint in bol d usw.). Meine Implementierung war in DevExpress, daher ist der Code möglicherweise nicht relevant. Wenn Sie denken, dass es nützlich ist, kann ich es kopieren, muss nur diese Implementierung finden.

In System.Windows.Forms wäre die zu verwendende Klasse Graphics. Es hat eine MeasureCharacterRanges() Methode, die eine StringFormat akzeptiert (mit GenericTypographic beginnen und von dort gehen). Es ist viel passender als TextRenderer, eine komplette Zeichenkette anzuzeigen, indem Teile mit verschiedenen Stilen, Schriften oder Pinseln verkettet werden.

Sie sind viel weiter gegangen als ich mit der tatsächlichen Messung der Polsterung. Die Steuerelemente von DevExpress haben Ihnen das rechteckige Rechteck für den Anfang des Textes gegeben, so dass es für mich gemacht wurde.

Here's ein Artikel von Pierre Arnaud, die für mich in Google kam, die in diesem Bereich berührt. Leider ist der Link GDI + "Gory details" dort defekt.

Cheers,
Jonno

2

Diese Antwort ist ein Auszug aus hier - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

Wenn Sie jemals ein Label oder TextBox in Windows Forms wollte, die ein wenig mehr wie auf dem Netz führt Sie dann habe wahrscheinlich herausgefunden, dass es keine intuitive Möglichkeit gibt, ein Label oder eine TextBox automatisch so anzupassen, dass sie an den darin enthaltenen Text angepasst wird. Es mag zwar nicht intuitiv sein, aber es ist definitiv nicht unmöglich. In diesem Beispiel verwende ich eine TextBox (Sie könnten genauso einfach eine Beschriftung verwenden), die an den Anfang eines Formulars angedockt ist. Fügen Sie dazu eine TextBox namens MyTextBox in das Formular ein, und legen Sie Dock auf fest DockStyle.Top. Verbinden Sie das Resize-Ereignis des TextBox mit diesem Ereignishandler.

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

Wenn Sie wollen, dass die ein Etikett oder TextBox, um die Größe, die nicht verankert ist (zum Beispiel eine, die in einem Flowlayoutpanel oder einem anderen Gremium ist, oder einfach nur auf dem Formular platziert), dann können Sie die Form der Größe ändern Griff und stattdessen einfach die Eigenschaften des Controls direkt ändern.

+0

Es gibt keine Eigenschaft Schrift in einer TextBox http://msdn.microsoft.com/en-us/library/system.windows.controls.textbox%28v=vs.110%29.aspx – user969153

0

Das Update ist zu berechnen, was MeasureText hinzufügen wird:

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

Wir die Breite eines Raumes und die Breite von zwei Räumen berechnen. Bei beiden wird die zusätzliche Breite hinzugefügt, sodass wir die zusätzliche Breite, die hinzugefügt wird, extrapolieren können. Die hinzugefügte Breite ist anscheinend immer die gleiche, so dass das Subtrahieren extra von jeder Breite, die von MeasureText zurückgegeben wird, die erwarteten Ergebnisse ergibt.

5

Der Wert, den Sie für die Berechnung der linken und rechten Ränder benötigen, ist TEXTMETRIC.tmHeight, das mit Win32 API abgerufen werden kann.

Allerdings fand ich, dass TmHeight nur eine Zeilenhöhe einer Schrift in Pixel ist, so dass diese drei Ansätze werden Sie den gleichen Wert geben (Sie verwenden können, je nachdem, was Sie in Ihrem Code mögen):

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

Zur linken und rechten Rand zu erhalten, verwenden wir den gleichen Code wie TextRenderer- unter der Haube tut:

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

Jetzt können Sie leicht prüfen, ob

(leftMargin + rightMargin) == overallPadding 

Just zu beachten:

ich dieses Problem zu lösen, um benötigt "Suche Highlight" -Funktion in einem ListView-based control zu implementieren, die GDI-Text-Rendering verwendet:

enter image description here

funktioniert wie ein Charme :)

Verwandte Themen