views:

1123

answers:

3

I have subclassed a control in C# WinForms, and am custom drawing text in my OnPaint() handler. The font is set to Courier New using the following code in my form:

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

In the control itself, the string is stored in realText, and I use the following code to draw it to the screen:

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

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

The result for some random example text looks as follows: http://img219.imageshack.us/img219/1778/courier.png

If you zoom in, you can see for example, that the space between the first 'as' is different than the space between the second 'as' (1 pixels versus 2 pixels). Does anybody have any idea what might be causing this, or how I can prevent it from happening? There is a lot more similar weirdness in spacing as I draw with different fonts, but I assume they're all results of the same problem.

Thanks in advance for any ideas you may have.

A: 

I have to be honest, but this never happened to me before. However, try setting the SmoothingMode to Antialiasing:

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

Another thing aside, make sure the from your using has DoubleBuffer set to true. Also, try not to create a new SolidBrush in every OnPaint call ..

AlaaShaker
Tried it, didn't change anything sadly. Tried all your suggestions actually, none of them helped :-/
Ko9
A: 

My experience with painting text into subclassed controls using WinForms is that the text rendering engine it uses (GDI+?) is not as good as Windows's own font engine, and certainly gives different results even when it works well.

I'm the author of a Visual Studio addin (http://entrian.com/source-search) that needs to paint controls within Visual Studio, and in order to make the fonts look the same as the standard controls in Visual Studio (listviews, treeviews, etc.) I have to bypass WinForms and paint the text using the 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);

...and family.

Probably not what you wanted to hear, but there it is.

RichieHindle
+3  A: 

I'm going to guess that it's because you're using Graphics.DrawString() instead of TextRenderer.DrawText(). The former paints text using GDI+ which is sort of crappy and outdated. The latter uses GDI which is more modern (in terms of text rendering). I believe this is difference noted by the previous answer (WinForms vs. Windows).

You might also try the overload of Graphics.DrawString() that takes a StringFormat object and specify StringFromat.GenericTypographic. However, this is really a bit of a hack around the problem. If you're using .NET 2.0 or later, you should be using the TextRenderer class instead of the crappy Graphics class for all of your text rendering needs. Graphics.MeasureString() and Graphics.DrawString() exist strictly for backwards compatibility with .NET 1.0 and 1.1.

edit: Oh yeah, and your code leaks a GDI object on every paint cycle. Brush objects are managed wrappers around unmanaged resources thus they must be explicitly disposed.

Mike Post
Wonderful, that did it without having to resort to win32 code. Thanks
Ko9
Does the GDI object get released when the garbage collector runs?
Andrew Shepherd
Nope. Brush and Pen objects in .NET are just managed wrappers around unmanaged resources (the GDI brush or pen). When the garbage collector runs it gets rid of the .NET wrapper, but not the underlying GDI object. General rule of thumb for GDI objects is to either wrap them in a using block or explicitly dispose of them in your finalizer. You should be able to verify this by using Task Manager (turn on the GDI Objects column) and watch the count increase as you leak the resources. The count won't go down even if you kick the GC into action.
Mike Post
More specifically: the GC will dispose of the managed wrapped, but not the underlying GDI object. Due to the design of GDI, those resources are not released until your process exits. That's why we have WPF -- to solve all these stupid little problems.
Mike Post
Worked for me!!! I have spent probably 3 hours trying to figure out what font I should be using because and not matter what I did it looked wrong.
Tony