2016-05-13 6 views
1

私はMVVMを学ぼうとしていますが、何か問題があります。私はxamlとc#を初めて使っています。人物オブジェクトを定義wpf application mvvm正しく構造化する方法

  • personクラス、::名前、年齢、その他の情報

  • モデルクラスpeopleは、プライベートのLinkedList(リストを所有している私がこれまで持っているもの

    また、get,,addなどのメソッドを含んでおり、いくつかの計算を行います。

  • viewmodelクラス、キャストしていますxamlの背後にあるコードとモデルの間の変換/解析/変換

  • xamlの背後にあるコードファイルmainWindow.xaml.csは、ボタンのクリックなどを聞き、viewModelクラスからメソッドを呼び出し、total.Content = objModelView.getTotal()のような簡単なバインディングを行います。

私はまだそれのまわりで私の頭をラップしようとしている、INotifyPropertyChangedObservableCollectionを使用していませんでし。私のプログラムは私が望むことをしているが、私はそれをより良く構築する方法がわからない。

は基本的に私は2つの主要な質問がある:

人々は/はのviewmodel内の項目のリストを開始ストアところ、私はオンラインの例を参照してください
  1. 、私はそれがどこである必要があり、代わりに、モデル内のリストを維持するべきではありませんすべてデータは正しく格納されますか?
  2. 私は、(モデルクラスのリスト内の)すべてのアイテムをdataGridに表示するとします。今すぐ私のプログラムで:mainWindow.xaml.csはボタンのクリックを検出し、それからモデルに格納するようにviewModelに依頼します。エラーがなければxamlはコードの後ろで行います people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd });これは悪い習慣ですか?いけないObservableCollectionをここで使用する方法を取得、それは何とか私のモデルクラスのリストの変更を検出し、削除し、DataGridに行を追加することができますか?

私は一日のために読んでいるが、私はここで打ったんだ、私はまた、WPF、C#とMVVMにはかなり新しいです、私はいくつかの方向

+0

INPCとObservableCollectionを使用する必要があります。それ以外の場合はMVVMではありません。 MVVMはモデル内で行われたすべてのアクションを実行する必要があり、INPC/INCCは、Viewがデータが変更されたことを知り、画面を更新する必要がある方法です。実際にMVPアプリケーションを作成しているようです。 – Aron

答えて

2

モデルはデータを格納し、ビューはそれを表示し、viewmodelは2つの間のブリッジです。

モデルレイヤーにすべてのデータを表示する必要はないため、モデルデータへの直接アクセスがビューにあるわけではありません。したがって、有用な情報だけにアクセス可能なビューモデルレイヤーがあります。

viewmodelは、同じデータを複数回表示して別々に表示したい場合に非常に便利です。データを複製する必要はなく、データから必要な情報とその表示方法を2回定義するだけです。

2番目の質問では、モデルをビューで使用しています:これはMVVMではありません。あなたがしたいのは、DatagridのItemsSource DPを、Personから情報をフェッチするPersonVMのリストにバインドすることです。

あなたのコードは、そのように構成することができます:あなたがすべてでMVVMを使用していない

public class Person { 
    public String Name {get; set;} 
    public int Age {get; set;} 
} 

public class PersonVM { 
    public PersonVM(Person model) { 
     _model = model; 
    } 

    private readonly Person _model; 
    internal Person Model {get {return _model;}} 

    public String Name { 
     get { return _model.Name; } 
     set { _model.Name = value; } 
    } 
    public int Age { 
     get {return _model.Age;} 
     set { _model.Name = value; } 
    } 
} 

//PersonV.xaml 

<StackPanel> 
    <TextBlock Text="{Binding Name}"/> 
    <TextBlock Text="{Binding Age}"/> 
</StackPanel> 


public class People : ObservableCollection<Person> { 

} 

public class PeopleVM : ObservableCollection<PersonVM> { 

    public PeopleVM(People model) { 
     _model = model; 
     foreach(Person p in _model) { 
      Add(new PersonVM(p)); 
     } 
     _model.CollectionChanged += CollectionChangedHandler; 
    } 

    private void CollectionChangedHandler(Object sender, NotifyCollectionChangedEventArgs args) { 
     switch (notifyCollectionChangedEventArgs.Action) { 
      case NotifyCollectionChangedAction.Add: 
       foreach(People p in args.NewItems) { 
        if(!this.Any(pvm => pvm.Model == p)) { 
         this.Add(new PersonVM(p)); 
        } 
       } 

       break; 
      case NotifyCollectionChangedAction.Remove: 
       foreach(People p in args.OldItems) { 
        PersonVM pvm = this.FirstOrDefault(pe => pe.Model == p); 
        if(pvm != null) this.Remove(pvm); 
       } 
       break; 
      case NotifyCollectionChangedAction.Reset: 
       Clear(); 
       break; 
      default: 
       break; 
      } 
    } 

    private readonly People _model; 
} 

