WPF中模型-视图-视图模型模式介绍

在目前开发中,流行的开发模式有MVC(模型-视图-控制器)和MVP(模型-视图-表示器),其中MVC模式常见于java web开发中,比如Struts2、Struts2,后来微软也推出了MVC模式的开发框架。MVP模式是从MVC演变过来的,作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。但是,如果把它们都用在与WPF中,存在着一个重要的缺陷:根本没有考虑数据绑定技术。也就是说,在这些模式中,控制器或者表示器负责作用与视图,如在文本库填写文字,加载列表框,填充网格。而WPF最大的优点在于它丰富的数据绑定能力。如果在WPF中利用MVC或者MVP,则完全忽略了WPF中数据绑定的便利性。

有没有一种方法可以从模型中分离视图,而不用忽略大WPF的特性呢,答案就是采用模型-视图-视图模型模式。这个模式是在John Gossman博客中看到的,他以前是Microsoft Expression Blend团队的成员之一,现在在Microsoft的WPF团队。模型-视图-视图模型模式把模型从视图分离,但同时充分利用WPF的特性。该模式采用纯粹的模型,创建一个包含状态的抽象视图,用可视化设计器创建一个视图,将其数据绑定到抽象视图。其中可视化设计器可以是Microsoft Expression Blend工具,抽象视图就是视图模型。视图和视图模型之间通过数据绑定建立双向连接。经过适当的设置,每一个视图只包含纯粹的XAML以及非常少量的过程代码,这样UI美工就可以专注于设计视图,而不必担心对开发有任何的影响。

在这里,还要介绍下视图模型这个概念。视图模型存在的目的就是把模型适配到视图。这就意味着在模型中有一个方法,该方法返回一个特性的类型,比如IList<T>类型,然后视图把来自模型的类型转换为类似WPF UI元素的CollectionView类来帮顶数据。此外,因为WPF自然的实现了命令(Command)模式,也就是说某个UI元素,比如按钮,它有一个属性为Command,是WPF定义的ICommand类型。于是,我们可以把命令放进视图模型中,并作为公有属性提供,以便视图绑定。这将非常有用,因为它允许把可执行代码绑定到表单上的按钮,而不用编写任何代码来连接按钮。WPF的命令模式以及视图模型的公有Command属性负责上述工作。

讲了那么多,下面通过实例来实践下。

一、建立一个模型类,命名为ProjectService,该类中提供了一个供视图模型类方法的方法GetAllProject,代码如下:

public class ProjectService { public static IList<Project> GetAllProject() { List<Project> list = new List<Project>(); list.Add(new Project { ProjectName = "CMMI4", Id = 1 }); list.Add(new Project { ProjectName = "GXQXT", Id = 2 }); return list; } } 

这段代码很简洁,功能非常的简单,仅仅是提供一个集合给视图

二、构建一个视图,主要是在界面中已下拉表的形式向用户显示列表。为此,需要构建一个响应的视图模型类,这样就可以让视图类来绑定它。所以在这里定义了一个视图模型类ProjectViewModel,该类提供一个列表和两个命令:

public class ProjectViewModel { private CollectionView projects; private DelegateCommand selectCommand; private DelegateCommand cancelCommand; private IView view; public ProjectViewModel() { } public ProjectViewModel(IView view) { this.view = view; this.projects = new CollectionView(ProjectService.GetAllProject()); this.selectCommand = new DelegateCommand(this.SelectCommandHandler); this.cancelCommand = new DelegateCommand(this.CancelCommandHandler); } public CollectionView Projects { get { return this.projects; } } public DelegateCommand SelectCommand { get { return this.selectCommand; } } public DelegateCommand CancelCommand { get { return this.cancelCommand; } } private void SelectCommandHandler(object sender, EventArgs e) { Project project = this.projects.CurrentItem as Project; System.Windows.MessageBox.Show(project.ProjectName, "Project"); this.view.Close(); } private void CancelCommandHandler(object sender, EventArgs e) { this.view.Close(); } } 

在ProjectViewModel中,用到了IView类型,它是一个接口。主要是提供一个视图的引用,在这里,我们在IView中定义了两个方法,Show方法和Close方法。而且WPF Window类碰巧也实现了这两个方法:

public interface IView { void Show(); void Close(); } 

然后ProjectViewModel要做的另外一件事就是把IList<Project>列表的项目转换成WPF的CollectionView类,所以在视图模型类中提供了Projects公有属性给视图。接下来的两个DelegateCommand 属性,用于视图类中的按钮设置ICommand性,DelegateCommand 不仅实现了ICommand接口,而且还允许调用ICommand的Execute方法时,调用一个委托,DelegateCommand 的代码如下:

public class DelegateCommand : ICommand { public delegate void SimpleEventHandler(object sender, EventArgs e); private SimpleEventHandler handler; private bool isEnabled = true; public DelegateCommand(SimpleEventHandler handler) { this.handler = handler; } public bool CanExecute(object parameter) { return this.isEnabled; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { this.handler(this, EventArgs.Empty); } private void OnCanExecuteChanged() { if(this.CanExecuteChanged != null) { this.CanExecuteChanged(this,EventArgs.Empty); } } public bool IsEnabled { get { return this.isEnabled; } set { this.isEnabled = value; this.OnCanExecuteChanged(); } } } 

 

到现在为止,已经接到了视图模型类,下面将讲述视图实际上如果使用它并和它通讯,视图类ProjectView的隐藏代码很少,唯一为它编写的代码就是在构造器中用来连接表达的Window元素的DataContext属性:

public partial class ProjectView : Window, IView { public ProjectView() { InitializeComponent(); this.DataContext = new ProjectViewModel(this); } } 

注意,ProjectView实现了IView接口的Show、Close方法。然后在ProjectView视图的XAML中编写显示到UI'的代码:

<ComboBox Height="26" Margin="175,98,28,0" Name="projectcombox" VerticalAlignment="Top" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Projects}"> <ComboBox.ItemTemplate> <DataTemplate> <Grid ShowGridLines="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="50"></ColumnDefinition> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Path=ProjectName}"/> <TextBlock Grid.Column="1" Text="{Binding Path=Id}"/> </Grid> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button Margin="47,0,0,35" Name="cancelButton" Command="{Binding Path=CancelCommand}" HorizontalAlignment="Left" VerticalAlignment="Bottom">Cancel</Button> <Button Margin="0,0,28,35" Name="okButton" Command="{Binding Path=SelectCommand}" HorizontalAlignment="Right" VerticalAlignment="Bottom">OK</Button> 

 

这样一来,一个模型-视图-视图模型表示模式的实例就出来了,通过这个实例,我们可以很明了的看到。UI设计与后台代码之间的关联就没有那么强了,界面设计与开发独立开来。

你可能感兴趣的:(mvc,command,Microsoft,WPF,binding,DataTemplate)