ViewModelの文字列キーからリソースを取得するコンバーター


はじめに

ViewModelにリソースを持つのはよくないよねー
ってことでViewModelにはキーだけで、そこからリソース本体を持ってくるコンバーターを作りました。

コンバーター本体

class StaticResourceConverter : MarkupExtension, IValueConverter
{
    private FrameworkElement _target = null;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var resourceKey = (string)value;

        return _target?.TryFindResource(resourceKey) ?? Application.Current.TryFindResource(resourceKey);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var rootObjectProvider = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
        if (rootObjectProvider == null)
            return this;

        _target = rootObjectProvider.RootObject as FrameworkElement;
        return this;
    }
}

ProvideValueのおかげで、コンバーターが置いてあるXAMLのFrameworkElementが取れます。
あとはそこからTryFindResourceでリソースが持ってこれます。

使用例

わかりやすくするために、不要であろう部分を削除しています。

sample.xaml
<UserControl x:Class="FreeCellPlug.Views.Card"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:vm="clr-namespace:FreeCellPlug.ViewModels"
             xmlns:conv="clr-namespace:FreeCellPlug.Views.Converter"
             xmlns:local="clr-namespace:FreeCellPlug.Views"
             mc:Ignorable="d"              
             d:DesignHeight="350" d:DesignWidth="250" 
             d:DataContext="{d:DesignInstance {x:Type vm:CardViewModel}}">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Views/Cards/Trump.xaml"/> <!-- ここにリソースディクショナリがある -->
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

    <ContentControl>
        <Viewbox>
            <Rectangle Fill="{Binding ResourceKey, Converter={conv:StaticResourceConverter}}" />
        </Viewbox>
    </ContentControl>
</UserControl>

MarkupExtensionを継承しているおかげで、Converter={conv:StaticResourceConverter} を追加するだけで使用できます。
TryFindResourceは、コンバーターを置いたのがRectangleでも、リソースがあるUserControlまでさかのぼってくれます。

参考:
https://stackoverflow.com/questions/695624/binding-a-datacontext-string-property-to-a-staticresource-key/695657#695657