Using Graphics.DrawString to Simulate TextBox rend

2019-09-05 03:58发布

问题:

I have a C# UserControl that hosts a TextBox.

When the custom control is disabled, I would like the TextBox to be rendered as if it were Disabled + ReadOnly (i.e. not greyed out). Therefore, when the custom control catches the EnabledChanged it sets the hosted TextBox properties accordingly.

However, the Enabled state of the UserControl takes precedence over everything else and the TextBox is still rendered greyed out (even though its internal ForeColor is correct).

Therefore, I decided to hide the hosted TextBox when the custom control is disabled and draw it myself. I can successfully render the TextBox border using the various ControlPaint.DrawXxx functions.

However, drawing the text results in a stretched output, when compared to the native rendering. That is, the text starts at the exact same pixel location, but character spacing is noticeably larger.

I use the TextBox's own Font to perform the rendering so I don't know what I'm doing wrong. The only justification that I can make is that the C# TextBox is rendered directly by Windows (using the ExtTextOut Win32 API) and this results in the evident differences.

What options can I use to mimic the native TextBox rendering ?

回答1:

The difference is that Graphics.DrawString uses GDI+ to render the text, whereas the Win32 API uses GDI internally for everything, including drawing text on controls.

As of .NET 2.0, you can easily mimic its appearance using the TextRenderer.DrawText method, which also uses GDI to draw.

In most cases, replacing Graphics.DrawString with TextRenderer.DrawText is straightforward. You don't show any code, so it's hard to give a specific example, though.


As for why you have to do this in the first place... Disabling a container control always disables all of its child controls. That's a hard rule in Windows with no exceptions. It's a pretty sensible one, of course.
If you don't want all of the controls inside of a container to be disabled, you shouldn't disable the entire container—just disable the individual controls inside of that container.

Even armed with a slightly better method of rendering text, I still strongly recommend that you not try and re-implement the TextBox control. It's a fairly complicated piece of work, and you're not likely to get it right with only a few days/weeks of effort.