问题
C#, Windows Forms app.
We're re-skinning our application and I'm also changing the awful default font we used in the old one. So I thought I'd call the following function to change the font of all controls on a form when the form is loaded.
internal static void SetFonts(Control control)
{
Font oldFont = control.Font;
if (oldFont.Name != GlobalFontName)
{
string familyName = GlobalFontName;
Font newFont = new System.Drawing.Font(familyName,
oldFont.Size, oldFont.Style, GraphicsUnit.Point, 0);
control.Font = newFont;
//oldFont.Dispose();
}
foreach (Control child in control.Controls)
SetFonts(child);
}
I thought it would keep resources down if I disposed of the old font after reassigning the control with the new one, but on closing the form I'm receiving access violation exceptions from one control type from a set of third party controls. If I comment out the line "oldFont.Dispose()" then I don't get the exception.
Is this a bug of the third party control set or is this to be expected?
Memory wise, can I get away with not explicitly disposing of the old font (The app runs on kiosks for 12hr+ a day) ?
回答1:
Don't dispose of the old Font, that's the job of the Control whose font you're changing. Also, use tools such as GDIView to monitor your handles (such as fonts).
回答2:
Control fonts are very weird, as a consequence of the fact that a Font
object actually encapsulates two different things:
- Information about typeface, typestyle, etc.
- A GDI font handle.
The latter aspect of a Font
object encapsulates a resource; the former does not. Calling Dispose
on a Font
object releases the latter, but does not destroy the former.
While I don't think I've seen their behavior "officially" documented anywhere, it seems that the built-in controls are never actually drawn using the Font
object that was used to set the Font
property; instead, they use the attributes of the assigned Font
object to generate a new GDI font object and then use that to draw the control. Although calling Dispose
on a font object will make it unusable as an argument to DrawString
or other such methods, it will not prevent its use as a "template" for making new font objects.
A consequence of this is that controls don't care if the assigned Font
object is disposed (whether before or after it's assigned), nor do they ever dispose it. Reading the Font
property from a control will always return the same Font
object that was last assigned to it, without regard for whether that Font
object was disposed. Consequently, it seems that if nothing will ever read a control's Font
property and expect to draw with it, the safest way to assign a control's Font
property without a temporary resource leak would be the very-bizarre-looking:
using f = new Font(...)
theControl.Font = f;
That's arguably safer than reading the control's Font
property and disposing that before assigning the new value, since the using
approach above knows that the Font
which is assigned to the control won't be used for anything else, while the latter approach can't know whether the same Font
is used by some other code that would object to its disposal.
I really wish MS had documented how Font
resources are supposed to be handled. Unfortunately, so far as I know they haven't.
回答3:
I decided to store the created fonts into a list as they were created and dispose of them during form.Dispose(). But on some windows I got errors doing this too.
What I've just realized is that the third party control must also be changing it's fonts during skinning/painting, so my font in the list was no longer valid for a control and may have been disposed by GC.
So storing the fonts for later disposal doesn't appear to be safe, and I wondered then if I should instead store controls that had had their fonts changed.
By this time the prospect of continuing along this line lost its attractiveness and instead I've manually gone through all 200+ forms in my app with search/replace instead lol
Im not very pleased.
来源:https://stackoverflow.com/questions/29103522/should-i-dispose-of-the-old-font-when-changing-the-font-of-a-control