3

I'm having a problems with the Graphics.DrawString() method. My UserControl uses a monospaced font to draw on a panel, and clearly, DrawString() formats the text in such a way, that it isn't exactly monospaced anymore. I have been using a StringFormat in the hope of fixing this, but without success:

StringFormat sf = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit | StringFormatFlags.MeasureTrailingSpaces);

The code for drawing the text:

for (int i = 0; i < document.Text.Count; i++)
                g.DrawString(document.Text[i], Font, Brushes.Black, new PointF(offset - 2, offset + Font.Height * i), sf);

For instance, when drawing "eeeeeeee", in Consolas 9, every now and then, two e's will be 1 pixel closer to another. Is there a way to display the text correctly, monospaced?

(My actual problem: I'm making my own textbox control from scratch, as an exercise and for fun. It's going quite well actually, but the panel displaying text should be able to position the caret according to the mouse click location. Finding the correct line is easy, because of the Font's Height property. Finding the current index in that line, however, is not. For non-monospaced fonts I can't imagine a way to determine the caret index, but for monospaced fonts it should be doable, since the width of a symbol is fixed. So, maybe there is a better way to determine this?)

Kind regards, Jacco

1 Answer 1

4

It is drawing monospaced. It's just that the width of each character isn't an integral number of pixels -- each character is probably something like 9.9 pixels wide. (To keep the characters legible, it snaps each one to the nearest pixel when it draws it, which is why you then sometimes see two closer together.)

One option is to use the AntiAlias flag, which will stop trying to snap each character to the nearest pixel -- it'll just grayscale-blur the edges so the spacing looks relatively consistent overall. However, antialising doesn't look good at small font sizes.

Or you could use TextRenderer instead of Graphics.DrawString. TextRenderer uses the old GDI rendering (instead of GDI+ like the Graphics class does), so it doesn't do sub-pixel rendering at all -- either every character is 9 pixels wide, or every character is 10 pixels wide. Sometimes, the older technology is better.

Sign up to request clarification or add additional context in comments.

3 Comments

The AntiAlias flag makes the font look really bad for me, even in size 12
Thanks! The drawing works perfectly now using TextRenderer. Measuring with TextRenderer.MeasureString() gives some problems though. Even after setting TextFormatFlags.NoPadding as a TextFormatFlags, the measurement of strings like "abcd" are incorrect (gives a width of 25, which should be 28, Consolas 9), "a" strangely enough gives 10 instead of 7.
I believe you can also set graphics.TextRenderingHint to different values to cause it to draw text in the same way that TextRender does. I don't remember the specific value to set it to, but there are only 5 or so choices.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.