2010-02-05 19 views
6

最近,我在查看模型(VM)中弄到了我的內褲。與「現場」收藏和特性同步的雙向視圖模型

就像this guy我得出結論,我需要在我的虛擬機上公開的集合通常包含與業務對象上公開的集合不同的類型。

因此,這兩種類型之間必須存在雙向映射或轉換。 (只是爲了讓事情複雜化,在我的項目中,這個數據是「Live」,這樣只要你改變一個屬性,它就會被傳送到其他計算機)

我可以應對這個概念,使用像Truss這樣的框架,儘管我懷疑在某個地方會有令人討厭的驚喜。

不僅必須轉換對象,還需要這兩個集合之間的同步。 (只是爲了使事情複雜化,我可以想到VM集合可能是業務對象集合的一個子集或並集,而不僅僅是1:1同步)的情況。

我可以看到如何使用複製ObservableCollection或類似CLINQ的方法進行單向「實時」同步。

問題就變成:創建/刪除項目的最佳方式是什麼?

雙直接同步似乎並不在卡上 - 我沒有找到這樣的例子,唯一支持遠程支持的類是ListCollectionView。雙向同步甚至會是一種合理的方式來添加回業務對象集合?

我所見過的所有樣本似乎都沒有解決任何「複雜」問題。

所以我的問題是:你如何解決這個問題?是否有一些技術來從虛擬機更新模型集合?什麼是最好的通用方法?

+0

你可以舉一個簡短的例子,哪些集合發生在哪裏?我想我理解你的問題,但不完全。如何使用業務邏輯集合,但在使用它們時使用轉換器?使用包含業務邏輯對象的代理對象集合怎麼樣? – Thorsten79 2010-02-05 13:40:14

回答

3

我也在努力通過MVVM與WPF一起使用兩個集合的雙向同步。我博客MVVM: To Wrap or Not to Wrap? How much should the ViewModel wrap the Model? (Part 1)MVVM: To Wrap or Not to Wrap? Should ViewModels wrap collections too? (Part 2)關於這個問題,包括一些示例代碼,顯示雙向同步。但是,正如這些帖子所指出的那樣,實施並不理想。我認爲它是一個概念證明。

我喜歡Alex_P發佈的框架BLINQCLINQObtics。這是一個非常好的方式來獲得同步的一方。也許另一方(從虛擬機到模型)可以通過備用路徑實現?我剛剛在我的博客上發佈了part 3,討論了其中的一些問題。

從我所看到的情況來看,在LINQ語句將數據投影到新結構的情況下,不支持雙向BLINQ和CLINQ。

但是,在LINQ查詢返回與底層集合相同的數據類型的情況下,它確實看起來像CLINQ可能支持雙向同步。這更多的是一種過濾方案,它與模型中包裝數據的ViewModel的用例不匹配。

+3

很高興看到我不是唯一一個摔角這個問題的人:)我得出的結論是,VM集合應該只讀,然後使用另一種機制,如命令來添加/刪除。我還沒有POC這個 – Schneider 2010-02-11 01:42:59

4

就我個人而言,我在我的模型和視圖模型中使用ObservableCollection。

class Model 
{ 
    public ObservableCollection<Foo> Foos; 
} 

class ViewModel 
{ 
    public Model Model; 
    public ObservableCollection<FooView> Foos; 

    public ViewModel() 
    { 
     Model.Foos.CollectionChanged += OnModelFoosCollection_CollectionChanged; 
    } 

    void OnModelFoosCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     Foo re; 

     switch (e.Action) 
     { 
      case NotifyCollectionChangedAction.Add: 
      re = e.NewItems[0] as Foo; 
      if (re != null) 
       AddFoo(re); //For other logic that may need to be applied 
      break; 
      case NotifyCollectionChangedAction.Remove: 
      re = e.OldItems[0] as Foo; 
      if (re != null) 
       RemoveFoo(re); 
      break; 
      case NotifyCollectionChangedAction.Reset: 
      Foos.Clear(); 
      /* I have an AddRange in an ObservableCollection-derived class 
       You could do Model.Foo.ForEach(ree => AddFoo(ree)); 
      */ 
      var converter = 
       from ree in Model.Foo 
       select new FooView(ree); 
      Reports.AddRange(converter); 
      break; 
      default: 
      //exercise for the reader :) 
      s_ILog.Error("OnModelFoosCollection_CollectionChangedDid not deal with " + e.Action.ToString()); 
      break; 
     } 
    } 

    void AddFoo(Foo f) 
    { 
     Foos.Add(new FooView(f)); 
    } 

    void RemoveFoo(Foo f) 
    { 
     var match = from f in Foos 
      where f.Model == f //if you have a unique id, that might be a faster comparison 
      select f; 
     if(match.Any()) 
      Foos.Remove(match.First()); 
    } 
} 

