最简单的 MVVM






4.66/5 (30投票s)
用通俗的语言介绍主要构建块,以便初学者更容易理解。
背景
接续我上一篇关于 MVVM 的原因和时机 的文章,这次,我将介绍如何创建一个复杂性最小的简单 MVVM 应用程序。我写这篇文章是为了那些想快速了解 MVVM 如何工作的人。它不会做什么花哨的事情,只使用一些基本的数据绑定和命令。这篇文章将对所有正在寻找如何将 View
连接到 ViewModel
以及命令发挥作用的快速示例的人有所帮助。
今天,如果你上网搜索,你会发现成千上万的文章讨论 MVVM 是什么以及它的主要构建块是什么。所以,我又要重复同样疯狂的事情了。但肯定的是,我会用通俗易懂的语言为你简要介绍主要构建块,以便初学者更容易理解。兴奋吗???
构建块的通俗介绍
那么,让我们快速开始介绍 MVVM 的三个主要层。
- Model - Model 包含类似于现实世界实体的类,例如
Product
、Customer
、Student
、Invoice
等。它封装了业务逻辑和数据。请注意,Model
类不直接引用视图,或者换句话说,Model
类对视图没有依赖,无论它们如何实现。从技术上讲,Model
类与封装数据访问的服务或存储库结合使用。 - ViewModel -
ViewModel
类的主要目的是将数据暴露给视图。它包括表示逻辑,并且可以独立于Model
进行测试。与Model
类似,ViewModel
从不引用View
,但它公开属性和命令以绑定View
数据。本质上,ViewModel
充当View
和Model
之间的协调器。 - View -
View
仅处理屏幕上看到的外观。作为最佳实践,View
类中不应包含需要单元测试的逻辑代码。简单来说,View
s 仅用于 UI 可视行为。
我希望到目前为止,你可能已经了解了 View
、ViewModel
和 Model
的职责。好了,在开始编码之前,我想再简要介绍一些其他需要处理的点,以便更好地、更清晰地实现 MVVM。
任何 MVVM 应用的重要组成部分
有两个主要组件在实现任何 MVVM 应用时都起着至关重要的作用。第一个是 ViewModel
的基类,第二个是 ICommand
接口。
基类 - 每个 ViewModel
都需要一些公共的东西。因此,为了遵循良好的设计,这些公共的东西应该放在一个基类中,然后这个类可以被各种 ViewModel
s 派生。现在的问题是,这个基类应该包含什么?
这个基类包含 INotifyPropertyChanged
接口的实现。正如我之前提到的,ViewModel
不能直接引用 View
。因此,这个接口连接了两者之间的差距,并允许消息传递到 View
。INotifyPropertyChanged
的示例实现是
public class ViewModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
请注意,上面的片段是基础的。因此,如果需要,可以以各种方式扩展它,例如添加各种验证,如属性名称检查等。
好的,让我们转向 ICommand
接口。
ICommand 接口 - 这个接口提供了非常有用的方法,如 Execute
和 CanExecute
,它们对命令部分提供了完全的控制。View
使用命令与 ViewModel
交互。示例代码是
public class DelegateCommand : ICommand
{
private Action<object> _action;
public DelegateCommand(Action<object> action)
{
_action = action;
}
#region ICommand Members
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_action(parameter);
}
#endregion
}
我希望这些知识足够用于一个简单的 MVVM 示例。
示例场景
这是 Student
实体的示例,为了简单起见,我只取学生的 FirstName
。学生列表将显示在屏幕上。如果用户从列表中选择学生姓名并单击给定的按钮,那么选定的姓名将显示在下面的 textbox
中。是不是很简单???
模型
正如我所说,我的模型类在这里非常简单,只有 2 行代码
public class Student
{
public string FirstName { get; set; }
}
ViewModel
这个类比我们的 Model
复杂一些。首先让我们看一下下面的代码片段,然后我将详细介绍它
public class StudentsViewModel:ViewModelBase
{
public ObservableCollection<Student> StudentList { get; set; }
public string SelectedStudent { get; set; }
private ICommand _updateStudentNameCommand;
public ICommand UpdateStudentNameCommand
{
get { return _updateStudentNameCommand; }
set { _updateStudentNameCommand = value; }
}
private string _selectedName;
public string SelectedName
{
get { return _selectedName; }
set
{
if (_selectedName != value)
{
_selectedName = value;
RaisePropertyChanged("SelectedName");
}
}
}
public StudentViewModel()
{
UpdateStudentNameCommand = new DelegateCommand(new Action<object>(SelectedStudentDetails));
StudentList = new ObservableCollection<Model.Student>
{
new Student { FirstName = "Bruce" },
new Student { FirstName = "Harry" },
new Student { FirstName = "Stuart" },
new Student { FirstName = "Robert" }
};
}
public void SelectedStudentDetails(object parameter)
{
if (parameter != null)
SelectedName = (parameter as SimplestMVVM.Model.Student).FirstName;
}
}
你可能已经注意到我们继承了 ViewModelBase
类,它提供了 INotifyPropertyChanged
的实现。在这里,我公开了 FirstName
、StudentList
和 UpdateDetailsCommand
等属性,视图可以绑定到这些属性。
好了,让我们一项一项来。
FirstName
- 这个属性用于绑定我ListView
的单个项目StudentList
- 这个属性包含名字列表,并设置为我的 ListView 的ItemSource
UpdateDetailsCommand
- 这个属性使用DelegateCommand
类返回一个ICommand
。它可以绑定到任何东西,如按钮按下或键盘按下。我将此命令绑定到我的UpdateStudentDetails
方法,在按钮按下时更新文本。
关于 ViewModel 的内容就这些。现在我们可以继续进行 UI 绘制。
视图
至于视图,代码隐藏是完全空的
public MainWindow()
{
InitializeComponent();
}
大多数与 UI 相关的代码都放在 XAML 文件本身中
<Window x:Class="SimplestMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SimplestMVVM.ViewModel"
Title="MVVM" Height="300" Width="300">
<Window.DataContext>
<local:StudentsViewModel x:Name="ViewModel"/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.878*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.RowSpan="2">
<ListView Name="ListViewStudentDetails"
Grid.Row="2" ItemsSource="{Binding StudentList}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding FirstName}"/>
</GridView>
</ListView.View>
</ListView>
<Button Command="{Binding UpdateStudentNameCommand}"
CommandParameter="{Binding ElementName=ListViewStudentDetails,Path=SelectedItem}"
Content="Display selected student"/>
<TextBlock FontWeight="Bold" Text="Selected student is: ">
<Run Text="{Binding SelectedName, Mode=TwoWay}"/></TextBlock>
</StackPanel>
</Grid>
</Window>
如果你仔细查看代码,你将清楚地了解我在 ViewModel
中提到的属性。
这就是最简单的 MVVM 示例。总共只有很少的代码。是不是很好???
未来计划
在我的下一篇文章中,我将更详细地讨论 MVVM 的不同方面。