Silverlight之MVVM模式简单介绍项目(数据绑定)

很久没有更新博客了,今天向大家介绍一下Silverlight MVVM模式的使用。 MVVM即Model-View-ViewModel模式,它是一种轻量级的,灵活的方式分离数据实体与视图之间的关系,可以更好的提高代码的可重用性,便于项目的管理和测试。View层主要应用于页面展现,Model为数据的构造,ViewModel层用于逻辑的实现,并且使用数据绑定将三者之间很好的联系起来。

本项目中我们通过演示制作一个简单的数据绑定的例子, 来讲述MVVM模式程序的工作原理,使用过WPF和Silverlight的朋友可能都知道,在XAML中,一般的数据绑定有三种,

One-Time,One-Way,Two-way。

One-Time绑定模式的意思即为从Data object绑定至UI这一层只进行一次绑定,程序不会继续追踪数据的在两者中任何一方的变化,这种绑定方式很使用于报表数据,数据仅仅会加载一次。

One-Way绑定模式即为单向绑定,即object-UI的绑定,只有当object中数据发生了变化,UI中的数据也将会随之发生变化,反之不然。

Two-Way绑定模式为双向绑定,无论数据在Object或者是UI中发生变化,应用程序将会更新另一方,这是最为灵活的绑定方式,同时代价也是最大的。

在这个程序中,我们将针对Two-Way数据绑定模式进行实验。掌握了双向绑定模式,其他两种也很好理解了,希望大家能够在实际项目中灵活使用这三种绑定方式。

本项目使用Visual Studio 2010 Ultimate和Silverlight 4 制作。

[本示例完整源码下载(0分)]  http://download.csdn.net/detail/aa466564931/3701792

首先创建一个名为CSSL4DataGridBindingInMVVM的Silverlight程序。我们将使用MainPage.xaml作为主界面,在Grid里面添加一些TextBlock,TextBox和Button作为简单的数据呈现及修改,下面的Xaml的内容,添加了4个TextBlock,4个TextBox和两个Button:

MainPage.xaml

    <Grid x:Name="LayoutRoot" Background="White"
          DataContext="{Binding Source={StaticResource viewModel}}" >
        <Grid Name="personGrid" DataContext="{Binding Path=person, Mode=TwoWay}"  >
            <Grid.RowDefinitions>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition Width="120"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock VerticalAlignment="Center" Name="lbName" Grid.Row="0" Grid.Column="0" Text="Name:" />
            <TextBox Name="tbName" Text="{Binding Path=Name, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"
                     BindingValidationError="tbValidate" Grid.Column="1" Height="30" VerticalAlignment="Top"></TextBox>
            <TextBlock VerticalAlignment="Center" Name="lbAge" Grid.Row="1" Grid.Column="0" Text="Age:" />
            <TextBox Name="tbAge" Text="{Binding Path=Age, Mode=TwoWay, NotifyOnValidationError=True,ValidatesOnExceptions=True}"
                     BindingValidationError="tbValidate"  Grid.Column="1" Grid.Row="1"></TextBox>
            <TextBlock VerticalAlignment="Center" Name="lbTelephone" Grid.Row="2" Grid.Column="0" Text="Telephone:" />
            <TextBox Name="tbTelephone" Text="{Binding Path=Telephone, Mode=TwoWay, NotifyOnValidationError=True,ValidatesOnExceptions=True}"
                     BindingValidationError="tbValidate" Grid.Column="1" Grid.Row="2"></TextBox>
            <TextBlock VerticalAlignment="Center" Name="lbComment" Grid.Row="3" Grid.Column="0" Text="Comment:" />
            <TextBox Name="tbComment" Text="{Binding Path=Comment, Mode=TwoWay,NotifyOnValidationError=True, ValidatesOnExceptions=True}" 
                     BindingValidationError="tbValidate" Grid.Column="1" Grid.Row="3"></TextBox>
            <Button Content="Change Model by code" Grid.Column="2" Grid.Row="4" Height="24" HorizontalAlignment="Left"
                    Name="btnChangeModel" VerticalAlignment="Top" Width="146" Margin="0,20,0,0" 
                    Command="{Binding Path=DataContext.SetInformation, ElementName=LayoutRoot}"
                    CommandParameter="{Binding Path=Name}" />
            <Button Content="Display" Grid.Column="1" Grid.Row="4" Height="23" HorizontalAlignment="Left"
                    Margin="0,21,0,0" Name="btnDisplay" VerticalAlignment="Top" Width="75" 
                    Command="{Binding Path=DataContext.GetInformation, ElementName=LayoutRoot}" 
                    CommandParameter="{Binding Path=Age}" />

            <Grid Name="DisplayGrid" DataContext="">
            </Grid>
        </Grid>
    </Grid>