現在,當您從Model的Foo集合中刪除某些東西時,它會自動刪除相應的FooView。這與我對這種事情的想法是一致的。如果我想刪除某些內容,那麼該模型就是真正需要刪除的地方。

感覺就像很多代碼一樣,但實際上並沒有那麼多。我相信可以建立一個這樣的通用版本,但IMO總是希望自定義邏輯處理元素的添加/刪除。

+0

好吧,這是有道理的,是我來到的結論。我仍然希望看到更多的證據表明其他民族的做法給我信心,這是「最好的方式」。例如,我從來沒有在示例或博客文章中看到過這種方法: -/ – Schneider 2010-02-05 23:08:37

+1

如果該模型只公開IList或什麼?將程序中的每個列表變成ObservableCollection都不是解決方案。如果你的模型沒有實現INotifyPropertyChanged會怎麼樣?在ViewModel中監聽ObservableCollection,然後將更改推回到模型可以正常工作,但您不能指望模型爲您提供這樣的便利,並且在很多情況下,ObservableCollection可能無法在您的模型中提供所需的功能。 – 2010-02-10 02:30:28

+0

我傾向於做更多Sacha Barber風格的MVVM。我正在編寫新的代碼,我沒有遺留模型,所以我甚至很少有模型。一般來說,如果我需要INPC,我寫它。如果我需要ObservableCollection,我使用它。 – Thomas 2010-02-10 16:57:40

3

唯一的情況是,當您需要雙向同步時,您用來顯示虛擬機集合的控件不會讓您知道用戶創建或刪除項目的意圖。即該控件直接處理您的虛擬機集合,並且您知道該項目已被添加/刪除的唯一方式是通過監視虛擬機的集合。如果情況並非如此,那麼您可以實現單向同步,並直接在模型集合上添加/刪除項目。

編輯:就拿勢必ItemViewModels的觀察到的集合 例如WPF的DataGrid 控制。如果其 CanUserAddRows屬性設置爲true 和用戶開始在底部的 空行中鍵入,DataGrid中 將使用您 ItemViewModel的默認構造函數來創建一個鬆散的項目 ,然後將其添加到 集合。沒有跡象 來自DG,它想要添加一個項目 collection.c 我想不出任何 其他控制是複雜的 足以能夠添加項目到 自己收集。
相反 的情況是,當你擁有的ListView 綁定到您的收藏和指示用戶的意圖 添加新項的命令 - 然後在命令處理程序 你只是新的項目添加到DataModel的 ,讓單向同步做其餘的 工作。在這種情況下,ListView不是 能夠添加到集合 禮物。

至於同步過程本身,請看Bindable LINQ項目 - 它可以最小化代碼量並提高可讀性。例如湯姆發佈的代碼將轉化爲這樣的事情:

class ViewModel 
{ 
    public Model Model; 
    public ObservableCollection<FooView> Foos; 
    public ViewModel() 
    { 
    Foos = from foo in Model.Foos.AsBindable() 
      select new FooView(foo); 
    } 
} 

編輯2:使用B-LINQ了一段時間後,現在我應該說,你可能有它的性能問題。我曾用它來同步相對較大的集合(數百個元素)集合,並且每秒都會添加和刪除幾十個元素,我必須放棄並按照Tom的建議實現同步。
儘管在項目中那些集合很小且性能不成問題的部分,我仍然使用B-LINQ。

+0

你能給出這樣的控件的例子嗎/不通知? – Schneider 2010-02-11 22:30:30

+1

在帖子中查看修改。 – 2010-02-15 13:47:45

1

我已經編寫了一些幫助類,用於在業務對象的視圖模型對象here中包裝可觀察的業務對象集合,也許它應該擴展到其他方式。一直在尋找貢獻......

1

我提出了一個基於MVVM的通用撤銷/重做框架,它使用了與您描述的問題相關的一些技術。它使用實現此接口的集合:

public interface MirrorCollectionConversor<V, D> 
{ 
    V GetViewItem(D modelItem, int index); 
    D GetModelItem(V viewItem, int index); 
} 

(V是視圖模型項目,d爲模型項目)

使用這個接口,它會自動同步視圖模型收集的模型集合發生更改時。如果更改視圖模型,則更改將簡單重定向到模型集合。

GetViewItem函數爲視圖模型對象與模型對象的關聯提供了一些靈活性。

你可以找到詳細信息here

(我承認施工非常複雜,我會很樂意聽取建議)。