wpf应用程序mvvm如何正确构造

时间:2016-05-13 05:46:14

标签: c# wpf mvvm

我正在尝试学习MVVM,但我遇到了一些麻烦。我是xaml和c#的新手。

到目前为止我所拥有的:

  • 定义人物对象的person类:姓名,年龄和其他信息

  • 模型类people拥有私人链接列表(人员列表),其中还包含getremoveadd等方法一些计算

  • 一个viewmodel类,在代码和模型后面的xaml之间进行转换/解析/转换。

  • 代码文件mainWindow.xaml.cs后面的xaml,它监听按钮点击等,并从viewModel类调用方法,并执行一些简单的绑定,例如total.Content = objModelView.getTotal()

我没有使用INotifyPropertyChanged ObservableCollection,仍然试图绕过它。虽然我的程序做了我想要的,但我不确定如何更好地构建它。

基本上我有两个主要问题:

  1. 我在网上看到人们在viewmodel中存储/启动项目列表的示例,我不应该将列表保留在模型中,而应该将所有数据存储在哪里?
  2. 假设我想将所有项目(在模型类的列表中)显示在dataGrid上。现在在我的程序中:mainWindow.xaml.cs将检测按钮单击,然后它要求viewModel将其存储在模型中,如果没有错误那么xaml后面的代码会做 people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd });这是不好的做法吗?不知道如何在这里使用ObservableCollection,它能以某种方式检测我的模型类列表中的更改,然后删除并将行添加到datagrid?
  3. 我一整天都在读书,但我很震惊,希望我能指点方向

4 个答案:

答案 0 :(得分:2)

只要有人希望解释,你的帖子就可以解答,也许是一个漫长的博客本身。我将尝试在这里回答您的两个具体问题。我不会为每个子答案显示代码,你必须把它作为家庭工作。 :)

  

我没有使用INotifyPropertyChanged ObservableCollection,仍在尝试   把头包住。虽然我的计划做了我想要的,但事实并非如此   确定如何更好地构建它。

为什么呢?如果你不使用这些魔术棒,你最好编写一个WinForms应用程序而不是WPF应用程序。忘记一切,深入研究这两个。您必须(无法逃避)理解并在MVVM / WPF中使用它们。你甚至可以推迟阅读我的进一步答案。

  

我在线查看人们存储/启动项目列表的示例   viewmodel,我不应该把列表保留在模型中,应该是   所有数据都存储在哪里?

他们没有错。模型层中的Person类代表一个真实世界的实体,但是,我不必在模型中使用People类。它只是ViewModel可以轻松容纳的集合。我个人总是喜欢这样。

  

假设我想显示所有项目(在列表中)   模型类)到dataGrid上。现在在我的计划中:   mainWindow.xaml.cs将检测按钮单击,然后询问viewModel   将它存储在模型中,如果没有错误,那么代码后面的xaml就可以了   people_dataGrid.Items.Add(新人{name = newName,age = newAge,   address = newAdd});这是不好的做法吗?不知道如何使用   ObservableCollection在这里,可以以某种方式检测列表中的更改   我的模型类,然后删除并添加行到datagrid?

那不是MVVM,相信我。最大限度地要求您在后面的视图代码中编写,初始化视图模型并将其设置为视图的数据上下文。

要处理视图事件(例如Button.Click),您应该使用将绑定到XAML中的ICommand属性的Button.Command实现。这样您就可以将控件的事件处理程序与后面的代码分离。

您的viewmodel中需要ObservableCollection<Person>,它将绑定DataGrid视图。因此,当单击按钮添加人员时,按钮的命令对象将更新此集合,并且视图将自动刷新,而无需您手动将其添加到数据网格。

答案 1 :(得分:2)

模型存储数据,视图显示它,viewmodel是两者之间的桥梁。

这并不意味着视图可以直接访问模型数据,因为您并不总是需要在模型层中显示所有数据。所以我们有一个viewmodel层,它只能访问有用的信息。

当您希望多次显示相同数据但显示方式不同时,viewmodel非常有用:您不必复制数据,只需要从这些数据中定义两次所需信息以及如何显示这些信息。

你在第二个问题中正在做的是在视图中使用模型:这不是MVVM。你想要做的是将Datagrid的ItemsSource DP绑定到PersonVM列表,该列表将从Person获取信息。

您的代码可以像这样结构化:

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>

答案 2 :(得分:2)

你根本就没有使用MVVM。听起来你正在使用MVP,这是一种完全不同的模式。

在继续之前,你需要了解MVVM的设计目的,因为它是一个非常复杂的(看起来过于工程化的模式),有大量的抽象,只是为了编写无处不在的To-Do列表。

但是你必须做所有这些,否则它不是MVVM。

MVVM的禅宗

MVVM源于认识到编写好的,无错误的,安全的UI代码很难实现。测试UI代码更难,并且涉及雇用人类测试人员,这很慢并且可能会出错。

所以他们提出的解决方案很简单。

不要在您的用户界面中编写任何代码

完成。

除外,不是。现在,你的UI没有做任何事情,它看起来很漂亮。因此,他们在用户界面和计划/业务逻辑/ Model之间添加了一个额外的图层,他们称之为ViewModel

ViewModel的工作是告诉用户界面要做什么。但诀窍是让它告诉用户界面该怎么做,而ViewModel根本不知道用户界面。

(MVP有类似的概念,但Presenter知道UI)

ViewModel根本不了解UI,我们可以编写干净的代码,可以使用我们常用的技巧轻松调试和测试。如单元测试,重构,静态代码分析,依赖注入等等......

时间很好!

除了视图模型仍然不知道UI。所以我们知道UI应该是什么样子,但UI不知道,因为没有人告诉它......

所以他们添加了Binding类。绑定类的工作是观察ViewModel,然后在ViewModel中发生更改时更新UI。

在MVVM的世界中,有两种不同的方法来处理Binding类。

你有WPF的做事方式,即实现一个事件,告诉Binding类ViewModel已经更新。这是快速而灵活的,但写起来真的很烦人。

你有AngularJS的做事方式,即轮询ViewModel以获取更新。这是非常缓慢和错误。

如果您到目前为止一直关注我,您会注意到MVVM定义了从您的模型到View的数据流。在这个链条的任何部分中断会使它“无法正常工作”。

这一切都很复杂,为什么要这么麻烦?

我发现证明MVVM过于复杂的唯一原因是你可以编写一个可以有90%测试覆盖率的GUI,因为视图只覆盖了程序的一小部分。

如果您认为自动化测试被高估了,那么您就不应该使用MVVM。

答案 3 :(得分:1)

我也是WPF,C#和MVVM的新手。这两到三个月我读了很多,所以也许我会分享我的理解。

  1. 你似乎和我一两周前有过同样的问题。数据不应存储在模型中。模型只是数据结构。数据库(或模拟的备选方案,如List)是存储这些数据的实际存储。有些人只是将它们保存在ViewModel中,而有些人会将它们移到MVVM之外的某些东西(例如&#34; Repository&#34;类)。

  2. 你做错了。在MVVM中,Views不以这种方式与ViewModels交互 - 它们通过Command和Bindings进行交互。您的View直接操作列表这一事实意味着它肯定是错误的。

  3. 示例:

    查看(窗口):

    <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的任何内容,所以我建议先阅读它。这里给出一个教程太长了,但是在你读完一些教程之后你可以提问。