Silverlight - 视图模型



本章我们将探讨Silverlight软件开发中一项重要的技术:使用视图模型

  • 视图模型是一个关键部分,它通过将视图与模型分开来引入一种称为分离表示的技术。

  • 视图模型提供了一种实现分离表示的方法,我们将看到它们如何利用Silverlight的数据绑定来减少用户界面中所需的代码量。

UI开发挑战

视图模型旨在解决开发用户界面软件时经常出现的一些问题。也许最重要的一点是,用户界面代码通常难以进行测试,尤其难以进行自动单元测试。还有一些代码质量问题会影响代码的持续灵活性和可维护性。

  • 如果您遵循Visual Studio设计工具引导您的最便捷路径,您最终可能会在代码隐藏中放入过多的代码。

  • 通常会看到大量应用程序功能添加到代码隐藏中。

  • 很少有开发人员会实际计划将业务逻辑放入用户界面类中,但由于这就是Visual Studio放置事件处理程序的地方,因此它成为完成任务的一个过于方便的地方。

  • 人们普遍认为,如果类具有明确定义且责任范围合理的职责,则软件更容易开发和维护。

  • 代码隐藏的工作是根据需要直接与构成用户界面的对象进行交互。

  • 一旦您开始在其中放置关于应用程序如何运行的决策代码,就会导致问题。

  • 应用程序逻辑不仅会流入应该关注用户界面的代码,一些开发人员还会开始依赖控件和其他用户界面对象来保存重要的应用程序状态。

  • 模型只保存数据,视图只保存格式化后的数据,控制器(ViewModel)充当两者之间的联络员。控制器可能会接收来自视图的输入并将其放入模型中,反之亦然。

MVC

分离表示

为了避免将应用程序逻辑放入代码隐藏或XAML中而导致的问题,最好使用称为分离表示的技术。使XAML和代码隐藏具有与用户界面对象直接交互所需的最小功能,用户界面类还包含复杂交互行为、应用程序逻辑以及其他所有内容的代码,如下面的左侧所示。

Separated Presentation

分离表示的重要特性:

  • 使用分离表示,用户界面类会更简单。当然它有XAML,但代码隐藏尽可能少。

  • 应用程序逻辑位于一个单独的类中,通常称为模型

  • 许多开发人员尝试使用数据绑定将XAML中的元素直接连接到模型中的属性。

  • 问题是模型完全关注应用程序的功能,而不关注用户如何与应用程序交互。

  • 大多数用户界面都有一些不属于应用程序模型的状态。例如,如果您的用户界面使用拖放,则某些内容需要跟踪诸如当前拖动项目的所在位置、在拖动到可能的放置目标时其外观应如何变化以及这些放置目标在拖动项目经过时也可能如何变化等方面。

  • 这种状态可能会变得非常复杂,需要进行彻底的测试。

  • 实际上,您通常希望在用户界面和模型之间放置另一个类。它有两个重要的作用。

    • 首先,它会为特定用户界面视图调整您的应用程序模型。

    • 其次,它是任何非平凡交互逻辑所在的地方,我的意思是让您的用户界面按您想要的方式运行所需的代码。

模型/视图/视图模型

视图模型是分离表示方法的一个示例,但让我们明确说明每一层中究竟是什么样的东西。有三层:

  • 模型
  • 视图
  • 视图模型

模型

这是一个由普通的C#类组成的经典对象模型,它与用户界面没有直接关系。

您通常期望您的模型代码能够在没有对任何用户界面库的引用的情况下编译。实际上,您可能能够使用完全相同的源代码并将其编译到Silverlight应用程序、普通的.NET控制台应用程序甚至服务器端Web代码中。

模型中的类型应表示您的应用程序使用的概念。

视图

视图通常是UserControl,它可能是您的MainPage,也可能只是您页面的一部分。

在大多数Silverlight应用程序中,将用户界面拆分为较小的部分来定义每个部分的UserControl或View是一个好主意。

Silverlight应用程序在这方面并非独一无二。显然特定于Silverlight的是View。用户界面的粒度越细,效果越好。您不太可能遇到其他开发人员处理相同文件的麻烦,保持简洁自然会阻止导致意大利面条式代码的捷径。

例如,定义一个视图来表示列表中的单个项目是很常见的。

视图模型

最后,对于每个视图,您都编写一个视图模型。所以,这是视图模型类的一个重要特性。

它存在于为特定的视图服务。视图模型专门用于特定的事物呈现方式,例如列表中显示的特定数据项。

这就是为什么它被称为视图模型;它专门为特定视图调整底层模型。与模型一样,视图模型也是一个普通的C#类。它不需要派生自任何特定类型。

碰巧的是,一些开发人员发现将一些常用功能放入基视图模型类中很方便,但模式并不需要这样做。特别是,您的视图模型不会派生自任何Silverlight特定类型。但是,与模型不同的是,它可以在其属性中使用Silverlight类型。

例如,您的视图模型可能会选择仅在特定条件下显示用户界面的某些部分,因此您可能会提供System.Windows.Visibility类型的属性,这是Silverlight元素对其Visibility属性使用的类型。这使得可以直接将元素(例如面板)的可见性绑定到视图模型成为可能。

示例

让我们来看一个简单的例子,在这个例子中我们将使用模型-视图-视图模型(MVVM)方法。

步骤1 - 创建一个新的Silverlight应用程序项目SilverlightMVVMDemo

步骤2 - 将三个文件夹(Model、ViewModel和Views)添加到您的项目中,如下所示。

SilverlightMVVMDemo

步骤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 - 在ViewModel文件夹中添加另一个StudentViewModel类,并粘贴以下代码。

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用户控件

Silverlight User Control

步骤6 - 点击添加。现在您将看到XAML文件。将以下代码添加到包含不同UI元素的StudentView.xaml文件中。

<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更新视图

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 - 编译并执行上述代码后,您将在网页上看到以下输出。

Add Student View

UI与视图模型

MVVM方法中最难的部分之一是确定分界线应该在哪里。哪些东西属于哪里并不总是显而易见的。

  • 特别是,一些用户界面元素提供了根据严格的视图来看,可能属于视图模型的功能。

  • 一般来说,并非所有在视图中实现的行为都那么视图模型友好。

  • 部分原因是没有标准的方法来打包视图模型行为以供重用,特别是如果您想使用设计环境(如Visual Studio或Blend)时。

MVVM的优点

MVVM具有以下优点:

  • 分离表示关注点(视图、视图模型、模型)

  • 简洁易于测试和管理的代码。可以在单元测试中包含表示层逻辑。

  • 没有代码隐藏代码,因此表示层和逻辑是松散耦合的。

  • 更好的数据绑定方式。

MVVM的缺点

对于简单的UI,MVVM可能过于复杂。当我们有复杂的数据绑定时,调试会比较困难。

广告