//PeopleV.xaml 
<ItemsControl ItemsSource={Binding}> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate DataType="{x:Type PersonVM}"> 
      <PersonV/> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

public class AppVM { 
    public AppVM() { 
     People p = ServiceLayer.LoadPeople(); //load people 
     People = new PeopleVM(p); 
    } 

    public PeopleVM People {get; set;}; 
} 

//MainWindow.xaml 
<Window ... 
    > 
    <Window.DataContext> 
     <local:AppVM/> 
    </Window.DataContext> 
    <PeopleV/> 
</Window> 
+0

こんにちはnkoniishvt、素晴らしい答え。しかし、ReactiveUIのようないくつかのライブラリで置き換えることができるボイラープレートがたくさんあります。 – Aron

+0

@Aronありがとう!私はこの図書館を見る必要があります、私は標準的な方法を使用するにはあまりにも多くを試してください。 – nkoniishvt

+0

ReactiveUIには 'PeopleVM'を実行する' .CreateDerivedCollection() '拡張メソッドがあります。それで 'Fody.PropertyChanged'は自動的にPropertyChangedを実装します。大幅に時間を節約できます。 – Aron

1

を得ることができると思います。私はこれらの2〜3ヶ月間かなりのビットを読んだので、多分私が理解したことを分かち合うでしょう。

  1. あなたは1週間前または2週間前と同じ質問をしているようです。データをモデルに保存しないでください。モデルは単なるデータ構造です。データベース(またはListのようなシミュレートされた代替案)は、これらのデータを格納する実際の記憶域です。 ViewModelに保持する人もあれば、MVVMの外部のものに移動させる人もいます(例えば、 "Repository"クラス)。

  2. あなたはすべて間違っています。 MVVMでは、ビューはこのようにViewModelと対話しません。つまり、CommandとBindingsを介して対話します。ビューが直接リストを操作しているということは、間違っていることを意味します。

例:

ビュー(ウィンドウ):

<Window.Resources> 
    <vm:MyViewModel x:Key="MyVM" /> 
</Window.Resources> 

<Window.DataContext> 
    <StaticResourceExtension ResourceKey="MyVM" /> 
</Window.DataContext> 

<DataGrid ItemsSource="{Binding PeopleList}" ..... > 
<Button Command="{Binding StoreCommand}" .... > 

のViewModel:リストに保たれている氏名/年齢であなたのモデルクラスは

public static readonly DependencyProperty PeopleListProperty = 
    DependencyProperty.Register("PeopleList", 
    typeof(ObservableCollection<Person>), 
    typeof(ViewModel)); 

public ObservableCollection<Person> PeopleList 
{ 
    get 
    { 
     return GetValue(PeopleListProperty) as ObservableCollection<EmissionEntry>; 
    } 
    set 
    { 
     SetValue(PeopleListProperty, value); 
    } 
} 

private ICommand _storeCommand; 
public ICommand StoreCommand 
{ 
    get 
    { 
     if (_storeCommand == null) 
      _storeCommand = new MyCommandImplementation(); 
     return _storeCommand; 
    } 
} 

Personですリポジトリをどこかに置いておきたい場合を除き、ViewModel。

おそらくICommandについては何も読んでいない可能性がありますので、まずそれを読むことをお勧めします。ここでチュートリアルをするのは時間がかかりすぎますが、読んだ後で質問することができます。

+1

ここの情報のほとんどは正しいです。ただし、Model/ViewModelでDependency Propertiesを使用することは絶対に避けてください。 Dependency PropertiesはViewクラスでのみ使用されます。それらが使用されている部分は、XAMLエンジンがプロパティにバインドするのを支援することです。 – Aron

+1

それはなぜそうでしょうか?私はINotifyPropertyChanged以上の依存関係プロパティを使用しています。ただし、バインディングターゲットは依存プロパティである必要があるため、ビューは依存プロパティを使用する必要があります。私が知る限り、依存関係プロパティーのソースでバインディングを行うことができます。このアプローチを使用するのに悪いことがない限り。 @Aron – Jai

+1

いくつかの理由があります。 a)DependencyObjectはWPFクラスであり、モデルでそれを使用するとPOCOが破棄され、ビュー技術から切り離されたWPF/MVVMの主な利点が削除されます。 WPFチームは、WPF/BindingがINPCをUIスレッドに戻すことを確実にするために多くの作業をしました(これは、UI技術とのデカップリングに役立つ別のもので、CollectionViewについては恥ずかしいものです)。 DependencyPropertiesは、UIスレッドからのみアクセスできます。 – Aron

2

あなたの記事への回答は、説明したいと思っている限り、おそらく全体の長いブログそのものである可能性があります。私はここであなたの特定の質問のうち2つに答えようとします。私は、各サブアンサーのコードを表示するつもりはないので、あなたはそれを家庭の仕事として取る必要があります。 :)

