如何在C# Windows Form程序中正确引入自定义字体(跨窗体崩溃问题)
解决WinForms跨窗体使用内存自定义字体崩溃问题
问题重现
开发WinForms程序时,尝试给Label和Button应用自定义内存字体,当前代码仅在主窗体(Home form)生效,其他窗体使用时程序运行约5秒后会停止。
核心原因
你的代码中PrivateFontCollection pfc是loadBold方法的局部变量,方法执行完毕后会被.NET垃圾回收器(GC)回收。但你创建的FontFamily ff和所有基于它生成的Font对象,都依赖这个集合的底层资源。当其他窗体尝试使用这些字体时,GC已经回收了pfc,导致字体资源失效,触发程序崩溃。
解决方案
将PrivateFontCollection改为静态类级别的成员,让它在整个程序生命周期内保持引用,避免被GC回收。同时统一封装字体的加载和分配逻辑,方便跨窗体调用。
修改后的完整代码
using System; using System.Drawing; using System.Drawing.Text; using System.Runtime.InteropServices; using System.Windows.Forms; public static class CustomFontManager { // 静态私有集合,全局保持引用,防止被GC回收 private static readonly PrivateFontCollection _fontCollection = new PrivateFontCollection(); public static FontFamily BoldFontFamily { get; private set; } [DllImport("gdi32.dll")] private static extern IntPtr AddFontMemResourceEx(IntPtr pbfont, uint cbfont, IntPtr pdv, [In] ref uint pcFonts); // 程序启动时调用一次,初始化自定义字体 public static void InitializeFonts() { byte[] fontBytes = Counter.Properties.Resources.TheSans; int dataLength = fontBytes.Length; IntPtr fontPtr = Marshal.AllocCoTaskMem(dataLength); Marshal.Copy(fontBytes, 0, fontPtr, dataLength); uint fontCount = 0; AddFontMemResourceEx(fontPtr, (uint)dataLength, IntPtr.Zero, ref fontCount); _fontCollection.AddMemoryFont(fontPtr, dataLength); Marshal.FreeCoTaskMem(fontPtr); BoldFontFamily = _fontCollection.Families[0]; } // 给指定控件分配自定义粗体字体 public static void SetBoldFont(Control targetControl, float fontSize) { if (BoldFontFamily == null) throw new InvalidOperationException("请先调用InitializeFonts初始化字体资源"); targetControl.Font = new Font(BoldFontFamily, fontSize, FontStyle.Bold); } }
使用方式
- 程序入口初始化字体
在Program.cs的Main方法中,先加载字体再启动主窗体:
[STAThread] static void Main() { Application.SetHighDpiMode(HighDpiMode.SystemAware); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // 初始化自定义字体 CustomFontManager.InitializeFonts(); Application.Run(new HomeForm()); }
- 跨窗体使用
在主窗体或其他窗体中,直接调用方法给控件设置字体:
// 主窗体给startBtn设置字体 CustomFontManager.SetBoldFont(startBtn, 14); // 其他窗体给Label设置字体 CustomFontManager.SetBoldFont(myInfoLabel, 12);
注意事项
- 不要手动Dispose
_fontCollection,直到程序退出时再处理,否则会导致所有依赖它的字体失效。 - 控件销毁时,
Font对象会自动释放资源,也可以手动调用control.Font.Dispose()避免内存泄漏。 - 如果需要多种自定义字体,可扩展
CustomFontManager添加多个FontFamily属性和初始化逻辑。
内容的提问来源于stack exchange,提问作者Simple_Code
相关产品推荐
相关产品推荐

