Windows Phone Mango:MVVM十分钟入门

在这篇文章中,我将谈一下在windows phone 7.1 Mango应用程序中使用MVVM设计模式。用较少的理论、更多的示例,在10分钟内来解释MVVM模式。

在开始之前,先简短介绍一下什么是MVVM:Model-View-ViewModel (MVVM)模式提供了一种灵活的方式,通过将应用程序分隔成三部分来创建windows phone应用程序。

a:View:放置XAML文件。

b:ViewModel:放置连接UI和数据的显示逻辑层。

c:放置数据模型和业务对象。

image

有关更多信息,看参阅官方MSDN文档

 

为什么选择MVVM?有什么好处?

  • 视图和显示逻辑层分离:从显示逻辑层分离出来View/XAML,可以使开发者只关注Code,而设计人员只关注XAML。

  • 自动单元测试:视图/逻辑分离大大改进了表现逻辑层的自动单元测试。

  • 代码重用:因为显示逻辑层在独立的组件或类中,和View(XAML)分离开来,你可以按照你喜欢的方式,通过继承和组合使它们结合起来。

  • 设计时数据支持: 你可以在Blend中看到UI的样子,也就是说,设计人员可以使用样本数据来测试UI,甚至可以模拟实际场景数据。

  • 多视图: 依据用户角色,同一ViewModel可以在不同的多视图中展现。

入门开始:

我们需要Windows Phone 7.1 Mango工程项目。在这个实例中,我们会使用上篇文章 为Windows Phone Mango MVVM 应用创建可复用 ICommand 实现类  所创建的DelegateCommand。请注意:commanding是伴随mango更新的新特性。(commanding意味着一些控件不支持Commands)。

MODEL

首先我们需要定义Model。在示例中,我们创建Person类,有两个属性:Name和Age。在这里,最重要的就是要实现INotifyPropertyChanged接口,因为,当Person对象发现变化时,需要通知给UI。(本例中,当按下Save Changes时,更新ListBox,更多信息参阅下面部分中的View)

 

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
 
namespace WPMangoMVVMSample
{
    public class Person : INotifyPropertyChanged
    {
        private string name;
        private int age;
 
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                if (this.name != value)
                {
                    this.name = value;
                    this.RaisePropertyChanged("Name");
                }
            }
        }
 
        public int Age
        {
            get
            {
                return this.age;
            }
            set
            {
                if (this.age != value)
                {
                    this.age = value;
                    this.RaisePropertyChanged("Age");
                }
            }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

 

VIEW MODEL

下一步就是创建PersonViewModel,主要包含以下部分:

  • SaveChangesCommand - 保存用户选择Person对象所引发的修改。
  • LoadDataCommand - 此命令是用来填充ObservableCollection(Person对象)。
  • SelectedName - 此属性表示选中的Person对象的名称。
  • SelectedAge - 此属性表示选中的Person对象的年龄。
  • SelectedPerson - 此属性表示所选中的Person对象。

PersonViewModel也实现了INotifyPropertyChanged,所有当某些属性更新时,UI会接到通知。 代码如下:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
 
namespace WPMangoMVVMSample
{
    public class PersonViewModel : INotifyPropertyChanged
    {
        private string name;
        private int age;
        private ObservableCollection<Person> personDataSource;
        private ICommand loadDataCommand;
        private ICommand saveChangesCommand;
 
        public PersonViewModel()
        {
            this.loadDataCommand = new DelegateCommand(this.LoadDataAction);
            this.saveChangesCommand = new DelegateCommand(this.SaveChangesAction);
        }
 
        private void LoadDataAction(object p)
        {
            this.DataSource.Add(new Person() { Name = "John", Age = 32 });
            this.DataSource.Add(new Person() { Name = "Kate", Age = 27 });
            this.DataSource.Add(new Person() { Name = "Sam", Age = 30 });
        }
 
        private void SaveChangesAction(object p)
        {
            if (this.SelectedPerson != null)
            {
                this.SelectedPerson.Name = this.name;
                this.SelectedPerson.Age = this.age;
            }
        }
 
        public ICommand LoadDataCommand
        {
            get
            {
                return this.loadDataCommand;
            }
        }
 
        public ICommand SaveChangesCommand
        {
            get
            {
                return this.saveChangesCommand;
            }
        }
 
        public ObservableCollection<Person> DataSource
        {
            get
            {
                if (this.personDataSource == null)
                {
                    this.personDataSource = new ObservableCollection<Person>();
                }
                return this.personDataSource;
            }
        }
 
        public string SelectedName
        {
            get
            {
                if (this.SelectedPerson != null)
                {
                    return this.SelectedPerson.Name;
                }
                return string.Empty;
            }
            set
            {
                this.name = value;
            }
        }
 
 
        public int SelectedAge
        {
            get
            {
                if (this.SelectedPerson != null)
                {
                    return this.SelectedPerson.Age;
                }
                return 0;
            }
            set
            {
                this.age = value;
            }
        }
 
        private Person selectedPerson;
 
        public Person SelectedPerson
        {
            get
            {
                return this.selectedPerson;
            }
            set
            {
                if (this.selectedPerson != value)
                {
                    this.selectedPerson = value;
                    if (this.selectedPerson != null)
                    {
                        this.name = this.selectedPerson.Name;
                        this.age = this.selectedPerson.Age;
                    }
                    this.RaisePropertyChanged("SelectedName");
                    this.RaisePropertyChanged("SelectedAge");
                }
            }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

 

VIEW

最后一步就是创建View。 我们仍然使用 MainPage.xaml。我们将创建:

  • "LoadData" 按钮: 调用LoadDataCommand填充数据源。
  • ListBox  绑定PersonViewModel,两个TextBox用来编辑选择的Person对象: 姓名和年龄。
  • "Save Changes" 调用SaveChangesCommand保存修改信息。

注意: TextBox的Binding 数据流向:Mode=TwoWay ,可以编辑和更新值。

<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Button Content="LoadData" Command="{Binding LoadDataCommand}" />
    <ListBox ItemsSource="{Binding DataSource}" SelectedItem="{Binding SelectedPerson, Mode=TwoWay}" Height="100">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Name:"/>
                    <TextBlock Text="{Binding Name}" />
                    <TextBlock Text="Age:" Margin="10,0,0,0"/>
                    <TextBlock Text="{Binding Age}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <TextBlock Text="Name:"/>
    <TextBox Text="{Binding SelectedName, Mode=TwoWay}" />
    <TextBlock Text="Age:"/>
    <TextBox Text="{Binding SelectedAge, Mode=TwoWay}" />
    <Button Content="Save Changes" Command="{Binding SaveChangesCommand}" />
</StackPanel>

绑定view至view model简单方法就是设置DataContext:

public MainPage()
{
    InitializeComponent();
 
    // simple way to bind the view to the view model
    this.DataContext = new PersonViewModel();
}

效果图:

XN_)BODP)CLKK2@FIW(6PCKQU~[5_056}X61}_S(A19MTG

 

源代码下载

 

译自:windowsphonegeek

 

由于本人翻译水平有限,有些地方欠妥,请园友们不吝指教!

posted @ 2011-09-22 16:43  sunfish  阅读(3095)  评论(9编辑  收藏  举报