2010-12-13 17 views
27

System.Windows.Forms.TextRenderer.DrawText metoda tynki sformatowany tekst z lub bez lewy i prawy wyściółka w zależności od wartości parametru flags:Jak uzyskać dokładne marże tekstowe wykorzystywane przez TextRenderer

  • TextFormatFlags.NoPadding - pasuje tekst szczelnie do obwiedni ,
  • TextFormatFlags.GlyphOverhangPadding - dodaje lewy i prawy margines,
  • TextFormatFlags.LeftAndRightPadding - dodaje jeszcze większe marginesy.

Teraz moje pytanie jest jak mogę dostać dokładną ilość wyściółki (lewy i prawy) dodane przez DrawText do tekstu dla danego kontekstu urządzenia, łańcuch, czcionki itp?

Mam wbiły .NET 4 z .NET Reflector i stwierdził, że TextRenderer oblicza „wystającą dopełnienie”, która jest 1/6 wysokości fontu, a następnie mnoży tę wartość do obliczenia marginesu lewego i prawego pomocą tych współczynników:

  • lewy, prawy 1,0 1,5 do TextFormatFlags.GlyphOverhangPadding,
  • lewo 2,0 prawym 2,5 do TextFormatFlags.LeftAndRightPadding.

Otrzymane wartości są zaokrąglane w górę i przekazywane do funkcji natywnego interfejsu API DrawTextExA lub DrawTextExW. Trudno jest odtworzyć ten proces, ponieważ wysokość czcionki jest pobierana nie od System.Drawing.Font, ale od System.Windows.Forms.Internal.WindowsFont, a te klasy zwracają różne wartości dla tej samej czcionki. I zaangażowanych jest wiele innych wewnętrznych klas BCL z przestrzeni nazw System.Windows.Forms.Internal. Dekompilowanie ich wszystkich i ponowne użycie ich kodu w mojej aplikacji nie jest opcją, ponieważ byłaby to poważna zależność od implementacji .NET. Dlatego muszę wiedzieć, czy istnieje jakiś publiczny API w WinForms lub przynajmniej jakie funkcje Windowsa mogę użyć, aby uzyskać wartości lewego i prawego marginesu.


Uwaga: Próbowałem TextRenderer.MeasureText i bez wyściółki i porównać wyniki, ale to dało mi jedynie sumę lewego i prawego marginesu i muszę je oddzielnie.


Uwaga 2: W przypadku, gdy zastanawiasz się, dlaczego muszę to: Chcę, aby narysować jeden ciąg z wielu czcionek/kolorów. To wymaga wywołania DrawText raz dla każdego jednolicie sformatowanego podciągu z opcją NoPadding (aby tekst się nie rozprzestrzeniał), ale chcę też dodać ręcznie normalny GlyphOverhangPadding na samym początku i na końcu całego tekstu wieloformatowego.

+0

+1 za kompleksową analizę :-). Miałem także ten problem, a wartości lewostronne, które podsłuchałeś, rozwiązały dla mnie - dzięki. – Fedearne

Odpowiedz

1

To może wydawać się (bardzo) surowy, ale jest to jedyny rodzimy realizacja mogę myśleć: DrawText zwraca się do IDeviceContext, który jest realizowany przez Graphics. Teraz możemy wykorzystać to z następującego kodu:

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

Dzięki nowej Bitmap można przejść piksel po pikselu i policzyć je przez pewien warunek. Ponownie, ta metoda jest bardzo prymitywna i marnotrawna, ale przynajmniej jest natywna ....

to nie jest badana, ale na podstawie następujących źródeł:

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

Robiłem coś podobnego kilka lat temu, aby podświetlić wyniki wyszukiwania (wzór wyszukiwania pojawia się w bol d itd.). Moja implementacja była w DevExpress, więc kod może nie być odpowiedni. Jeśli uważasz, że to jest użyteczne, mogę go skopiować, wystarczy znaleźć tę implementację.

W klasie System.Windows.Forms używana klasa to Graphics. Ma metodę MeasureCharacterRanges(), która akceptuje StringFormat (zacznij od GenericTypographic i przejdź od tego miejsca). Jest znacznie bardziej odpowiednie niż TextRenderer do wyświetlania kompletnego ciągu przez łańcuchy z różnymi stylami, czcionkami lub pędzlami.

Zrobiłeś znacznie więcej niż ja, korzystając z rzeczywistego pomiaru wyściółki. Kontrolki DevExpress dały ci prostokąta prostego tekstu na początek, więc zrobiono to dla mnie.

Here's Artykuł Pierre Arnaud, który wyskoczył mi w Google, który dotyczy tego obszaru. Niestety, link GDI + "Gory details" jest uszkodzony.

Cheers,
Jonno

2

Ta odpowiedź jest fragmentem tutaj - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

Jeśli kiedykolwiek chciał etykiecie lub TextBox w Windows Forms, który wykonuje trochę bardziej jak w internecie, a następnie Prawdopodobnie odkryłem, że nie ma intuicyjnego sposobu na to, by etykieta lub pole tekstowe automatycznie dostosowywało jego wysokość do tekstu, który zawiera. Chociaż może nie być intuicyjny, zdecydowanie nie jest to niemożliwe.

W tym przykładzie użyję TextBox (możesz równie łatwo użyć etykiety), która jest zadokowana na górze formularza. Aby tego użyć, dodaj do formularza aTextBox o nazwie MyTextBox i ustaw Dock na DockStyle.Top. Połącz zdarzenia Resize w TextBox z tą procedurą obsługi zdarzeń.

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

Jeśli chcesz zmienić rozmiar etykiecie lub tekstowe, które nie jest zadokowany (na przykład taki, który jest w FlowLayoutPanel lub innego panelu, lub po prostu umieszczone na formularzu), a następnie można obsługiwać formie za Resize nawet zamiast tego i po prostu modyfikuj właściwości Kontroli bezpośrednio.

+0

Brak właściwości czcionki na polu tekstowym http://msdn.microsoft.com/en-us/library/system.windows.controls.textbox%28v=vs.110%29.aspx – user969153

0

Rozwiązaniem jest obliczenie co MeasureText ma zamiar dodać:

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

Obliczamy szerokość jednej przestrzeni i szerokości dwóch przestrzeniach. Oba mają dodatkową szerokość, więc możemy ekstrapolować dodatkową szerokość, która jest dodawana. Dodana szerokość widocznie jest zawsze taka sama, więc odjęcie extra z każdej szerokości zwróconej przez MeasureText daje oczekiwane wyniki.

5

Wartość potrzebna do obliczenia lewego i prawego marginesu to TEXTMETRIC.tmHeight, który można uzyskać za pomocą Win32 API.

Uważam jednak, że tmHeight jest tylko wysokość linii czcionki w pikselach, więc te trzy podejścia daje taką samą wartość (można użyć cokolwiek chcesz w kodzie):

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

aby uzyskać marginesy lewy i prawy, używamy tego samego kodu co robi TextRenderer pod maską:

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

teraz można łatwo sprawdzić, że

(leftMargin + rightMargin) == overallPadding 

Wystarczy pamiętać:

Musiałem rozwiązać ten problem, w celu wdrożenia „Szukaj Highlight” funkcji w ListView-based control który używa GDI renderowania tekstu:

enter image description here

działa jak czar :)

Powiązane problemy