MVVM模式中应尽量避免在code-behind文件中添加过多的代码,逻辑代码大多通过继承ICommand类实现,这里我们在Button的Command中添加Binding和BindingParameter,Button的单击代码实现写在PersonCommand类中,下面我们将会介绍如何创建这个类,同时大家可以注意到这里有一些Validation(验证)的东西,在TextBox中有两个比较特殊的属性,NotifyOnValidationError和ValidationOnExceptions,Notify事件一般用于在model类的读取和设置属性时发生的异常(通常是类型转换出现的异常)时会抛出错误,比方说你的TextBox中绑定了“年龄”这个属性,并且在Model类中将它设为Int类型,那么当你使用双向绑定时,给它输入字符串类型的数据或者为空值,那么此时在Model的Set中变回抛出异常,你可以说自己在Set里面进行类型检测处理,但是通过这种方式工作量会比较大,所以我们推荐使用NotifyOnValidationError这个属性,ValidationOnException这个属性意思为当数据字段在验证时抛出异常的情况下会触发BindValidationError这个事件,所以当数据发生异常时候我们可以定义自己的方法将错误呈现在页面上。这里我们将这两个属性均设置为true。BindValidationError事件后的“tbValidate”是我们设置在MainPage.xaml.cs文件中用于呈现错误一个方法。

由于我们需要在异常中为验证设定一个状态,保存在IsolatedStorageSettings中,这是系统为Silverlight应用程序开辟的一个存储空间,可以存放你需要的数据,我们这里存入验证的状态位,在Command类中将会检查此状态位,决定是否执行更新,下面是MainPage,xaml.cs的代码,比较简单,但是也比较重要

MainPage.xaml.cs

    public partial class MainPage : UserControl
    {
        private bool flag = true;
        private IsolatedStorageSettings appSetting;
        public MainPage()
        {
            InitializeComponent();
            appSetting = IsolatedStorageSettings.ApplicationSettings;
            if (!appSetting.Contains("validateFlag"))
            {
                appSetting.Add("validateFlag", this.flag);
            }
        }

        /// <summary>
        /// The method is used for catching binding exceptions.
        /// We can also store validate variable with IsolatedStorageSettings.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void tbValidate(object sender, ValidationErrorEventArgs e)
        {
            if (e.Action == ValidationErrorEventAction.Added)
            {
                (e.OriginalSource as Control).Background = new SolidColorBrush(Colors.Yellow);
                flag = false;
            }
            if (e.Action == ValidationErrorEventAction.Removed)
            {
                (e.OriginalSource as Control).Background = new SolidColorBrush(Colors.White);
                flag = true;
            }
            appSetting["validateFlag"] = flag;
        }

    }


 下面我们创建Model类,为了演示方便,我们只创建四个属性。此类命名为PersonModel,注意由于是双向绑定模式,我们需要继承INotifyPropertyChanged接口,该接口的作用是当Model数据发生改变时候,将会通知客户端,我们这里需要实现名为PropertyChangedEventHandler的事件和Changed方法,当属性被重新设置时需要调用这个Changed方法来改变客户端的值。

PersonModel.cs

    /// <summary>
    /// Person Modal class, contains name, age, telephone and comment properties.
    /// The Model implement INotifyPropertyChanged interface for notifying clients
    /// that properties has been changed.
    /// </summary>
    public class PersonModel : INotifyPropertyChanged
    {
        private string name;
        private int age;
        private string telephone;
        private string comment;
        private Regex regexInt = new Regex(@"^-?\d*{1}quot;);
        public event PropertyChangedEventHandler PropertyChanged;
        public PersonModel(string name, int age, string telephone, string comment)
        {
            this.name = name;
            this.age = age;
            this.telephone = telephone;
            this.comment = comment;
        }

        public void Changed(string newValue)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(newValue));
            }
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                if (value == string.Empty)
                    throw new Exception("Name can not be empty.");
                name = value;
                Changed("Name");
            }
        }

        public int Age
        {
            get
            {
                return age;
            }
            set
            {
                int outPut;
                if (int.TryParse(value.ToString(), out outPut))
                {
                    if (outPut < 0)
                        throw new Exception("Age must be greater than zero.");
                    age = outPut;//outPut.ToString();
                    Changed("Age");
                }
                else
                {
                    throw new Exception("Age must be an integer number.");
                }
            }
        }

        public string Telephone
        {
            get
            {
                return telephone;
            }
            set
            {
                if (value == string.Empty)
                    throw new Exception("Telephone can not be empty.");
                if (!regexInt.IsMatch(value))
                    throw new Exception("Telephone number must be comprised of numbers.");
                telephone = value;
                Changed("Telephone");
            }
        }

        public string Comment
        {
            get
            {
                return comment;
            }
            set
            {
                if (value == string.Empty)
                    throw new Exception("Comment can not be empty.");
                comment = value;
                Changed("Comment");
            }
        }
    }


