37

主にユニットのテスト容易性を向上させるために、WPFアプリケーションで依存性注入の使用を開始したいと思います。私のアプリは主にMV-VMパターンに沿って構築されています。私はIoCコンテナのAutofacを見ていますが、それはこの議論にとってそれほど重要ではないと思います。

App.xaml.csでコンテナーを作成して解決できるため、開始ウィンドウにサービスを挿入するのは簡単なようです。

私が苦労しているのは、ViewModelsとServicesをユーザーコントロールにDIする方法です。ユーザーコントロールはXAMLマークアップを介してインスタンス化されるため、ユーザーコントロールを使用する機会はありませんResolve()

私が考えることができる最善の方法は、コンテナーをシングルトンに配置し、ユーザーコントロールにグローバルコンテナーからViewModelを解決させることです。これは、コンポーネントがServiceLocatorに依存している必要があるため、せいぜい中途半端な解決策のように感じます。

WPFで完全なIoCは可能ですか?

[編集]-Prismが提案されていますが、Prismを評価することさえ大きな投資のようです。もっと小さいものが欲しいです。

[編集]これが私が止められたコードフラグメントです

//setup IoC container (in app.xaml.cs)
var builder = new ContainerBuilder();
builder.Register<NewsSource>().As<INewsSource>();
builder.Register<AViewModel>().FactoryScoped();
var container = builder.Build();

// in user control ctor -
// this doesn't work, where do I get the container from
VM = container.Resolve<AViewModel>();

// in app.xaml.cs
// this compiles, but I can't use this uc, 
//as the one I want in created via xaml in the primary window
SomeUserControl uc = new SomeUserControl();
uc.VM = container.Resolve<AViewModel>();
4

8 に答える 8

17

実際には非常に簡単です。jedidja が言及したように、Prism にこの例があります。ViewModel に View を挿入するか、View に ViewModel を挿入することができます。Prism StockTraderRI では、View を ViewModel に挿入していることがわかります。基本的に、View (および View インターフェース) には Model プロパティがあります。このプロパティはコード ビハインドに実装され、DataContext を値に設定します (例: this.DataContext = value;. ViewModel のコンストラクターでは、View が注入されます。次に、View.Model = this;自身を DataContext として渡すものを設定します。

また、逆の操作を簡単に実行して、ViewModel をビューに挿入することもできます。ViewModel にはビューへの後方参照がまったくないことを意味するため、実際にはこれを好みます。これは、ViewModel を単体テストするときに、モックに対するビューさえ持っていないことを意味します。さらに、View のコンストラクターで、挿入された ViewModel に DataContext を設定するだけで、コードがよりクリーンになります。

これについては、Jeremy Miller と私が Kaizenconf で行った Separated Presentation Patterns トークのビデオ録画でもう少し詳しく説明しています。その最初の部分はhttps://vimeo.com/2189854にあります。

于 2008-11-15T07:46:06.637 に答える
10

私はあなたが問題にぶつかったと思います。コントロールは、XAML を使用して宣言的に作成するのではなく、親に挿入する必要があります。

DI が機能するには、依存関係を受け入れるクラスを DI コンテナーで作成する必要があります。これは、設計時に親が子コントロールのインスタンスを持たず、デザイナーのシェルのように見えることを意味します。これはおそらく推奨されるアプローチです。

もう 1 つの「代替手段」は、コントロールのコンストラクターまたは同様のものから呼び出されるグローバルな静的コンテナーを持つことです。2 つのコンストラクターが宣言される一般的なパターンがあります。1 つはコンストラクター注入用のパラメーター リストを持ち、もう 1 つはデリゲートするパラメーターなしです。

// For WPF
public Foo() : this(Global.Container.Resolve&lt;IBar&gt;()) {}

// For the rest of the world
public Foo(IBar bar) { .. }

私はほとんどこれをアンチパターンと呼んでいますが、一部のフレームワークには他に選択肢がないという事実があります。

私は WPF の半分も専門家ではないので、ここで downmod の健全なサービスを期待しています :) しかし、これがお役に立てば幸いです。Autofac グループ (ホームページからリンクされています) は、この質問をする別の場所かもしれません。Prism または MEF のサンプル アプリ (いくつかの WPF の例を含む) を使用すると、何が可能かがわかります。

于 2008-11-22T17:43:30.830 に答える
4

同様の問題が発生しています。Expression Blend 2.0 (Strong Type) でデザイン時のサポートを提供するソリューションを期待しています。さらに、エクスプレッション ブレンドで利用可能なモック + 自動生成データ サンプルを用意するソリューションを楽しみにしています。

もちろん、IOC パターンを使用してこれらすべてを機能させることも検討しています。

興味深い記事としての Paul Stovell: http://www.paulstovell.com/blog/wpf-dependency-injection-in-xaml

だから私は、デザイン時にバインディングとモッキングオブジェクトのためのより価値のあるデザインタイムサポートを追加するためにいくつかのことを試みます.現在、ビュー(コード)とModelView( Xaml)、私はいくつかのシナリオを試しました:

解決策 1 : Generic を使用してビューを作成する

public class MyDotNetcomponent<T> : SomeDotNetcomponent 
{
    // Inversion of Control Loader…
    // Next step add the Inversion of control manager plus
    // some MockObject feature to work under design time
    public T View {Get;}
}

Blend はジェネリックをサポートしていないため、このソリューションは機能しません。デザイン サーフェスですが、Xaml にはいくつかあり、実行時にはうまく機能しますが、デザインでは機能しません。

解決策 2: ObjectDataProvider

