2010-05-25 24 views
1

域模型集合(通常是List或IEnumerable)是委託到ViewModel。WPF/MVVM:將域模型集合委託給ViewModel

這意味着我的CustomerViewModel有一個List或IEnumerable類型的訂單集合。

綁定控件無法識別列表中的變化。但與ObservableCollection是。

這是MVVM設計模式中的問題。

你如何應對?

UPDATE:我是怎麼做的樣品:

public class SchoolclassViewModel : ViewModelBase 
{ 
    private Schoolclass _schoolclass; 
    private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();   

    public SchoolclassViewModel(Schoolclass schoolclass) 
    { 
     _schoolclass = schoolclass; 
     _schoolclass.Pupils = new List<Pupil>(); 

     foreach (var p in schoolclass.Pupils)   
      Pupils.Add(new PupilViewModel(p));    
    } 

    public Schoolclass GetSchoolclass 
    { 
     get { return _schoolclass; } 
    } 

    public int ID { get; set; }  

    public string SchoolclassName 
    { 
     get { return _schoolclass.SchoolclassName;} 
     set 
     { 
      if(_schoolclass.SchoolclassName != value) 
      {      
       _schoolclass.SchoolclassName = value; 
       this.RaisePropertyChanged("SchoolclassName"); 
      } 

     } 
    } 

    public ObservableCollection<PupilViewModel> Pupils 
    { 
     get{ return _pupils;} 
     set 
     { 
      _pupils = value; 
      this.RaisePropertyChanged("Pupils"); 
     } 
    } 
} 
+2

這是MVVM設計模式的問題嗎?列表中的更改僅在可觀察集合時才被識別。這是所有的編程,而不僅僅是MVVM。 – 2010-05-25 17:34:17

+1

這不是一個普遍問題,因爲只有在MVVM設計模式下,才能將模型委託給視圖模型。 域模型/集合必須獨立於ViewModel需求開發,這些需求在數據綁定中具有ObservableCollection用於更改通知。 它關於ViewModel(通知何時集合更改什麼是UI需求)和Model(根本沒有更改通知,因爲該模型未綁定到View!)之間的關注分離 – msfanboy 2010-05-25 18:10:05

+0

您可以將ObservableCollection 作爲IEnumerable ,並且只要基礎類型是ObservableCollection 綁定仍然按預期工作,對不對? (我認爲這是對的,但我現在不記得)。 – ckramer 2010-05-25 19:51:58

回答

0

好吧,我會繼續前進,添加我的想法作爲答案,而不是在評論中。 :)

我認爲底線是,這只是WPF和數據綁定工作方式的現實。爲了使雙向數據綁定操作,集合需要一種通知綁定到它們的控件的方法,並且大多數域對象中使用的標準列表和集合不會/不會/不應該支持這種方式。正如我在評論中提到的那樣,被要求爲非集合屬性實現INotifyPropertyChanged是標準域對象可能無法滿足的另一個要求。

域對象並不打算成爲視圖模型,因此您可能會發現需要在兩種類型的對象之間來回映射。這與在域對象和數據訪問對象之間來回映射並不矛盾。每種類型的對象在系統中都有不同的功能,並且每種類型的對象都應該專門用來支持他們在系統中的角色。所有這一切,Agies的使用AOP自動生成代理類的想法非常有趣,而且我打算研究一些東西。

+1

aren't這些代理類只是域模型的克隆?像ViewModel大多數時候是一個域模型的克隆!? – msfanboy 2010-05-28 11:36:40

+1

您可能會說「表示」或「投影」而不是「克隆」,並且正是出於上述原因。視圖模型解決了與域對象不同的問題,就像域對象解決了與DAO不同的問題。在某些情況下,如果跨層的需求確實相似,則可能使用相同的對象。例如,你可能讓你的域對象實現INotifyPropertyChanged。完全支持列表中的雙向數據綁定可能會更具挑戰性(同樣,您可能會看AOP)。 – 2010-05-28 13:54:17

+0

well atm我有3個聚合域模型。他們都支持2way數據綁定,PropertyChanges和CollectionChanges。 現在需要ViewModels,因爲它們是模型的純副本,根本沒有意義! – msfanboy 2010-06-08 18:31:15

3

我不這樣做就你描述的方式解決這個問題。

如果我需要呈現Foo對象並在視圖及其相關Bar目的,FooViewModel將通常實施ObservableCollection<BarViewModel>類型的Bars屬性。

請注意,這與潛在的Foo類是否具有IEnumerable<Bar>類型的Bars屬性無關。 Foo類可能不會。除UI之外,應用程序可能甚至不需要遍歷Foo的所有Bar對象。

編輯

當我的觀點是應用程序的對象模型的簡單表示,我爲您的樣品做很多做的事情。在我的構造函數中的代碼通常會更緊湊一點:

_Bars = new ObservableCollection<BarViewModel>(
    _Foo.Bars.Select(x => new BarViewModel(x))); 

但它本質上是相同的東西。

但是這裏假設Foo實際上暴露了Bars屬性。它可能不會。或者也許只有一些Bar對象應該出現在視圖中。或者也許它們應該與其他物體混合在一起,並且FooViewModel應該公開某種CompositeCollection

我要說的是,視圖模型是視圖的模型。這不一定與底層對象模型有直接的對應關係。

挑選一個簡單的例子:我的程序可能會給用戶一種將物品拖放到五個不同的類別中的方法,將它們拖放到五個不同的ListBox控件中。最終,這樣做會在Item對象上設置Category屬性。我的視圖模型將包含一個CategoryViewModel對象的集合,每個對象都具有ObservableCollection<ItemViewModel>類型的屬性,以便在集合之間來回拖動項目很容易實現。

的事情是,有可能連一個Category類應用程序的對象模型,更不用說Category對象的集合。 Item.Category可能只是string類型的財產。 CategoryViewModel不鏡像應用程序的對象模型。它只存在於UI中支持視圖。

+0

@Robert 您的評論聽起來很有趣和困惑,我同一時間。 我已經用代碼示例更新了最初的問題,我是如何做到的。可以 你描述你的方式與我的示例不知何故?我不明白你所描述的/ 一切: – msfanboy 2010-05-25 20:19:34

+2

@Robert:我想你確認他的投訴,這是每次要使用一個集合從您的視圖模型您的域模型,你必須投影到一個ObservableCollection(如果你想做一些保存操作的話,可以將它投影回來)。 – 2010-05-25 22:26:06

+1

當然。但抱怨是真的,「我想提供一個不平凡的用戶界面,而我的域模型不實現屬性更改通知和值轉換和命令。」如果你想在你的領域模型中實現這些東西,你可以,然後將其重構爲視圖模型,一旦你意識到將視圖耦合到領域模型會導致許多你不想擁有的問題。創建視圖模型只是預先進行重構。 – 2010-05-26 00:15:54

0

我所做的是在我的領域模型中使用ObservableCollection而不是使用我自己的集合類型,該類型在其他有用的標準和自定義接口中實現了INotifyCollectionChanged接口。我的想法與Rockford Lhotka在他的書中提到的很相似,即更改通知不僅僅適用於表示層,因爲域狀態層中的其他業務對象在另一個對象狀態發生更改時通常需要某種通知。

使用此方法學,您可以創建自己的集合類型,該集合類型仍具有更改通知以及您需要的任何自定義行爲的好處。您集合的基類可以實現爲純粹的基礎結構代碼,然後可以創建一個子類,該子類可以使用this book中使用的子類型分層技術來包含業務邏輯。所以最後你可以有一個集合,它可以包裝IEnumerable類型並且提供你想要的更改通知以及你的域模型和表示代碼。