[WPF/xaml]条件により使うResourceDictionaryを切り替えて、画面の見た目を変える


やりたいこと

画面に配置するコントロール類は変えずに、条件によってコントトール類の見た目を変えたい。
イメージとしては、同じアプリでも

  • お客様Aで動くアプリの画面では、背景は黒で、ボタンは赤色
  • お客様Bで動くアプリの画面では、背景は白で、ボタンは青色

というようなことをしたい。

やり方

リソースディクショナリを切り替えるという方法で、実現できる。
(リソースディクショナリの作り方 → こちら)

サンプル

リソースディクショナリを2つ作る

ここでは、「Dictionary1.xaml」と「Dictionary2.xaml」の2つを作っている。

中身は下記の通り。GridとButtonのstyleを定義している。
(違いは、GridとButtonのBackgroundの色のみ)

Dictionary1.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp20">
    <Style x:Key="MyButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="Red"/>
    </Style>
    <Style x:Key="MyGridStyle" TargetType="Grid">
        <Setter Property="Background" Value="Black"/>
    </Style>
</ResourceDictionary>
Dictionary2.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp20">
    <Style x:Key="MyButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="Blue"/>
    </Style>
    <Style x:Key="MyGridStyle" TargetType="Grid">
        <Setter Property="Background" Value="White"/>
    </Style>
</ResourceDictionary>

Dictionaryを使うメイン画面

GridとButtonのStyleに、Keyを使ってリソースディクショナリに記述したstyleを指定している。

MainWindow.xaml
<Window x:Class="WpfApp20.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp20"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="200">

    <Grid Style="{StaticResource MyGridStyle}">
        <Button Content="ボタン" Margin="20" Style="{StaticResource MyButtonStyle}"/>
    </Grid>
</Window>

※リソースエディタ上ではこのように見えてる。

リソースディクショナリの切り替えをする部分

App.xamlのコードビハインドに、ディクショナリの切り替えをする処理を記述する。
ここに書けば、ほかのxamlで書いたWindowにも反映されるっぽい。

App.xaml.cs
using System;
using System.Windows;

namespace WpfApp20
{
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application
    {
        public App()
        {
            // 条件により、読み込むDictionary.xamlを変える
            string dicPath = (true) ? "Dictionary1.xaml" : "Dictionary2.xaml";

            ResourceDictionary dic = new ResourceDictionary();
            dic.Source = new Uri(dicPath, UriKind.Relative);
            this.Resources.MergedDictionaries.Add(dic);
        }
    }
}

※App.xamlは、プロジェクト作成時のまま変更なし。

動作

これで完成。
App.xaml.csの「string dicPath = (true) ? "Dictionary1.xaml" : "Dictionary2.xaml";」の部分の条件をtrueにするかfalseにするかで、下記のように画面が変わる。

問題

このやり方で実現できるのだが、画面を作成時、デザイナーを開いてxamlコードをみると、下図のように、波線でエラーが出てしまう。(エラー(警告?)は出ているが、ビルドは通る。)

実行時にしかリソースディクショナリが紐づけられないからだと思われる。
実行時に同じ個所を見ると、実行の際にディクショナリが紐づけられたためか、波線の警告は出ていない。

動きはするが、デザイン時にどういう画面表示になるか見ることができず、不便。
ざっと調べてはみたが、これといった解決方法を見つけられず。→折を見て、調べてみる。

別のプロジェクトにリソースディクショナリのxamlがある場合

下記を参照。
https://qiita.com/NumAniCloud/items/3d64199aee8876d53f67

参考

WPF アプリケーションの国際化 (Windows/.NET/WPF)
http://umezawa.dyndns.info/wordpress/?p=5131