多数の値の変換を必要とするビューを持つWPFアプリケーションに取り組んでいます。当初、私の哲学(XAML Disciplesに関するこの活発な議論に一部影響を受けました)は、ビューのデータ要件のサポートについて厳密にビューモデルを作成することでした。つまり、データを可視性、ブラシ、サイズなどに変換するために必要な値の変換は、値コンバーターと多値コンバーターで処理されます。概念的には、これは非常にエレガントに見えました。ビューモデルとビューの両方に明確な目的があり、うまく分離されます。「データ」と「外観」の間に明確な線が引かれます。
まあ、この戦略を「古い大学の試み」にした後、私はこの方法で開発を続けたいかどうか疑問に思っています。私は実際に、値コンバーターをダンプし、(ほとんど)すべての値変換の責任をビューモデルの手に直接置くことを強く考えています。
値コンバータを使用する現実は、明確に分離された懸念の見かけの価値まで測定しているようには見えません。値コンバーターの最大の問題は、使用するのが面倒なことです。新しいクラスを作成し、実装するIValueConverter
かIMultiValueConverter
、値をobject
適切な型にキャストし、DependencyProperty.Unset
(少なくとも多値コンバーターの場合)テストし、変換ロジックを記述し、コンバーターをリソースディクショナリに登録する必要があります [下記の更新を参照]、最後に、かなり冗長なXAMLを使用してコンバーターを接続します(バインディングとコンバーターの名前の両方にマジックストリングを使用する必要があります)[以下の更新を参照])。特にVisual Studioのデザインモード/ Expression Blendでは、エラーメッセージはしばしば不可解であるため、デバッグプロセスもピクニックではありません。
これは、すべての価値変換を担当するビューモデルを作成するという代替策が改善であると言っているわけではありません。これは、草が反対側でより緑であるという問題である可能性が非常に高い。関心事のエレガントな分離を失うことに加えて、派生プロパティの束を記述し、RaisePropertyChanged(() => DerivedProperty)
ベースプロパティを設定する際に慎重に呼び出す必要があります。これは、不快なメンテナンスの問題であることがわかります。
以下は、ビューモデルに変換ロジックを処理させ、値コンバーターを廃止することの長所と短所をまとめた最初のリストです。
- 長所:
- マルチコンバーターが排除されるため、総バインディング数が少なくなります
- 少ないマジックストリング(バインディングパス
+コンバーターリソース名) 各コンバータを登録する必要はありません(さらにこのリストを維持します)- 各コンバーターを作成する作業が少なくなります(インターフェイスの実装やキャストは不要)
- 変換に役立つ依存関係を簡単に挿入できます(例:カラーテーブル)
- XAMLマークアップは冗長ではなく、読みやすい
- コンバーターの再利用は引き続き可能です(ただし、ある程度の計画が必要です)
- DependencyProperty.Unsetに不可解な問題はありません(複数値コンバーターで気づいた問題)
*取り消し線は、マークアップ拡張機能を使用すると消える利点を示しています(以下の更新を参照)
- 短所:
- ビューモデルとビューの間のより強い結合(たとえば、プロパティは可視性やブラシなどの概念を処理する必要があります)
- ビュー内のすべてのバインディングの直接マッピングを可能にする、より多くのプロパティ
(以下の更新2を参照)RaisePropertyChanged
派生プロパティごとに呼び出す必要があります- 変換がUI要素のプロパティに基づいている場合、コンバーターに依存する必要があります
おそらくおわかりのように、この問題については胸焼けがあります。私は、リファクタリングの道を進むのを非常にためらっていますが、値変換を使用するか、ビューモデルで多数の値変換プロパティを公開するかどうかにかかわらず、コーディングプロセスが同じように非効率的で退屈です。
賛否両論ありませんか?価値変換の両方の手段を試した人にとって、あなたにとってどちらがより効果的であると思いましたか?他の選択肢はありますか?(弟子たちは、タイプ記述子プロバイダーについて何かを言及しましたが、彼らが話していることを理解できませんでした。これについての洞察はありがたいです。)
更新
本日、「マークアップ拡張機能」と呼ばれるものを使用して、値コンバーターを登録する必要をなくすことができることを発見しました。実際、これらを登録する必要がなくなるだけでなく、を入力しConverter=
たときにコンバーターを選択するためのインテリセンスが実際に提供されます。:ここでは私が始まった記事ですhttp://www.wpftutorial.net/ValueConverters.htmlが。
マークアップ拡張機能を使用する機能により、上記の長所と短所のリストと議論のバランスが多少変わります(取り消し線を参照)。
この啓示の結果として、私はハイブリッド私がコンバータを使用するシステムで実験てるBoolToVisibility
と私は呼んでMatchToVisibility
、他のすべての変換のためのビューモデル。MatchToVisibilityは基本的に、バインドされた値(通常は列挙型)がXAMLで指定された1つ以上の値と一致するかどうかを確認できるコンバーターです。
例:
Visibility="{Binding Status, Converter={vc:MatchToVisibility
IfTrue=Visible, IfFalse=Hidden, Value1=Finished, Value2=Canceled}}"
基本的にこれは、ステータスが終了またはキャンセルのいずれかであるかを確認します。そうである場合、可視性は「可視」に設定されます。それ以外の場合、「非表示」に設定されます。これは非常に一般的なシナリオであることが判明し、このコンバーターを使用すると、ビューモデルの約15個のプロパティ(および関連するRaisePropertyChangedステートメント)が節約されました。を入力するとConverter={vc:
、「MatchToVisibility」がインテリセンスメニューに表示されることに注意してください。これにより、エラーの可能性が著しく減少し、値コンバーターの使用が面倒になりにくくなります(必要な値コンバーターの名前を覚えたり検索したりする必要がなくなります)。
興味があれば、以下のコードを貼り付けます。この実装の一つの重要な特徴は、MatchToVisibility
それがバインドされた値であるかどうかをチェックしていることでenum
、それがあれば、それはチェックを確認するためにValue1
、Value2
なども同じ型の列挙型です。これにより、列挙値のいずれかが誤って入力されているかどうかの設計時および実行時チェックが提供されます。これをコンパイル時のチェックに改善するには、代わりに以下を使用できます(手で入力したので、間違えた場合はご容赦ください):
Visibility="{Binding Status, Converter={vc:MatchToVisibility
IfTrue={x:Type {win:Visibility.Visible}},
IfFalse={x:Type {win:Visibility.Hidden}},
Value1={x:Type {enum:Status.Finished}},
Value2={x:Type {enum:Status.Canceled}}"
これは安全ですが、私にとっては価値がありません。これを行う場合は、ビューモデルのプロパティを使用することもできます。とにかく、デザイン時のチェックはこれまでに試したシナリオに完全に適していることがわかりました。
ここにコードがあります MatchToVisibility
[ValueConversion(typeof(object), typeof(Visibility))]
public class MatchToVisibility : BaseValueConverter
{
[ConstructorArgument("ifTrue")]
public object IfTrue { get; set; }
[ConstructorArgument("ifFalse")]
public object IfFalse { get; set; }
[ConstructorArgument("value1")]
public object Value1 { get; set; }
[ConstructorArgument("value2")]
public object Value2 { get; set; }
[ConstructorArgument("value3")]
public object Value3 { get; set; }
[ConstructorArgument("value4")]
public object Value4 { get; set; }
[ConstructorArgument("value5")]
public object Value5 { get; set; }
public MatchToVisibility() { }
public MatchToVisibility(
object ifTrue, object ifFalse,
object value1, object value2 = null, object value3 = null,
object value4 = null, object value5 = null)
{
IfTrue = ifTrue;
IfFalse = ifFalse;
Value1 = value1;
Value2 = value2;
Value3 = value3;
Value4 = value4;
Value5 = value5;
}
public override object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var ifTrue = IfTrue.ToString().ToEnum<Visibility>();
var ifFalse = IfFalse.ToString().ToEnum<Visibility>();
var values = new[] { Value1, Value2, Value3, Value4, Value5 };
var valueStrings = values.Cast<string>();
bool isMatch;
if (Enum.IsDefined(value.GetType(), value))
{
var valueEnums = valueStrings.Select(vs => vs == null ? null : Enum.Parse(value.GetType(), vs));
isMatch = valueEnums.ToList().Contains(value);
}
else
isMatch = valueStrings.Contains(value.ToString());
return isMatch ? ifTrue : ifFalse;
}
}
ここにコードがあります BaseValueConverter
// this is how the markup extension capability gets wired up
public abstract class BaseValueConverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public abstract object Convert(
object value, Type targetType, object parameter, CultureInfo culture);
public virtual object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
これがToEnum拡張メソッドです
public static TEnum ToEnum<TEnum>(this string text)
{
return (TEnum)Enum.Parse(typeof(TEnum), text);
}
更新2
この質問を投稿してから、プロパティと依存プロパティにNotifyPropertyChangedコードを挿入するために「ILウィービング」を使用するオープンソースプロジェクトに出会いました。これにより、ビューモデルの「ステロイドの値コンバーター」としてのジョシュスミスのビジョンの実装が非常に簡単になります。「自動実装プロパティ」を使用するだけで、ウィーバーが残りを行います。
例:
このコードを入力すると:
public string GivenName { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenName, FamilyName);
}
}
...これがコンパイルされます:
string givenNames;
public string GivenNames
{
get { return givenName; }
set
{
if (value != givenName)
{
givenNames = value;
OnPropertyChanged("GivenName");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenName, FamilyName);
}
}
これにより、入力、読み取り、過去のスクロールなどのコード量を大幅に節約できます。さらに重要なことは、依存関係を把握する必要がなくなることです。呼び出しFullName
を追加するために苦労して依存関係のチェーンをさかのぼることなく、新しい「プロパティ取得」を追加できRaisePropertyChanged()
ます。
このオープンソースプロジェクトとは何ですか?元のバージョンは「NotifyPropertyWeaver」と呼ばれますが、所有者(Simon Potter)は一連のILウィーバーをホストするための「Fody」と呼ばれるプラットフォームを作成しました。この新しいプラットフォームでのNotifyPropertyWeaverに相当するものは、PropertyChanged.Fodyと呼ばれます。
- Fodyのセットアップ手順: http ://code.google.com/p/fody/wiki/SampleUsage(「Virtuosity」を「PropertyChanged」に置き換えます)
- PropertyChanged.Fodyプロジェクトサイト: http : //code.google.com/p/propertychanged/
NotifyPropertyWeaver(インストールは少し簡単ですが、バグ修正以外に将来更新される必要はありません)を使用したい場合は、プロジェクトサイトhttp://code.google.com/p/をご覧 ください。 notifypropertyweaver /
いずれにせよ、これらのILウィーバーソリューションは、ステロイドと値コンバーターのビューモデル間の議論の計算を完全に変更します。
MatchToVisibility
(私はオンとオフを切り替えることができる部品のトンで、特に1つのビューを持っている。ほとんどの場合、ビューのセクションがさえて(ラベル付けされているいくつかの簡単なモード切り替えを可能にするための便利な方法と思われたx:Name
モードに合わせて)これは「ビジネスロジック」であるとは思いもしませんでしたが、あなたのコメントに少し考えてみます。
BooleanToVisibility
可視性に関連する1つの値(true / false)を取得し、それを別の値に変換します。これは、の理想的な使用のようValueConverter
です。一方、MatchToVisibility
ビジネスロジックをエンコードするView
(表示するアイテムの種類)。私の意見では、この論理はViewModel
、私が呼んでいるものにプッシュされるべきEditModel
です。ユーザーが見ることができるのは、テスト対象のものです。