はじめに
MVVMで永遠に付いて回る悩みの一つは、Viewの持ち物であるデザインリソースをViewModelといかに切り離すかです。
例えば、カラーやブラシなどロジックとは関係ないリソースは、ViewModelでは持たずリソースのキー名だけで管理できるのが理想です。
WPFには、FindResource関数が用意されていますが、コイツは、リソース本体を読み込んで返すという基本的な関数なためViewModelに、重たいリソース情報を持つことになります。
また、Viewの参照も必要とするためMVVMとは相性の良くない機能です。
Styleを活用する
WPFには、Styleという機能が用意されています。
ご存じない方に一言で説明するならば、プロパティ値の集合がStyleです。
Xamlのタグに、Windth="100"というようにプロパティ設定を行いますが、このWindth="100"という部分だけを纏めたものがStyleです。
そのためターゲットとなるオブジェクトによって、設定できるプロパティ名は変わってきます。
ViewModelから状態によって、見た目を変化させるというプログラムの多くは、Styleを切り替える事で実現出来るはずです。
StyleConverterを制作する
疎結合を保つためViewModelには、リソースのキー名だけを持つようにします。
Xamlの受け手のStyle=""は、Styleクラスですので、そのままバインディングできません。
このように、バインディング値を変換しなければならない時には、Converterという仕組みを利用します。
public class StyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
FrameworkElement targetElement = values[0] as FrameworkElement;
string styleName = values[1] as string;
if (styleName == null)
{
return null;
}
Style newStyle = (Style)targetElement.TryFindResource(styleName);
return newStyle;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
上記のコードが、StyleConverterの実装例です。
呼び出し例
呼び出しは、マルチバインディングを使っています。
1つ目が、リソースを格納しているオブジェクトへの参照で、2つ目が、キー名となります。
ViewModelの記述は、割愛します。
<Button>
<Button.Style>
<MultiBinding>
<MultiBinding.Converter>
<converters:StyleConverter />
</MultiBinding.Converter>
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}" />
<Binding Path="ButtonStyle" />
</MultiBinding.Bindings>
</MultiBinding>
</Button.Style>
</Button>