65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF:MVVM(模型视图视图模型)简化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (79投票s)

2009年5月20日

CPOL

4分钟阅读

viewsIcon

452929

downloadIcon

13673

提供一个清晰简单的示例,清楚地说明 MVVM 及其用法。

引言

几个月前,我从 WinForms 编程转向了 WPF,很自然地,我很快就上手了。说实话,自从 Silverlight 推出以来,我一直在开发 Silverlight 应用程序,鉴于 Silverlight 是 WPF 的一个子集,它的学习曲线很低。然而,WPF 中的命令(Commanding)概念有些不同,我很快就发现 WPF 中的命令比 Silverlight 强大得多。

命令(Commanding)表现出色的一个领域是它如何与 MVVM 相辅相成。但什么是 MVVM,它有什么用?在我看来,这是学习 WPF(和 Silverlight)编程中最难理解的概念。你为什么会问?因为它很简单,而作为开发人员,我们经常喜欢那些能让我们绞尽脑汁的代码或概念,这样我们就能向同行吹嘘我们花了 2 个小时就理解并实现了下一个“大事件”(我没有在炫耀)。顺便说一句,我发现所有关于 MVVM 的博客都通过添加过多的代码来使其复杂化,这只会让你摸不着头脑。简单是解决所有复杂问题的关键。所以,让我们先深入研究一点理论,然后用一些简洁的代码来结束。

目的

本文的目的是

  1. 给出模型-视图-视图模型(Model View View-Model)的简单清晰的定义
  2. 提供一个清晰简单的示例,清楚地说明 MVVM 的用法

MVVM?

clip_image001.gif

图1。

以防您无法阅读图片中的文字,这里是图片中的内容

  1. 视图(View)持有对视图模型(ViewModel)的引用。视图主要通过绑定(Binding)到视图模型中的实体来显示内容。
  2. 视图模型(ViewModel)向视图公开命令(Commands)、可通知属性(Notifiable Properties)和可观察集合(Observable Collections)。视图绑定(Binds)到这些视图模型实体/成员。
  3. 模型(Model)是您的数据和/或应用程序对象,它们在应用了应用程序逻辑的同时移动数据。如果您有业务逻辑层(Business Layer),那么您可能不需要这个。

上面是一个简单的图,它准确地告诉您 MVVM 是什么。用我自己的话说,视图模型(ViewModel)是整个模式中最重要的部分,因为它是连接视图(View)和模型(Model)的粘合剂,并将它们绑定在一起。现在,让我们来探索一些代码。

代码

您即将看到的应用程序在设计和实现上都非常复杂,因此任何人都不应批评它。以下是该应用程序功能的概述。它会获取您的名字、姓氏和年龄,然后在一个消息框中显示给您。下面是这个非常复杂的类图。

ClassDiagram1.png

图2。

让我们看一下 PersonModel 类,它是应用程序中唯一的模型。

