【问题标题】:Should I dispose of the old font when changing the font of a control?更改控件的字体时是否应该处理旧字体?
【发布时间】:2015-03-17 15:44:01
【问题描述】:

C#,Windows 窗体应用程序。

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

  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()”行,那么我不会得到异常。

这是第三方控件集的错误还是可以预料到的?
内存方面,我可以不明确地处理旧字体(该应用每天在信息亭上运行 12 小时以上)吗?

【问题讨论】:

  • 如果您的电话无法正常工作,请考虑使用 Reflector 或 ILSpy 之类的反编译器来查看第 3 方代码。看看它的 Font 属性设置器做了什么。
  • 好的规则是只处理您添加的内容。你加oldFont了吗?不,不要丢弃它。其他东西添加了它并将处理它。你只负责处理newFont(当表单被卸载时)。
  • 我决定将创建的字体在创建时存储到一个列表中,并在最后处理它们。但我这样做也出错了。

标签: c# winforms fonts dispose


【解决方案1】:

不要处理旧字体,这是您要更改字体的控件的工作。此外,使用 GDIView 等工具来监控您的句柄(例如字体)。

【讨论】:

  • 据我所知,无论是在设置新的Font 时还是在控件本身被销毁时,都没有任何内置控件释放其关联的Font。这样的字体被简单地放弃了,如果在表单的生命周期内被放弃的字体数量仅限于控件的数量,这可能会很好,但如果在表单的生命周期中创建和放弃了许多字体,则可能不太好。控件的生命周期。
【解决方案2】:

控制字体非常奇怪,因为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 是否被其他一些反对其处置的代码使用。

我真的希望 MS 记录了应该如何处理 Font 资源。不幸的是,据我所知他们没有。

【讨论】:

  • 如果在窗体或控件尚未显示(未创建句柄)时执行此操作,则在显示控件时会发生“ArgumentException”。异常在 ToLogFont 方法中触发,从 'System.Windows.Forms.NativeWindow.DebuggableCallback' 开始......它将关闭您的应用程序。用标签和文本框测试
  • @Kabwla-TwoLips:很有趣。 Font 的设计看起来真的是一团糟,恕我直言,因为它结合了不可变状态和资源。
  • 我对这些可能需要也可能不需要处理的许多 gdi 类型所做的就是制作我自己的类型,它只有简单的描述(字体名称、大小和样式)。然后我给它一个Use 方法,它将Font 的动作包装在using 块中。我发现这至少对PenSolidBrush,尤其是Font很有用。
【解决方案3】:

我决定将创建的字体在创建时存储到一个列表中,并在 form.Dispose() 期间处理它们。但是在某些窗口上我也遇到了错误。

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

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

到这个时候,沿着这条路线继续前进的前景失去了吸引力,相反,我已经手动浏览了我的应用程序中的所有 200 多个表单,而不是搜索/替换,哈哈

我不是很高兴。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-19
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多