Почему DrawString демонстрирует неожиданное поведение в C# Winforms?

У меня есть подкласс элемента управления в C# WinForms, а в моем обработчике OnPaint() есть пользовательский текст для рисования. Шрифт установлен на Courier New, используя следующий код в моей форме:

FontFamily family = new FontFamily("Courier New");
this.myControl.Font = new Font(family, 10);

В самом элементе управления строка хранится в realText, и я использую следующий код, чтобы вывести ее на экран:

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    e.Graphics.DrawString(realText, Font, new SolidBrush(ForeColor), ClientRectangle);
}

Результат для некоторого случайного текста примера выглядит следующим образом: /img219/1778/courier.png

Если вы увеличите масштаб, вы увидите, например, что расстояние между первым «as» отличается от расстояния между вторым «as» (1 пиксель против 2 пикселей). Кто-нибудь знает, что может быть причиной этого, или как я могу предотвратить это? В интервалах гораздо больше похожих странностей, поскольку я рисую разными шрифтами, но я предполагаю, что все они являются результатом одной и той же проблемы.

Заранее спасибо за любые идеи, которые могут у вас возникнуть.


person Ko9    schedule 07.05.2009    source источник


Ответы (3)


Я предполагаю, что это потому, что вы используете Graphics.DrawString() вместо TextRenderer.DrawText(). Первый рисует текст с помощью GDI+, что является паршивым и устаревшим. Последний использует более современный (с точки зрения рендеринга текста) GDI. Я считаю, что это разница, отмеченная в предыдущем ответе (WinForms и Windows).

Вы также можете попробовать перегрузку Graphics.DrawString(), которая принимает StringFormat и укажите StringFormat.GenericTypographic. Тем не менее, это действительно немного взломать проблему. Если вы используете .NET 2.0 или более позднюю версию, вам следует использовать класс TextRenderer вместо дрянного класса Graphics для всех ваших потребностей в рендеринге текста. Graphics.MeasureString() и Graphics.DrawString() существуют исключительно для обратной совместимости с .NET 1.0 и 1.1.

edit: О да, и ваш код пропускает объект GDI при каждом цикле рисования. Объекты Brush — это управляемые оболочки для неуправляемых ресурсов, поэтому они должны быть удалены явно.

person Mike Post    schedule 08.05.2009
comment
Замечательно, что удалось это сделать, не прибегая к коду win32. Спасибо - person Ko9; 08.05.2009
comment
Освобождается ли объект GDI при запуске сборщика мусора? - person Andrew Shepherd; 08.05.2009
comment
Неа. Объекты Brush и Pen в .NET — это просто управляемые оболочки для неуправляемых ресурсов (кисть или перо GDI). Когда сборщик мусора запускается, он избавляется от оболочки .NET, но не от базового объекта GDI. Общее эмпирическое правило для объектов GDI заключается в том, чтобы либо обернуть их в блок using, либо явно удалить их в финализаторе. Вы должны быть в состоянии проверить это с помощью диспетчера задач (включите столбец GDI Objects) и наблюдайте за увеличением количества по мере утечки ресурсов. Счетчик не уменьшится, даже если вы запустите сборщик мусора. - person Mike Post; 08.05.2009
comment
Более конкретно: сборщик мусора удалит управляемую оболочку, но не базовый объект GDI. Из-за конструкции GDI эти ресурсы не освобождаются до тех пор, пока ваш процесс не завершится. Вот почему у нас есть WPF — для решения всех этих глупых маленьких проблем. - person Mike Post; 08.05.2009
comment
Работал на меня!!! Я потратил, наверное, 3 часа, пытаясь понять, какой шрифт мне следует использовать, потому что, что бы я ни делал, это выглядело неправильно. - person Tony; 08.07.2010

Должен быть честным, но раньше со мной такого никогда не случалось. Однако попробуйте установить для SmoothingMode значение Antialiasing:

e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

Кроме того, убедитесь, что для свойства DoubleBuffer установлено значение true. Кроме того, старайтесь не создавать новую SolidBrush при каждом вызове OnPaint.

person AlaaShaker    schedule 07.05.2009
comment
Пробовал, ничего не изменилось к сожалению. На самом деле перепробовал все ваши советы, ни один из них не помог :-/ - person Ko9; 08.05.2009

Мой опыт рисования текста в подклассах элементов управления с помощью WinForms заключается в том, что используемый механизм рендеринга текста (GDI+?) не так хорош, как собственный механизм шрифтов Windows, и, безусловно, дает другие результаты, даже когда он работает хорошо.

Я автор надстройки Visual Studio (http://entrian.com/source-search ), который должен рисовать элементы управления в Visual Studio, и чтобы шрифты выглядели так же, как стандартные элементы управления в Visual Studio (списки, древовидные представления и т. д.), я должен обойти WinForms и нарисовать текст с помощью Win32 API:

[DllImport("gdi32.dll")]
public static extern bool ExtTextOut(IntPtr hdc, int X, int Y,
   uint fuOptions, [In] ref RECT lprc, string lpString, uint cbCount,
   [In] int[] lpDx);

...и семья.

Возможно, это не то, что вы хотели услышать, но это так.

person RichieHindle    schedule 08.05.2009