namespace OliverCode.MVVM.Model 
{ 
    internal class PersonModel : System.ComponentModel.INotifyPropertyChanged 
    { 
        private string firstName; 
        public string FirstName 
        { 
            get { return firstName; } 
            set 
            { 
                firstName = value; 
                OnPropertyChanged("FirstName"); 
            } 
        } 
        private string lastName; 
        public string LastName 
        { 
            get { return lastName; } 
            set 
            { 
                lastName = value; 
                OnPropertyChanged("LastName"); 
            } 
        } 
        private int age; 
        public int Age 
        { 
            get { return age; } 
            set 
            { 
                age = value; 
                OnPropertyChanged("Age"); 
            } 
        } 
#region INotifyPropertyChanged Members 
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 
        private void OnPropertyChanged(string propertyName) 
        { 
            if (PropertyChanged != null) 
                PropertyChanged(this,
                    new System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
        } 
#endregion 
    } 
}

Person 类实现了 INotifyPropertyChanged 接口,该接口使 WPF 元素在 Person 对象上的任何属性发生更改时立即得到通知。

继续……让我们看看这个被巧妙命名的视图,PersonView(如果我可以补充的话,非常有创意)。

<UserControl x:Class="OliverCode.MVVM.View.PersonView" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Height="Auto" Width="Auto" 
xmlns:local="clr-namespace:OliverCode.MVVM.ViewModel"> 
<StackPanel Orientation="Vertical" Margin="4"> 
<!--Here is where the view gets a reference to the ViewModel Declaratively--> 
<StackPanel.DataContext> 
<local:PersonViewModel /> 
</StackPanel.DataContext> 
<StackPanel Orientation="Vertical" DataContext="{Binding Path=Person, Mode=TwoWay}"
    Margin="4"> 
<StackPanel Orientation="Horizontal"> 
<Label Content="First Name:" Margin="0,0,4,0"/> 
<TextBox Width="250" Text="{Binding Path=FirstName}"/> 
</StackPanel> 
<StackPanel Orientation="Horizontal" Margin="0,5,0,0"> 
<Label Content="Last Name:" Margin="0,0,4,0"/> 
<TextBox Width="250" Text="{Binding Path=LastName}"/> 
</StackPanel> 
<StackPanel Orientation="Horizontal" Margin="0,5,0,0"> 
<Label Content="Age:" Margin="35,0,4,0"/> 
<TextBox Width="50" MaxLength="3" Text="{Binding Path=Age}"/> 
</StackPanel> 
</StackPanel> 
<StackPanel> 
<!—The Command is bound to the Property in the PersonViewModel call SavePersonCommand--> 
<Button Content="Save" HorizontalAlignment="Right" Width="80"
   Command="{Binding Path=SavePersonCommand}"/> 
</StackPanel> 
</StackPanel> 
</UserControl>

上面的 XAML 中最关键的一点是 PersonViewModel 如何附加到 PersonViewDataContext。 (这是视图获取视图模型引用的典型方式。)同时也要注意 Button 元素,它的 Command 使用 Binding 类来附加 SavepersonCommand,这是 ViewModel 上的一个属性。通常,绑定到命令比这要复杂,但由于 WPF MVVM Toolkit 1.0(可在 这里找到)由 Microsoft 团队提供,开发人员现在可以轻松地绑定到命令。我在项目中包含了 DelegateCommand 类,因此您无需直接下载它。还有一个 CommandReference 类,其目的是解决 WPF 在从 XAML 绑定数据时的限制。(该类未在程序中使用。)

WPF MVVM Toolkit 1.0 摘要

工具包中有几个类,但您应该关注的是 DelegateCommand。这个类可以方便地编写一个函数来处理一个手势或命令。手势可以被认为是任何可以触发命令的交互。我像这样在我的 PersonViewModel 中直接使用 DelegateCommand

private DelegateCommand savePersonCommand; 
public ICommand SavePersonCommand 
{ 
    get 
    { 
        if(savePersonCommand == null) 
            savePersonCommand = new DelegateCommand(new Action(SaveExecuted), 
                new Func<bool>(SaveCanExecute)); 
        return savePersonCommand; 
    } 
} 
public bool SaveCanExecute() 
{ 
    return Person.Age > 0 && !string.IsNullOrEmpty(Person.FirstName) && 
        !string.IsNullOrEmpty(Person.LastName); 
} 
public void SaveExecuted() 
{ 
    System.Windows.MessageBox.Show(string.Format("Saved: {0} {1} - ({2})",
        Person.FirstName, Person.LastName, Person.Age)); 
}

这是完整的 personViewModel

namespace OliverCode.MVVM.ViewModel 
{ 
    internal class PersonViewModel 
    { 
        public PersonModel Person { get; set; } 
        private DelegateCommand savePersonCommand; 
        public ICommand SavePersonCommand 
        { 
            get 
            { 
                if(savePersonCommand == null) 
                    savePersonCommand = new DelegateCommand(new Action(SaveExecuted),
                        new Func<bool>(SaveCanExecute)); 
                return savePersonCommand; 
            } 
        } 
        public PersonViewModel() 
        { 
            //This data will load as the default person from the model attached to
            //the view 
            Person = new PersonModel 
			{ FirstName = "John", LastName = "Doe", Age = 999 }; 
        } 
        public bool SaveCanExecute() 
        { 
            return Person.Age > 0 && !string.IsNullOrEmpty(Person.FirstName) && 
                !string.IsNullOrEmpty(Person.LastName); 
        } 
        public void SaveExecuted() 
        { 
            System.Windows.MessageBox.Show(string.Format("Saved: {0} {1} - ({2})", 
                Person.FirstName, Person.LastName, Person.Age)); 
        } 
    } 
}

很简单,不是吗?我希望我通过提供一个简单的 WPF MVVM 演示,为一些人节省了数小时的查找时间。别忘了查看 http://olivercode.wordpress.com/

谢谢,祝您编码愉快。

历史

  • 2009 年 5 月 20 日:初始发布
© . All rights reserved.