接下来,我们创建ViewModel类,此类用于连接View和Model,并且被Command类调用,是MVVM模式的核心。创建一个class文件,命名为PersonViewModel,此类含有PersonModel的实例,用于当实体类数据发生改变时通知客户端程序,这里我们创建两个ICommand类型的属性,分别用于获取model数据和设置model数据。

PersonViewModel.cs

    /// <summary>
    /// The MainPage.xaml page bind this class with Grid control, PersonViewModel
    /// class is the ViewModel layer in MVVM pattern design, this class contains 
    /// a model instances and invoke Command class.
    /// </summary>
    public class PersonViewModel
    {
        public PersonModel person
        {
            get; 
            set;
        }

        public PersonViewModel()
        {
            this.person = new PersonModel("John", 1, "13745654587", "Hello");
        }

        public PersonViewModel(string name, int age, string telephone, string comment)
        {
            this.person = new PersonModel(name, age, telephone, comment);
        }

        public ICommand GetInformation
        {
            get
            {
                return new PersonCommand(this);
            }
        }

        public ICommand SetInformation
        {
            get
            {
                return new ChangeModelCommand(this);
            }
        }

        public void UpdatePerson(PersonModel entity)
        {
            MessageBox.Show(String.Format("Name: {0} \r\n Age: {1} \r\n Telephone: {2} \r\n Comment: {3}",
                person.Name,person.Age,person.Telephone,person.Comment),"Display Message", MessageBoxButton.OK);
        }


下面我们创建这两个Command类PersonCommand类和ChangeModelCommand类,因为在本程序中,数据无论在后台代码或者是前台UI中发生改变另一方也会随之改变,PersonCommand类的作用是当用户在点击Display按钮时出现对话框显示当前model的值,由于用户可以在TextBox中改变model的值(UI改变),所以我们需要建立一个ChangeModelCommand来响应Change Model by Code按钮,意为从object方向改变model的值,这样来阐述用户从两方面来改变Model值都是可以成功的。Command类需要注意的几个要点是这样的:

1. 继承ICommand接口。

2.实现CanExecute属性,该属性判断Command类中的Execute方法能否被执行。

3.实现Execute方法,Command类的主要逻辑代码。

4.包含ViewModel的一个实例,可以在Execute方法中直接调用ViewModel的方法,或是改变ViewModel中Model实例的数据。

PersonCommand.cs

    /// <summary>
    /// The class implements ICommand interface and displays a dialog box
    /// to show data.
    /// </summary>
    public class PersonCommand : ICommand
    {
        public PersonViewModel viewModel;
        public event EventHandler CanExecuteChanged;
        private IsolatedStorageSettings appSetting; 
        public PersonCommand(PersonViewModel view)
        {
            this.viewModel = view;
            appSetting = IsolatedStorageSettings.ApplicationSettings;
        }

        public bool CanExecute(object parameter)
        {
            bool validateFlag = false;
            if (appSetting.Contains("validateFlag"))
            {
                validateFlag = (bool)appSetting["validateFlag"];
            }
            if (validateFlag)
            {
                return true;
            }
            else
            {
                return false;
            }
        }        

        public void Execute(object parameter)
        {
            viewModel.UpdatePerson(viewModel.person);        
        }
    }

 

ChangeModelCommand.cs

    public class ChangeModelCommand: ICommand
    {
        /// <summary>
        /// This class is used to change model instance via code, and view layer will update 
        /// when background data source has been changed.
        /// </summary>
        private PersonViewModel viewModel;
        public event EventHandler CanExecuteChanged;
        public ChangeModelCommand(PersonViewModel viewModel)
        {
            this.viewModel = viewModel;
        }

        public bool CanExecute(object parameter)
        {
            if (parameter.ToString() != string.Empty)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Change Model instance by Execute method.
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            PersonModel model;
            model = viewModel.person;
            model.Name = "Default Name";
            model.Age = 0;
            model.Telephone = "11111111111";
            model.Comment = "Default Comment";
        }
    }

至此,项目创建完毕,按Ctrl+F5看看结果吧。
 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值