syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

XAMLルート要素のプロパティのローカライズ

(これは2012-06-19に書いた故OCNブログの記事を移植したものです)

コレ、結構悩んだんですが、WPFにおいてWindowとかUserControlとかPageとかのルート要素のフォントなどをローカライズするためにリソース文字列を指定するには、(Attribute Syntaxは使わずに)StaticResourceExtensionクラスをProperty Element Syntaxで遅延指定するのがコツみたいです。
ResourceDictionaryを読み込む順序関係上、ローカライズというかルート要素自身のResourcesに含まれるStaticResourceを使って属性値を
「FontFamily="{Binding Path=MyUIFontName, Source={StaticResource MyResourcesInstance}}"」
の形で直接指定できそうにないっぽいから、糖衣構文で解決しました、みたいな感じです。

ちなみに参考にしたのはココです。とても読みやすい。

下記、手順を簡単に書き出してみます。Visual Studio IDEでの操作を想定しています。

[1]
例えばまず文字列リソースに、英語版だと"Segoe UI"、日本語版だと"Meiryo UI"になるようなリソース文字列MyUIFontNameを作ります。さらに、リソース ディクショナリにてUserControl用のStyleとしてMyUserControlBaseStyleを定義して、その中でFontFamilyプロパティに対してMyUIFontNameをStaticResource経由でバインド指定します。

……
    <Style TargetType="UserControl" x:Key="MyUserControlBaseStyle">
        <Setter Property="FontFamily" Value="{Binding Path=MyUIFontName, Source={StaticResource MyResourcesInstance}}" />
    </Style>
……

[2]
その後、

<UserControl x:Class="MyWpfCtrls.UserControl1" ...>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="MyResDictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resouces>
    <UserControl.Style>
        <StaticResourceExtension ResourceKey="MyUserControlBaseStyle"/>
    </UserControl.Style>
</UserControl>

な感じでルート要素にスタイルを適用することでそのプロパティを一括オーバーライド設定します。まさにHTMLのスタイル シート的発想。

もし特定の属性(プロパティ)だけ、例えばWindowのTitle文字列を個別にローカライズする場合、

……
    <Window.Title>
        <Binding Path="MyWinTitleName" Source="{StaticResource MyResourcesInstance}"/>
    </Window.Title>
……

のようにBindingクラスを使って書くこともできます。FontFamilyやBackgroundなどをすべてのウィンドウやコントロールで統一したり一括管理したりするときは、Styleを使って基本設定用のテンプレートを用意しておくのがお勧めです。

アプリケーション側がWPFだったらスタートアップXAML(App.xaml)にMergedDictionariesを指定できるので、たぶんAttribute Syntaxでもいけると思うんですが、アプリ側がWinFormsやMFCになっていて、相互運用するときはスタートアップXAMLが使用できなさそうなのでこんな小手先のテクニックを使いました……

実際には他にもっと良い方法があるのかも。まだまだXAMLの仕様や使い方を把握しきれてないので、トンチンカンなコードを書いてる可能性もなきにしもあらずです。

余談1

ちなみに自分はSegoe UIよりVerdanaのほうが読みやすいと思います。Verdanaは日本語WPF 3.5環境ではメイリオ(Meiryo)にフォント合成(GDIでいうFont Link)されるっぽいので*1、いっそ日英ともにVerdanaを使ったほうがよいかと。ただ、Verdana、Meiryo、Meiryo UIの全角ギリシャ文字は超絶的に判別不能で美しくないので注意。これらに関してはSegoe UIのほうが読みやすいです。

余談2

そういえばExtended WPF Toolkit、結構使えそうです。Windows Formsにしかなかった、かゆいところに手が届く的なコントロール類も豊富に用意されています。ただしコイツは本家のWPF Toolkitにアセンブリ依存するっぽいので、再配布するときは忘れないように注意が必要(WPF Toolkit自体がWPF標準ライブラリ外の傍流なんですが)。なお、Windows Formsとまったく同じ要領で使えるかというとそうではなく、"WPFらしい"実装に置き換わっています。MSにはこういった優秀なコントロールを標準としてもっとガンガン追加していって欲しいんですが……

*1:WPF 4.5ではSegoe UIやConsolasを使うと、デフォルトで日本語部分がMeiryo UIになる模様です。