Silverlight - 视图模型
在本章中,我们将研究 Silverlight 软件开发中的一项重要技术,即视图模型的使用。
视图模型是一个关键部分,它通过将视图与模型分开来引入一种称为分离表示的技术。
视图模型提供了一种实现分离表示的方法,我们将了解它们如何利用 Silverlight 的数据绑定来减少用户界面中所需的代码量。
UI 开发挑战
视图模型旨在解决开发用户界面软件时经常出现的某些问题。 也许最重要的一点是用户界面代码通常很难进行不可分割的测试,尤其是自动化单元测试。 还有代码质量问题可能会影响代码的持续灵活性和可维护性。
如果您遵循 Visual Studio 设计工具引导您走的阻力最小的道路,您最终可能会在后面的代码中放入过多的代码。
在后台代码中添加大量应用程序功能是很常见的。
很少有开发人员真正计划将业务逻辑放入用户界面类中,但由于 Visual Studio 就是在其中放置事件处理程序的,因此它成为完成工作的一个非常方便的地方。
人们普遍认为,如果类具有明确定义且合理的职责范围,那么软件就更容易开发和维护。
隐藏代码的工作是在必要时直接与构成用户界面的对象进行交互。
一旦您开始放置决定应用程序行为方式的代码,这往往会导致问题。
不仅应用程序逻辑可以流入应该与用户界面相关的代码中,一些开发人员还开始依赖控件和其他用户界面对象来保存重要的应用程序状态。
模型仅保存数据,视图仅保存格式化日期,控制器(ViewModel)充当两者之间的联络人。 控制器可能从视图获取输入并将其放置在模型上,反之亦然。
分离表示
为了避免将应用程序逻辑放入代码隐藏或 XAML 中而导致的问题,最好使用一种称为分离表示的技术。 用户界面类拥有直接使用用户界面对象所需的最少 XAML 和代码,还包含用于复杂交互行为、应用程序逻辑和其他所有内容的代码,如下左侧所示。
分离表示的重要特征 −
通过分离的呈现,用户界面类更加简单。 当然它有 XAML,但背后的代码只做实用的事情。
应用程序逻辑属于一个单独的类,通常称为模型。
许多开发人员尝试使用数据绑定将 XAML 中的元素直接连接到模型中的属性。
问题是模型完全关心应用程序的功能,而不关心用户如何与应用程序交互。
大多数用户界面都有一些不属于应用程序模型的状态。 例如,如果您的用户界面使用拖放操作,则需要跟踪诸如被拖动的项目现在所在的位置、当其在可能的放置目标上移动时其外观应如何变化以及这些放置目标可能如何变化等信息。 当项目被拖过它们时会发生变化。
这种状态可能会变得异常复杂,需要进行彻底的测试。
在实践中,您通常需要在用户界面和模型之间放置一些其他类。 这有两个重要作用。
首先,它会根据特定的用户界面视图调整您的应用程序模型。
其次,它是任何重要交互逻辑所在的地方,我指的是让用户界面按照您想要的方式运行所需的代码。
模型/视图/视图模型
视图模型是分离表示方法的一个示例,但让我们清楚每一层中到底有什么类型的东西。 一共有三层 −
- 模型
- 视图
- 视图模型
模型
这是一个经典对象模型,由与用户界面没有直接关系的普通 C# 类组成。
您通常希望模型代码能够在不引用任何用户界面库的情况下进行编译。 事实上,您可能能够获取完全相同的源代码并将其编译成 Silverlight 应用程序、普通的 .NET 控制台应用程序,甚至服务器端 Web 代码。
模型中的类型应代表您的应用程序所使用的概念。
视图
视图通常是一个用户控件,它可能是您的主页,也可能只是页面的某些部分。
在大多数 Silverlight 应用程序中,最好将用户界面分成小块,为每个块定义一个 UserControl 或 View。
Silverlight 应用程序在这方面并不是独一无二的。 显然 Silverlight 特有的东西就是视图。 用户界面越细粒度,效果就越好。 您不仅不太可能被其他处理相同文件的开发人员绊倒,而且让事情保持小而简单自然会阻止导致类似意大利面条的代码的捷径。
例如,定义 View 来表示列表中的单个项目是很常见的。
视图模型
最后,为每个View编写一个ViewModel。 因此,这是 ViewModel 类的重要功能之一。
它的存在是为了服务于特定的视图。 ViewModel 专门用于呈现事物的特定方式,例如列表中显示的特定数据项。
这就是为什么它被称为ViewModel; 它特别针对特定视图调整底层模型。 与模型一样,ViewModel 也是一个普通的 C# 类。 它不需要派生自任何特定类型。
事实上,一些开发人员发现将一些常见功能放入基本 ViewModel 类中很方便,但该模式并不要求这样做。 特别是,您的 ViewModel 不派生自任何 Silverlight 特定类型。 但是,与模型不同的是,它可以在其属性中使用 Silverlight 类型。
例如,您的 ViewModel 可能会选择使用户界面的某些部分仅在某些条件下可见,因此您可能会提供 System.Windows.Visibility 类型的属性,这是 Silverlight 元素用于其 Visibility 属性的类型 。 这使得将元素(例如面板)的可见性直接绑定到 ViewModel 成为可能。
示例
让我们看一个简单的示例,我们将在其中使用模型-视图-视图模型 (MVVM) 方法。
步骤 1 − 创建一个新的 Silverlight 应用程序项目 SilverlightMVVMDemo。
步骤 2 − 将三个文件夹(Model、ViewModel 和 Views)添加到您的项目中,如下所示。
步骤 3 − 在 Model 文件夹中添加一个 StudentModel 类,并将以下代码粘贴到该类中。
using System.ComponentModel; namespace SilverlightMVVMDemo.Model { public class StudentModel {} public class Student : INotifyPropertyChanged { private string firstName; private string lastName; public string FirstName { get { return firstName; } set { if (firstName != value) { firstName = value; RaisePropertyChanged("FirstName"); RaisePropertyChanged("FullName"); } } } public string LastName { get { return lastName; } set { if (lastName != value) { lastName = value; RaisePropertyChanged("LastName"); RaisePropertyChanged("FullName"); } } } public string FullName { get { return firstName + " " + lastName; } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } }
步骤 4 − 将另一个 StudentViewModel 类添加到 ViewModel 文件夹中并粘贴以下代码。
using SilverlightMVVMDemo.Model; using System.Collections.ObjectModel; namespace SilverlightMVVMDemo.ViewModel { public class StudentViewModel { public ObservableCollection<Student> Students { get; set; } public void LoadStudents() { ObservableCollection<Student> students = new ObservableCollection<Student>(); students.Add(new Student { FirstName = "Mark", LastName = "Allain" }); students.Add(new Student { FirstName = "Allen", LastName = "Brown" }); students.Add(new Student { FirstName = "Linda", LastName = "Hamerski" }); Students = students; } } }
步骤 5 − 右键单击 Views 文件夹并选择添加新项目...,添加Silverlight 用户控件。
步骤 6 − 单击添加。 现在您将看到 XAML 文件。 将以下代码添加到 StudentView.xaml 文件中,该文件包含不同的 UI 元素。
<UserControl x:Class = "SilverlightMVVMDemo.Views.StudentView" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "400"> <Grid x:Name = "LayoutRoot" Background = "White"> <StackPanel HorizontalAlignment = "Left"> <ItemsControl ItemsSource = "{Binding Path=Students}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation = "Horizontal"> <TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}" Width = "100" Margin = "3 5 3 5"/> <TextBox Text = "{Binding Path = LastName, Mode = TwoWay}" Width = "100" Margin = "0 5 3 5"/> <TextBlock Text = "{Binding Path = FullName, Mode=OneWay}" Margin = "0 5 3 5"/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </Grid> </UserControl>
步骤 7 − 现在将 StudentView 添加到 MainPage.xaml 文件中,如下所示。
<UserControl x:Class = "SilverlightMVVMDemo.MainPage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views = "clr-namespace:SilverlightMVVMDemo.Views" mc:Ignorable = "d" d:DesignHeight = "576.316" d:DesignWidth = "863.158"> <Grid x:Name = "LayoutRoot" Background = "White"> <views:StudentView x:Name = "StudentViewControl" Loaded = "StudentViewControl_Loaded"/> </Grid> </UserControl>
步骤 8 − 以下是 MainPage.xaml.cs 文件中 Loaded 事件的实现,这将从 ViewModel 更新 View。
using System.Windows; using System.Windows.Controls; namespace SilverlightMVVMDemo { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); } } private void StudentViewControl_Loaded(object sender, RoutedEventArgs e) { SilverlightMVVMDemo.ViewModel.StudentViewModel studentViewModelObject = new SilverlightMVVMDemo.ViewModel. StudentViewModel(); studentViewModelObject.LoadStudents(); StudentViewControl.DataContext = studentViewModelObject; } }
步骤 9 − 编译并执行上述代码后,您将在网页上看到以下输出。
UI 与 ViewModel
MVVM 方法最困难的部分之一是确定分界线应该在哪里。 哪些东西属于哪里并不总是显而易见的。
特别是,一些用户界面元素提供的功能,根据严格的视图,可以说应该属于 ViewModel。
一般来说,并非 View 中实现的所有行为都对 ViewModel 友好。
造成这种情况的部分原因是没有任何标准方法来打包 ViewModel 行为以供重用,特别是如果您想使用设计环境(例如 Visual Studio 或 Blend)。
MVVM 的优点
MVVM具有以下优点 −
分离表示关注点(View、ViewModel、Model)
干净的可测试和可管理的代码。 可以在单元测试中包含表示层逻辑。
代码后面没有代码,因此表示层和逻辑是松散耦合的。
更好的数据绑定方式。
MVVM 的缺点
对于简单的 UI,MVVM 可能有点大材小用。 当我们有复杂的数据绑定时,调试会有点困难。