我正在尝试学习MVVM,但我遇到了一些麻烦。我是xaml和c#的新手。
到目前为止我所拥有的:
定义人物对象的person
类:姓名,年龄和其他信息
模型类people
拥有私人链接列表(人员列表),其中还包含get
,remove
,add
等方法一些计算
一个viewmodel类,在代码和模型后面的xaml之间进行转换/解析/转换。
代码文件mainWindow.xaml.cs
后面的xaml,它监听按钮点击等,并从viewModel类调用方法,并执行一些简单的绑定,例如total.Content = objModelView.getTotal()
。
我没有使用INotifyPropertyChanged
ObservableCollection
,仍然试图绕过它。虽然我的程序做了我想要的,但我不确定如何更好地构建它。
基本上我有两个主要问题:
people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd });
这是不好的做法吗?不知道如何在这里使用ObservableCollection,它能以某种方式检测我的模型类列表中的更改,然后删除并将行添加到datagrid?我一整天都在读书,但我很震惊,希望我能指点方向
答案 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的新手。这两到三个月我读了很多,所以也许我会分享我的理解。
你似乎和我一两周前有过同样的问题。数据不应存储在模型中。模型只是数据结构。数据库(或模拟的备选方案,如List
)是存储这些数据的实际存储。有些人只是将它们保存在ViewModel中,而有些人会将它们移到MVVM之外的某些东西(例如&#34; Repository&#34;类)。
你做错了。在MVVM中,Views不以这种方式与ViewModels交互 - 它们通过Command
和Bindings进行交互。您的View直接操作列表这一事实意味着它肯定是错误的。
示例:
查看(窗口):
<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
的任何内容,所以我建议先阅读它。这里给出一个教程太长了,但是在你读完一些教程之后你可以提问。