You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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);
    }
}

使用方式

  1. 程序入口初始化字体
    在Program.cs的Main方法中,先加载字体再启动主窗体:
[STAThread]
static void Main()
{
    Application.SetHighDpiMode(HighDpiMode.SystemAware);
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    
    // 初始化自定义字体
    CustomFontManager.InitializeFonts();
    
    Application.Run(new HomeForm());
}
  1. 跨窗体使用
    在主窗体或其他窗体中,直接调用方法给控件设置字体:
// 主窗体给startBtn设置字体
CustomFontManager.SetBoldFont(startBtn, 14);

// 其他窗体给Label设置字体
CustomFontManager.SetBoldFont(myInfoLabel, 12);

注意事项

  • 不要手动Dispose_fontCollection,直到程序退出时再处理,否则会导致所有依赖它的字体失效。
  • 控件销毁时,Font对象会自动释放资源,也可以手动调用control.Font.Dispose()避免内存泄漏。
  • 如果需要多种自定义字体,可扩展CustomFontManager添加多个FontFamily属性和初始化逻辑。

内容的提问来源于stack exchange,提问作者Simple_Code

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:35:28