<ObjectDataProvider ObjectType="{x:Type CP:IFooView}" />
<!-- Work in Blend -->
<!—- IOC Issue: we need to use a concrete type and/or static Method there no way to achive a load on demande feature in a easy way -->

解決策 3: ObjectDataProvider を継承する

<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView}" />
<!-- Cannot inherit from ObjectDataProvider to achive the right behavior everything is private-->

解決策 4: モック ObjectDataProvider をゼロからジョブに作成する

<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView }" />
<!-- Not working in Blend, quite obvious-->

解決策 5: マークアップ拡張機能を作成する (Paul Stovell)

<CWM:ServiceMarkup MetaView="{x:Type CP:IFooView}"/>
<!-- Not working in Blend -->

1点だけクリア。「blend で動作しない」と言ったのは、Binding ダイアログが使用できず、デザイナーが XAML を自分で手書きする必要があることを意味します。

次のステップは、時間をかけて Expression Blend のプラグインを作成できるかどうかを評価することです。

于 2008-11-13T01:28:29.637 に答える
3

Caliburnを確認する必要があります。これは、完全なDIをサポートする単純なWPF /SilverlightMVCフレームワークです。見た目はとてもかっこよく、好きなIoCコンテナを使用できます。ドキュメントウィキにはいくつかの例があります

于 2008-12-03T12:06:06.513 に答える
3

はい、常に行っています。ViewModel をコントロールの DataContext に「注入」できます。

実際、WPF は DI を使用するとさらに使いやすくなります。依存オブジェクトとプロパティでさえ、シームレスに動作します。

于 2008-11-12T19:03:44.937 に答える
1

Glen Block (上記を参照) は、一般的なアプローチは、ビューでビュー モデルを "解決" できる場所としてDataContextを使用するようにMVVMソリューションを設計することであると述べています。次に、expression blend 2008 のデザイン拡張機能を使用できます (これを利用するために、expression blend デザイン ツールを使用する必要がないことに注意してください)。例えば:

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" 
d:DataContext="{d:DesignInstance Type=local:MyViewModelMock, IsDesignTimeCreatable=True}"

ビューでは、 DataContextを期待する型にキャストするプロパティ ゲッターを使用できます(コード ビハインドで簡単に使用できるようにするためです)。

private IMyViewModel ViewModel { get { return (IMyViewModel) DataContext; } }

ビューのテストを容易にしたり、さまざまなランタイム実装を挿入したりできるように、インターフェイスを使用することを忘れないでください。

一般に、ソリューションのいたるところでコンテナから物事を解決するべきではありません。実際、すべてのコンストラクターでコンテナーを渡したり、グローバルにアクセスできるようにしたりすることは、悪い習慣と見なされています。(「Service Locator」戦略が「アンチパターン」を構成する理由についての議論を調べる必要があります)。

コンテナー (Prism Unity や MEF など) が解決できる明示的な依存関係を持つパブリック ビュー コンストラクターを作成します。

必要に応じて、内部のデフォルト コンストラクターを作成して、ビュー モデルのモック (または実際のモデル) を作成することもできます。これにより、この「デザイン コンストラクター」が外部 (「シェル」またはその他の場所)で不注意に使用されるのを防ぐことができます。テスト プロジェクトでは、" AssemblyInfo " の " InternalsVisibleToAttribute "を使用して、このようなコンストラクターを使用することもできます。ただし、もちろん、完全な依存関係コンストラクターを使用してモックを挿入できるため、通常は必要ありません。また、テストの大部分は最初にViewModelに焦点を当てる必要があるためです。ビュー内のすべてのコードは、理想的には非常に単純であるべきです。(View に多くのテストが必要な場合は、その理由を自問することをお勧めします!)

Glen は、 Views を View Modelsに、またはView Models を Viewsに注入できるとも述べています。すべてを分離するための完全に優れた手法があるため、私は後者を好みます (宣言的バインディング、コマンド、イベント集約、メディエーター パターンなどの使用)。ビュー モデルは、コア ビジネス ロジックを調整するためにすべての面倒な作業が行われる場所です。必要なすべての「バインディング」ポイントがView Modelによって提供される場合、ビューについて何も知る必要はありません(ほとんどの場合、XAML で宣言的に接続できます)

ビュー モデルをユーザー インタラクションのソースに依存しないようにすると、テストがはるかに簡単になります (できれば最初に)。また、任意のビュー (WPF、Silverlight、ASP.NET、コンソールなど) を簡単にプラグインできることも意味します。実際、適切なデカップリングが達成されたことを確認するために、"MVM" (Model-ViewModel) アーキテクチャがワークフロー サービスなどのコンテキストで機能するかどうかを自問することができます。考えてみると、ほとんどの単体テストはおそらくその前提で設計されているでしょう。

于 2013-10-29T16:37:29.113 に答える
0

View FirstまたはViewmodel Firstを決定する必要があると思います。他の答えが与えられた場合、それは決定できます..いくつかのオープンソースフレームワークが同じことを行います。ViewModel が最初に使用される Caliburn を使用し、その非常に優れたアプローチ

于 2011-03-14T08:43:26.807 に答える
0

IoC (Unity) をマークアップ拡張として使用することで、ViewModel が実行時に解決される非常に軽量なフレームワークを作成しました。

このフレームワークでは、コード ビハインドなしで XAML を記述できますが、コマンド、データ バインディング、およびイベント ハンドラーをルーティングすることもできます。

いずれにせよ、あなたのケースでは緩い XAML は必要ないと思いますが、コード ( http://xtrememvvm.codeplex.com ) を見ると、コードの一部を使用して、ビュー モデルとサービスを注入することで、独自の問題を解決します。

于 2013-03-07T17:11:28.077 に答える