はじめに
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までさかのぼってくれます。