Windowsフォームのコントロールサイズに合わせてフォントを自動縮小する

Windowsフォームにおいて、コントロールのサイズを可変にした場合、中のフォントの大きさは自動では変わってくれない…という問題に突如ぶち当たったので、その対策を考えてみました。

いろいろ調べてみたのですが、一発で調整出来るような方法は存在せず、テキスト描画時のサイズを見て微調整していくようなコードを実装するしかなさそうです。

コントロールの内部にテキストを描画したときの大きさは、以下のメソッドで取得出来ます。

Graphics.MeasureString Method (System.Drawing) | Microsoft Docs

というわけで、予め最大のフォントサイズを指定しておき、このメソッドで取得したサイズとコントロール自体のサイズを比較して、徐々にフォントサイズを小さくしていく…という方式を採りました。

具体的には以下のような実装になります。

Graphics g = pe.Graphics;
Font realFont = this.Font; // 描画するフォントサイズ

// 描画領域のサイズを計測
SizeF size = g.MeasureString(this.Text, realFont);

// 縦横サイズの両方が、コントロールの表示領域に収まっているかチェック
while (size.Width >= this.Width || size.Height >= this.Height)
{
    // フォントサイズが1.0以下になった場合は中断
    if (realFont.Size <= 1.0f)
        break;

    // 収まりきらなかった場合は、フォントサイズを1.0小さくして再度描画サイズを計測
    realFont = new Font(realFont.FontFamily, realFont.Size - 1.0f);
    size = g.MeasureString(this.Text, realFont);
}

サンプルコードを以下に示しておきます。以下ではLabelを使用していますが、他のコントロールでも同じ理屈で実装出来るかと思います。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace AutoShrinkLabelSample
{
    public partial class AutoShrinkLabel : Label
    {
        public AutoShrinkLabel()
        {
            InitializeComponent();
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            Graphics g = pe.Graphics;
            Font realFont = this.Font;

            SizeF size = g.MeasureString(this.Text, realFont);
            while (size.Width >= this.Width || size.Height >= this.Height)
            {
                if (realFont.Size <= 1)
                    break;

                realFont = new Font(realFont.FontFamily, realFont.Size - 1);
                size = g.MeasureString(this.Text, realFont);
            }

            TextFormatFlags flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter;
            TextRenderer.DrawText(g, this.Text, realFont, new Rectangle(0, 0, (int)size.Width, (int)size.Height), this.ForeColor, flags);
        }
    }
}

※追記
上記のコードは、LabelオリジナルのOnPaintの実装を潰してしまってるので、いくつかのプロパティ(例えばTextAlignとか)の指定が効かなくなってしまいます。
オリジナル実装を残したままフォントサイズだけ変えることが出来れば良いのですが、それも難しそうなので、overrideしたOnPaintの中で個別に実装していくのが宜しいかと思います。

※追記2
サンプルプロジェクトをgithubに上げてみました。
GitHub - aquarla/CSharp-Auto-Shrink-Label-Sample