2016-05-13 53 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。

相关问题