2010-12-13 3 views
27

System.Windows.Forms.TextRenderer.DrawText метод оказывает форматированный текст с или без левой и правой набивка в зависимости от значения flags параметра:Как получить точные текстовые поля используются TextRenderer

  • TextFormatFlags.NoPadding - вписывает текст плотно в ограничительной рамки ,
  • TextFormatFlags.GlyphOverhangPadding - содержит левый и правый поля,
  • TextFormatFlags.LeftAndRightPadding - добавляет еще большие поля.

Теперь мой вопрос как я могу получить точное количество дополнения (левый и правый) добавляют DrawText к тексту для данного контекста устройства, строка, шрифт и т.д.?

Я выкопал в .NET 4 с помощью .NET Reflector и обнаружил, что TextRenderer вычисляет «накладку навеса», которая составляет 1/6 от высоты шрифта, а затем умножает это значение для вычисления левого и правого полей с использованием этих коэффициентов:

  • влево 1.0, 1.5 для правой TextFormatFlags.GlyphOverhangPadding,
  • влево 2.0, справа 2.5 для TextFormatFlags.LeftAndRightPadding.

Результирующие значения округлены и переданы на собственные функции API-интерфейса или DrawTextExW. Трудно воссоздать этот процесс, потому что высота шрифта берется не с System.Drawing.Font, а с System.Windows.Forms.Internal.WindowsFont, и эти классы возвращают разные значения для одного и того же шрифта. И задействовано много других внутренних классов BCL из пространства имен System.Windows.Forms.Internal. Декомпиляция всех из них и повторное использование их кода в моем приложении не является вариантом, потому что это будет серьезная зависимость реализации .NET. Вот почему мне нужно знать, есть ли какой-либо публичный API в WinForms или, по крайней мере, какие функции Windows я могу использовать для получения значений левого и правого полей.


Примечание: Я пытался TextRenderer.MeasureText с и без дополнения и сравнить результаты, но это дало мне только сумма левого и правого краев, и мне нужно их отдельно.


Примечание 2: В случае вам интересно, почему мне это нужно: Я хочу, чтобы нарисовать одну строку с несколькими шрифтами/цветов. Это включает вызов DrawText один раз для каждой равномерно форматированной подстроки с параметром NoPadding (так что текст не распространяется), но я также хочу добавить вручную обычный GlyphOverhangPadding в самом начале и в конце всего многоформатного текста.

+0

+1 для всестороннего анализа :-). У меня также была эта проблема, и значения левого дополнения, которые вы понюхали, решили это для меня - спасибо. – Fedearne

ответ

1

Это может показаться (очень) сырой нефти, но это единственный родной реализации я могу думать: DrawText привлекает к IDeviceContext, которая реализуется Graphics. Теперь мы можем воспользоваться тем, что с помощью следующего кода:

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

С новым Bitmap вы можете пойти пиксель за пикселем и сосчитать их некоторым условием. Опять же, этот метод очень грубый и расточительный, но, по крайней мере, это родной ....

Это не проверял, но на основе следующих источников:

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

Я сделал нечто подобное несколько лет назад, для выделения результатов поиска (рисунок поиска отображается в bol d и т. д.). Моя реализация была в DevExpress, поэтому код может быть неактуальным. Если вы считаете, что это полезно, я могу скопировать его, просто нужно найти эту реализацию.

В System.Windows.Forms, класс использования будет Graphics. Он имеет метод MeasureCharacterRanges(), который принимает StringFormat (начните с GenericTypographic и перейдите оттуда). Это гораздо более уместно, чем TextRenderer для отображения полной строки путем цепочки частей с разными стилями, шрифтами или кистями.

Вы прошли путь дальше меня с фактическим измерением заполнения. Элементы управления DevExpress дали вам прямоугольник с прямоугольником для начала, чтобы это было сделано для меня.

Here's статья Пьера Арно, которая пришла ко мне в Google, которая касается этой области. К сожалению, ссылка GDI + «Gory details» нарушена.

Cheers,
Jonno

2

Этот ответ отрывок из здесь - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

Если вы когда-либо хотели ярлык или TextBox в Windows Forms, которые выполняет немного больше, как в Интернете, то вы Вероятно, вы поняли, что нет интуитивного способа сделать ярлык или TextBox автоматически отрегулировать его высоту, чтобы он соответствовал содержащемуся в нем тексту. Хотя это может и не быть интуитивным, это определенно не невозможно.

В этом примере я буду использовать TextBox (вы можете так же легко использовать ярлык), который прикреплен к вершине формы. Чтобы использовать это, добавьте aTextBox с именем MyTextBox в форму и установите Dock в DockStyle.Top. Подключите событие Resize для TextBox к этому обработчику событий.

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

Если вы хотите, чтобы изменить размер и ярлык или TextBox, не стыковка (например, тот, который находится в FlowLayoutPanel или другой панели, или просто размещен на форме), то вы можете обрабатывать изменение размеров форм в даже вместо этого, и просто измените свойства Control напрямую.

+0

Нет свойства Шрифт в текстовом поле http://msdn.microsoft.com/en-us/library/system.windows.controls.textbox%28v=vs.110%29.aspx – user969153

0

Исправление вычислить, что MeasureText собирается добавить:

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

Вычислим ширину одного пространства и ширину двух пространств. Оба имеют дополнительную ширину, поэтому мы можем экстраполировать добавленную дополнительную ширину. Добавленная ширина, по-видимому, всегда одна и та же, поэтому вычитание extra из каждой ширины, возвращаемой MeasureText, дает ожидаемые результаты.

5

Значение, необходимое для вычисления левого и правого полей, равно TEXTMETRIC.tmHeight, которое можно получить с помощью Win32 API.

Однако, я обнаружил, что tmHeight просто высота строки шрифта в пикселях, так что эти три подхода даст вам то же самое значение (вы можете использовать любой вам нравится в вашем коде):

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

Для получения левого и правого полей, мы используем один и тот же код, как и TextRenderer под капотом:

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

Теперь вы можете легко проверить, что

(leftMargin + rightMargin) == overallPadding 

Просто к сведению:

мне нужно решить эту проблему для того, чтобы реализовать «Поиск Highlight» функцию в ListView-based control, который использует GDI визуализации текста:

enter image description here

Работает как шарм :)

Смежные вопросы