2016-05-13 97 views
1

我試圖學習MVVM,但我遇到了一些麻煩。我新來xaml和c#。wpf應用程序mvvm如何正確構建

我到目前爲止有:

  • 一個person類,定義一個Person對象:姓名,年齡等信息

  • 一個模型類people,擁有私人鏈表(名單人),其中還包含方法如get,remove,add並做一些計算

  • viewmodel類,做投在代碼之後的xaml和模型之間進行/解析/轉換。

  • 代碼文件mainWindow.xaml.cs後面的xaml,它監聽按鈕點擊等,並調用viewModel類中的方法,並執行一些簡單的綁定,如total.Content = objModelView.getTotal()

我沒有使用INotifyPropertyChangedObservableCollection,仍然試圖環繞它我的頭。雖然我的程序做了我想要的,但我不確定如何更好地構建它。

基本上我有2個主要問題:

  1. 我在網上看到的例子,人們店/發起視圖模型的項目清單,我不應該保持模型的列表,而不是,這應該是所有的數據存儲的權利?
  2. 比方說,我假設將所有項目(在模型類的列表中)顯示在dataGrid上。現在在我的程序中:mainWindow.xaml.cs將檢測按鈕點擊,然後它要求viewModel將其存儲在模型中,如果沒有錯誤,那麼xaml後面的代碼將會執行 people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd });這是不好的做法嗎?不知道如何在這裏使用ObservableCollection,它可以以某種方式檢測我的模型類的列表中的更改,然後刪除並添加行到數據網格?

我一直在讀了整整一天,但我在這裏打,希望我能得到一些方向

+0

您需要使用INPC和ObservableCollection,否則它不是MVVM。 MVVM應該在模型中完成所有的操作,INPC/INCC是View如何知道數據已經改變,並且屏幕需要更新。這聽起來像你實際上正在編寫一個MVP應用程序。 – Aron

回答

2

模型存儲數據,視圖顯示它,視圖模型是兩者之間的橋樑。

這並不意味着視圖可以直接訪問模型數據,因爲您並不總是需要在模型圖層中顯示所有數據。所以我們有一個viewmodel層,它只能提供有用的信息。

當您希望多次顯示相同的數據但顯示不同時,viewmodel非常有用:您不必複製數據,只需要從這些數據中定義兩次需要的信息,以及如何顯示它們。

您在第二個問題中所做的是在模型中使用視圖:這不是MVVM。你想要做的是將Datagrid的ItemsSource DP綁定到從Person獲取信息的PersonVM列表。

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有一個'.CreateDerivedCollection()'擴展方法,可以爲你提供'PeopleVM'。然後'Fody.PropertyChanged'會爲你自動實現PropertyChanged。大量節省時間。 – Aron

1

我也是很新的WPF,C#和MVVM。這兩三個月我讀了相當多的一點點,所以也許我會分享我的理解。

  1. 你似乎有同樣的問題,我有一兩個星期前。數據不應該存儲在模型中。模型只是數據結構。數據庫(或模擬的替代品,如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}" .... > 

視圖模型:

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中使用依賴屬性。依賴屬性專門用於View類。他們所使用的部分內容是幫助XAML引擎綁定到屬性。 – Aron

+1

爲什麼會這樣?我一直在使用超過INotifyPropertyChanged的依賴屬性。但是,正確地說,視圖必須使用依賴屬性,因爲綁定目標必須是依賴屬性。就我所知,綁定可以在依賴屬性源上完成。除非使用這種方法有什麼不好的地方。 @Aron – Jai

+1

幾個原因。 a)DependencyObject是一個WPF類,在模型中使用它可以中斷POCO,並且消除了WPF/MVVM的關鍵優勢,它從視圖技術中解耦。這意味着測試更困難b)WPF團隊做了大量工作來確保WPF/Binding將INPC迴歸到UI線程(這是另一個有助於從UI技術中解耦出來,對CollectionViews感到羞恥的另一件事)。 DependencyProperties只能從UI線程訪問。 – Aron

2

對您的文章的回覆只要有人願意解釋,也許是一個完整冗長的博客本身。我會盡量在這裏回答2個具體問題。我不打算展示每個子答案的代碼,你必須把它當作家庭作業。 :)

我沒有使用INotifyPropertyChanged ObservableCollection,仍然試圖 包裹我的頭圍繞它。雖然我的程序做我想要的,但我不是 肯定如何更好地構建它。