私はまだそれのまわりで私の頭をラップする をしようと、INotifyPropertyChangedののObservableCollectionを使用してdidntの。私のプログラムは私がしたいことをしている間は、私は それをより良く構造化する方法はありません。

なぜですか?これらのマジックスティックを使用しない場合は、WinFormsアプリケーションを作成してWPFを作成しない方がよいでしょう。すべてを忘れて、この2つに潜ってください。 MVVM/WPFで理解して使用する必要があります(エスケープしないでください)。私のさらなる答えを読むことを延期することさえできます。

私は人々ストア/は のviewmodel内の項目のリストを開始するオンライン例を参照してください、私はすべてのデータが右保存する する必要があることを、代わりにモデルにリストを保つべきではないのですか?

間違っていません。 Personモデルレイヤのクラスは現実の世界のエンティティを表していますが、モデル内でPeopleクラスを持つことについては気にしません。これはViewModelで簡単に対応できるコレクションです。私は個人的に常にその方法を好むだろう。

ここでは、すべてのアイテム( モデルクラスのリスト内)をdataGridに表示するとします。今すぐ私のプログラムで: mainWindow.xaml.csはボタンのクリックを検出し、それからviewModel にそれをモデルに保存するように要求します。エラーがなければxamlの後ろにコードがあります people_dataGrid.Items.Add(new person {name = newName、age = newAge、 アドレス= newAdd});これは悪い習慣ですか?どのように ObservableCollectionを使用するには、私のモデルクラスのリスト の変更を検出し、その後削除し、DataGridに行を追加することができますか?

これはMVVMではなく、私を信頼してください。ビューコードで書き込む必要がある最大限のものは、ビューモデルを初期化し、ビューのデータコンテキストとして設定することです。

ビューイベント(例:Button.Click)を処理するには、XAMLのButton.CommandプロパティにバインドされるICommandの実装を使用する必要があります。これにより、コントロールのイベントハンドラをコードの背後から切り離すことができます。

DataGridにバインドされるビューモデルにObservableCollection<Person>が必要です。したがって、ボタンをクリックして人物を追加すると、ボタンのコマンドオブジェクトによってこのコレクションが更新され、手動でデータグリッドに追加することなく、ビューが自動的に更新されます。

+0

方向をありがとう!私は土台からすべてを働かせます – user308553

2

MVPを使用しているようですが、これはまったく異なるパターンです。

MVVMが設計されたことを理解する必要があります。なぜなら、非常に複雑な(設計されたパターンよりもはっきりと見える)ために、遍在するTo-Doリストを作成するだけです。

ただし、すべてを実行する必要があります。そうでない場合は、MVVMを実行する必要があります。

MVVMの禅

MVVMは良い、バグのない、安全なUIのコードを書くことが困難であるという認識から生まれました。 UIコードのテストは難しく、人間のテスターを雇うことが必要で、遅くて間違っている可能性があります。

だから、彼らが思いついた解決策は簡単でした。

はあなたのUIで行わ

をすべてのコードを記述しないでください。

を除きます。今、あなたのUIは何もしません、それはちょうどかなり見えます。そこで彼らは、UIとProgram/Business Logic/Modelの間に余分なレイヤーを追加し、それをViewModelと呼びました。

ViewModelの仕事は、UIに何をすべきかを伝えることでした。しかし、このトリックは、UIについて何も知らずに、UIに何をすべきかを伝えることであった。ViewModel

(MVPは、同様の概念を持っていますが、プレゼンターはUIについて知っDOES)

ViewModelがまったくUIを知っていない、我々は簡単にデバッグと私たちを使用して試験することができるクリーンなコードを書くことができたことでは通常のトリックの袋。ユニットテスト、リファクタリング、静的コード解析、依存性注入などなど。

時は良いです!

ビューモデルを除き、まだUIについてはわかりません。だから、私たちはUIの外観を知っていますが、UIは知らないので、誰もそれを伝えていないので...

そこで彼らはBindingクラスを追加しました。バインディングクラスの仕事は、ViewModelを監視し、ViewModelで何か変更があるたびにUIを更新することです。

MVVMの世界では、Bindingクラスの仕組みには2つの異なるアプローチがあります。

あなたは、ViewModelが更新されたことをBindingクラスに伝えるイベントを実装するためのWPFのやり方を持っています。これは速くて柔軟ですが、本当に書くのは面倒です。

あなたは更新のためにViewModelをポーリングするためにAngularJSのやり方をしています。これはばかげて遅くてバグです。

これまで私に従っていれば、MVVMはモデルからビューへのデータの流れを定義していることに気づくでしょう。このチェーンのどの部分でのブレークは、それが「うまくいかない」ようにします。

これですべてが完成しました。なぜ気になるのですか?

MVVMの複雑さを正当化する唯一の理由は、ビューがプログラムのごく一部をカバーするため、90%のテストカバレッジを持つことができるGUIを作成できることです。

自動テストが過大評価されていると思われる場合は、MVVMを使用しないでください。

関連する問題