当更改控件的字体时,我是否应该丢弃旧字体?

3

C#,Windows Forms 应用程序。

我们正在重新设计我们的应用程序,并且我还要更改旧版本中使用的糟糕的默认字体。因此,我想在加载窗体时调用以下函数来更改窗体上所有控件的字体。

  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);
  }

我认为在通过新字体重新分配控件后处理旧字体可以节省资源,但是在关闭表单时,来自第三方控件集合的一种控件类型会导致访问冲突异常。如果我注释掉“oldFont.Dispose()”这一行,那么我就不会收到异常信息了。
这是第三方控件集的一个bug还是预期行为呢?从内存角度来看,不明确处理旧字体是否可行(应用程序在kiosks上每天运行12个小时以上)?

如果你的电话不工作,那么考虑使用反编译器(如Reflector或ILSpy)来查看第三方代码。看看它的字体属性设置器做了什么。 - Hans Passant
3
好的规则是只处理你添加的内容。你是否添加了oldFont?没有。不要处理它。其他东西添加了它并会进行处理。你只需要负责在窗体卸载时处理newFont即可。 - Sinatr
我决定将创建的字体存储到列表中,并在最后处理它们,但我在执行此操作时也遇到了错误。 - Czeshirecat
3个回答

2
不要丢弃旧字体,这是正在更改字体的控件的工作。此外,使用诸如GDIView之类的工具来监视句柄(例如字体)。

1
据我所知,内置控件中没有一个会在设置新的字体或销毁控件本身时处理其关联的“Font”。这些字体被简单地遗弃,如果表单生命周期内遗弃的字体数量仅限于控件数量,则可能还好,但如果在控件生命周期内创建和遗弃了许多字体,则可能不太好。 - supercat

1
控制字体非常奇怪,因为 Font 对象实际上封装了两个不同的东西:
  1. 有关字体、字形等的信息。
  2. GDI 字体句柄。
Font 对象的后者方面封装了一个资源;前者则没有。对 Font 对象调用 Dispose 会释放后者,但不会销毁前者。
虽然我认为它们的行为没有在任何地方“正式”记录下来,但似乎内置控件从未使用用于设置 Font 属性的 Font 对象来绘制;相反,它们使用分配的 Font 对象的属性生成新的 GDI 字体对象,然后使用该对象来绘制控件。尽管对字体对象调用 Dispose 会使其无法作为 DrawString 或其他类似方法的参数使用,但这并不会阻止其用作制作新字体对象的“模板”。
这意味着控件不会在已分配的Font对象被释放时(无论是在分配之前还是之后),也不会对其进行释放。从控件中读取Font属性将始终返回上次分配给它的相同Font对象,而不考虑该Font对象是否被释放。因此,如果没有任何东西会读取控件的Font属性并期望进行绘制,为了避免临时资源泄漏,最安全的方法是使用非常奇怪的方式来分配控件的Font属性:
using f = new Font(...)
  theControl.Font = f;

这可能比读取控件的Font属性并在分配新值之前处置它更安全,因为上述using方法知道分配给控件的Font不会用于其他任何事情,而后一种方法无法知道是否由某些其他代码使用了相同的Font,并且会反对其处理。
我真希望微软能够记录下应该如何处理Font资源。不幸的是,据我所知,他们还没有这样做。

如果在表单或控件尚未显示(没有句柄创建)时执行此操作,则在显示控件时会引发“ArgumentException”异常。该异常由ToLogFont方法触发,起始于“System.Windows.Forms.NativeWindow.DebuggableCallback”... 它将使您的应用程序崩溃。已使用Label和TextBox进行测试。 - Kabwla-TwoLips
@Kabwla-TwoLips:有趣。在我看来,Font的设计真的有些混乱,因为它将不可变状态与资源结合在一起。 - supercat
我为许多可能需要处理的gdi类型创建了自己的类型,该类型仅具有简单的描述(字体名称、大小和样式)。然后,我提供了一个“使用”方法,它将“Font”中的操作包装在“using”块中。我发现这对于至少“Pen”、“SolidBrush”和特别是“Font”非常有用。 - Dave Cousineau

0

我决定将已创建的字体存储到列表中,并在form.Dispose()期间处理它们。但是在某些窗口上,这样做也会出现错误。

我刚刚意识到的是,第三方控件在皮肤/绘画期间也必须更改其字体,因此列表中的字体对于控件而言不再有效,并且可能已被GC处理。

因此,将字体存储以供稍后处理似乎并不安全,我想知道是否应该存储已更改字体的控件。

到这个时候,继续沿着这条路线进行的前景失去了吸引力,所以我手动查找/替换了我的应用程序中的所有200多个表单。

我不太高兴。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接