爲什麼?如果你不使用這些魔術棒,最好你寫一個WinForms應用程序,而不是WPF。忘掉一切,並深入這兩個。你必須(不能逃避)理解並在MVVM/WPF中使用它們。你甚至可以推遲閱讀我對這個問題的進一步答案。

我在網上看到的例子,人們店/發起 視圖模型的項目清單,我不應該保持模型代替列表中,這應該是 ,所有的數據被保存嗎?

他們沒有錯。 Person模型層中的類代表一個真實世界的實體,但必須,但是我不會在模型中有People類。這只是ViewModel可以容易地容納的一個集合。我個人總是喜歡這種方式。

比方說,我假設要在dataGrid上顯示所有項目(在 模型類的列表中)。現在在我的程序中: mainWindow.xaml.cs將檢測到按鈕點擊,然後它詢問viewModel 將其存儲在模型中,如果沒有錯誤,則xaml後面的代碼將執行 people_dataGrid.Items.Add(new person {name = newName,age = newAge, address = newAdd});這是不好的做法嗎?不知道如何使用 ObservableCollection在這裏,它可以以某種方式檢測我的模型類的列表 中的更改,然後刪除行並將其添加到數據網格?

這不是MVVM,相信我。在最大程度上你需要在後面的視圖代碼中編寫代碼,正在初始化視圖模型並將其設置爲視圖的數據上下文。

要處理視圖事件(Button.Click對前)你應該使用ICommand執行將被綁定到XAML Button.Command財產。這樣你可以將控制的事件處理程序從後面的代碼中分離出來。

您需要在您的視圖模型中有一個ObservableCollection<Person>,該視圖將被視爲在DataGrid範圍內。所以當點擊一個按鈕添加人物時,按鈕的命令對象將更新這個集合,並且視圖將自動刷新而不需要手動添加到數據網格。

+0

感謝您的指導!我會重新工作,從零開始 – user308553

2

您沒有使用MVVM都:

你的代碼可以構造這樣。這聽起來像你正在使用MVP,這是一個完全不同的模式。

在繼續之前,您需要了解MVVM的設計目的,因爲它是一個非常複雜(似乎過度設計的模式),只有大量的抽象才能編寫無處不在的待辦事項列表。

但是你必須全部做它,否則它不是MVVM。

MVVM的禪

MVVM脫胎於,編寫好實現的,沒有錯誤的,安全的UI代碼是很難的。測試用戶界面代碼比較困難,涉及到招聘人類測試人員,這些測試人員很慢,可能會弄錯。

所以他們提出的解決方案很簡單。

不要寫任何代碼在UI

完成。

除外,不。現在,你的用戶界面不會做任何事情,它看起來很漂亮。所以他們在UI和程序/業務邏輯/ Model之間增加了一個額外的層,他們稱之爲ViewModel

ViewModel的工作是告訴UI該怎麼做。但訣竅是讓它告訴UI該做什麼,根本不知道UI。

(MVP也有類似的概念,但演示者瞭解UI)

通過具有ViewModel不知道的UI可言,我們可以編寫乾淨的代碼,可以方便的進行調試和測試,使用我們的平常的一竅門。比如單元測試,重構,靜態代碼分析,依賴注入等等......

時間太好了!

除了視圖模型仍然不知道用戶界面。所以我們知道UI應該是什麼樣子,但UI不知道,因爲沒有人告訴它...

因此,他們添加了Binding類。綁定類的工作是觀察ViewModel,然後在ViewModel發生更改時更新UI。

在MVVM的世界中,Binding類的工作方式有兩種不同的方法。

你有WPF的做事方式,即實現一個事件,告訴Binding類ViewModel已經更新。這是快速和靈活的,但真的很煩人寫。

而你有AngularJS的做事方式,即輪詢ViewModel進行更新。這是非常緩慢和越野車。

如果你到目前爲止一直關注我,你會注意到MVVM定義了從你的模型到你的視圖的數據流。在這個鏈條的任何部分中斷會使它「不起作用」。

這一切都如此複雜,爲什麼要麻煩?

我發現證明MVVM的過度複雜性的唯一原因是您可以編寫一個GUI,您可以擁有90%的測試覆蓋率,因爲該視圖僅涵蓋程序的一小部分。

如果您認爲自動化測試被高估了,那麼您不應該使用MVVM。

相關問題