ElementHost 使用時に WPF のフォントを“デフォルト”に戻す方法

id:NyaRuRu:20071221:p1 に関係するようなしないような話ですが,まあせっかくなのでついでのネタ.

何が問題か?

WPF と WinForms の相互運用の気合いのは入り方はすばらしく,WPF の UI Element 中に WinForms Control (or Win32 ウィンドウ) をホストすることも,WinForms Control 中に WPF の UI Element をホストすることも可能です.ただし,WinForms アプリケーションの中に WPF UI Element を埋め込む場合,Windows Vista など新しい環境では,WPF 描画部分のフォント設定が普段と変わって見苦しく感じるかもしれません.
WPF アプリケーションを Vista 環境で実行すると,デフォルトでメイリオや Segoe といった新しいフォントが使用されるはずです.一方 WinForms 2.0 アプリケーションは,その他の“Legacy”な Win32 アプリケーション同様,Windows Vista 環境で実行しても基本的に古い UI フォント (MS UI Gothic など) を使用します.WinForms アプリケーションに ElementHost クラスを利用して WPF 要素をはめ込んだ場合,ElementHost のフォント設定に古い UI フォントが使わうため,その子である WPF 描画要素も古いフォントを使用することになります.

スクリーンショット


実行環境は Windows Vista Ultimate Edition 日本語版.上の WPF ボタンが何もしない場合で,WinForms のデフォルトフォント (この場合は MS UI Gothic) が使用されています.下の WPF ボタンが SystemFonts.MessageBoxFont を再設定したものです.

コードと解説

SystemFonts.MessageBoxFont を使うというアイディアは『Light Up Your Fonts on Vista: Selecting Segoe UI Using GetThemeSysFont, NONCLIENTMETRICS, or SystemFonts.MessageBoxFont』からいただいています.
コードは以下で全部です.必要なアセンブリさえ参照設定に加えていただければ,コンソールアプリケーション用のプロジェクトでもコピペで動くはずです.

using System;
using GDIPlus = System.Drawing;
using WinForms = System.Windows.Forms;
using System.Windows.Controls;
using System.Windows.Forms.Integration;

// Required Assemblies
// - PresentationCore.dll
// - PresentationFramework.dll
// - System.dll
// - System.Drawing.dll
// - System.Windows.Forms.dll
// - System.Windows.Presentation.dll
// - WindowsBase.dll
// - WindowsFormsIntegration.dll

static class Program
{
    [STAThread]
    static void Main()
    {
        WinForms::Application.EnableVisualStyles();
        WinForms::Application.SetCompatibleTextRenderingDefault(false);

        var form = new WinForms::Form()
        {
            AutoScaleDimensions = new GDIPlus::SizeF(6F, 12F),
            AutoScaleMode = WinForms::AutoScaleMode.Font,
            ClientSize = new GDIPlus::Size(200, 200),
            Name = "MyForm",
            Text = "Font Test",
        };

        form.Load += (_o, _e) =>
        {
            new []
            {
                new ElementHost()
                {
                    Dock = WinForms::DockStyle.Top,
                    ClientSize = new GDIPlus::Size(0, 100),
                    Child = new Button() { Content = "Button1" },
                },
                new ElementHost()
                {
                    Dock = WinForms::DockStyle.Bottom,
                    ClientSize = new GDIPlus::Size(0, 100),
                    Font = GDIPlus::SystemFonts.MessageBoxFont,
                    Child = new Button() { Content = "Button2" },
                },
            }.ToList().ForEach(form.Controls.Add);
        };

        WinForms::Application.Run(form);
    }
}