SilverLight Tip 1 : Validation

简介: 1:SL的数据验证和WPF的不同 首先,很遗憾,SL中不再存在ValidationRules,要在SL中绑定时验证数据,并且显示在UI,只能依赖于NotifyOnValidationError=True, ValidatesOnExceptions=True这两个属性,如下: 如果要查看WPF的数据验证的方式,可以查看该文《WPF快速指导5:验证》。

1:SL的数据验证和WPF的不同

首先,很遗憾,SL中不再存在ValidationRules,要在SL中绑定时验证数据,并且显示在UI,只能依赖于NotifyOnValidationError=True, ValidatesOnExceptions=True这两个属性,如下:

image

如果要查看WPF的数据验证的方式,可以查看该文《WPF快速指导5:验证》。

2:一般情况下的验证

一般情况下,UI绑定数据类型的属性,如在上图中,绑定的就是Name和Age,它在UI的VIEWMODEL中,如下:

    public class MainPageVM : INotifyPropertyChanged
    {
        public MainPageVM()
        {
        }

        private string name = "luminji";

        public string Name
        {
            get { return name; }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new Exception("姓名不能为空");
                }
                name = value;
                OnPropertyChanged("Name");
            }
        }

        private int age = 1;

        public int Age
        {
            get { return age; }
            set
            {
                if (value > 100)
                {
                    throw new Exception("年龄必须小与100");
                }
                age = value;
                OnPropertyChanged("Age");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler pceh = PropertyChanged;
            if (pceh != null)
            {
                pceh(this, new PropertyChangedEventArgs(propertyName));
            }
        }
       
    }

采用UI直接绑定VM的属性,如果Age>100,则UI会提示出现输入有误。

 

3:绑定实体类型

不过,如果属性很多,我们就会考虑绑定实体类型。如User,而这个实体类,是在服务器端的,形如:

    public class User
    {
        private string name = "luminji";

        public string Name
        {
            get { return name; }
            set
            {
                name = value;
            }
        }

        private int age = 1;

        public int Age
        {
            get { return age; }
            set
            {
                age = value;
            }
        }
    }

比如,我们使用的是Ria Service。Ria Service有可能是从DAL层获取数据并开放接口给客户端。我们都知道,客户端的代码都是自动生成的,自动生成的代码在SL项目的Generated_Code目录下的(SL项目名).Web.g.cs文件下。所以,最终User类型在客户端的代理形式为:

    /// <summary>
    /// The 'User' entity class.
    /// </summary>
    [DataContract(Namespace="http://schemas.datacontract.org/2004/07/SilverlightApplication3.Web.Model")]
    public sealed partial class User : Entity
    {
        
        private int _age;
        
        private string _name;
        
        #region Extensibility Method Definitions

        /// <summary>
        /// This method is invoked from the constructor once initialization is complete and
        /// can be used for further object setup.
        /// </summary>
        partial void OnCreated();
        partial void OnAgeChanging(int value);
        partial void OnAgeChanged();
        partial void OnNameChanging(string value);
        partial void OnNameChanged();

        #endregion
        
        
        /// <summary>
        /// Initializes a new instance of the <see cref="User"/> class.
        /// </summary>
        public User()
        {
            this.OnCreated();
        }
        
        /// <summary>
        /// Gets or sets the 'Age' value.
        /// </summary>
        [DataMember()]
        public int Age
        {
            get
            {
                return this._age;
            }
            set
            {
                if ((this._age != value))
                {
                    this.OnAgeChanging(value);
                    this.RaiseDataMemberChanging("Age");
                    this.ValidateProperty("Age", value);
                    this._age = value;
                    this.RaiseDataMemberChanged("Age");
                    this.OnAgeChanged();
                }
            }
        }
        
        /// <summary>
        /// Gets or sets the 'Name' value.
        /// </summary>
        [DataMember()]
        [Editable(false, AllowInitialValue=true)]
        [Key()]
        [RoundtripOriginal()]
        public string Name
        {
            get
            {
                return this._name;
            }
            set
            {
                if ((this._name != value))
                {
                    this.OnNameChanging(value);
                    this.ValidateProperty("Name", value);
                    this._name = value;
                    this.RaisePropertyChanged("Name");
                    this.OnNameChanged();
                }
            }
        }
        
        /// <summary>
        /// Computes a value from the key fields that uniquely identifies this entity instance.
        /// </summary>
        /// <returns>An object instance that uniquely identifies this entity instance.</returns>
        public override object GetIdentity()
        {
            return this._name;
        }
    }

这个时候,如果我们在UI中继续绑定这个实体类型,势必会丢掉UI异常通知的行为,因为,显然,我们不能跑到这个客户端的代理类中throw new Exception("姓名不能为空"); ,那会在下一次代码自动生成的时候被覆盖掉。

4:解决方案之建立映射

一种解决方案是我们的UI仍旧不绑定实体类型,而是为类型的属性在ViewModel中建立一一映射的关系,如下:

    public class MainPageVM : INotifyPropertyChanged
    {
        public MainPageVM()
        {
            user = new User();
            serviceUser = new DomainServiceUser();
            serviceUser.Load<User>(serviceUser.GetAUserQuery(), new Action<System.ServiceModel.DomainServices.Client.LoadOperation<User>>(this.GetAUserCallBack), null);
        }

        DomainServiceUser serviceUser;
        User user;
        
        
        void GetAUserCallBack(LoadOperation<User> arg)
        {
            user = (arg.Entities as IList<User>)[0];
            Name = user.Name;
            Age = user.Age;
        }


        public string Name
        {
            get { return user.Name; }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new Exception("姓名不能为空");
                }
                user.Name = value;
                OnPropertyChanged("Name");
            }
        }


        public int Age
        {
            get { return user.Age; }
            set
            {
                if (value > 100)
                {
                    throw new Exception("年龄必须小与100");
                }
                user.Age = value;
                OnPropertyChanged("Age");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler pceh = PropertyChanged;
            if (pceh != null)
            {
                pceh(this, new PropertyChangedEventArgs(propertyName));
            }
        }
       
    }

UI效果图:

image

到此位置的源码下载为:SilverlightApplication20110618.zip

5:解决方案之使用ValidationSummary

使用ValidationSummary,需要我们引入程序集System.Windows.Controls.Data.Input,在UI前台,我们需要安置一个ValidationSummary:

image

接着,我们让前台的ValidationSummary的Errors赋值给VM。

    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            ViewModel = new MainPageVM(this.vs.Errors);
        }

        public MainPageVM ViewModel
        {
            get
            {
                return (MainPageVM)this.DataContext;
            }
            set
            {
                this.DataContext = value;
            }
        }

        private void btnSave_Click(object sender, RoutedEventArgs e)
        {

        }
    }

我们还需要为UI绑定一些Command,以便在需要验证输入的时候,让VM去判断是否有错误发生。一旦有错误发生,则为Errors添加错误项。

    public class MainPageVM : INotifyPropertyChanged
    {
        public MainPageVM(ObservableCollection<ValidationSummaryItem> errors)
        {
            m_errors = errors;
            Click = new ActionCommand(this.OnClick);
            serviceUser = new DomainServiceUser();
            serviceUser.Load<User>(serviceUser.GetAUserQuery(), new Action<System.ServiceModel.DomainServices.Client.LoadOperation<User>>(this.GetAUserCallBack), null);
            //serviceUser.Load<User>(serviceUser.GetAUserQuery(), LoadBehavior.RefreshCurrent, new Action<System.ServiceModel.DomainServices.Client.LoadOperation<User>>(this.GetAUserCallBack), null);
        }

        DomainServiceUser serviceUser;
        User user;
        ObservableCollection<ValidationSummaryItem> m_errors;

        void GetAUserCallBack(LoadOperation<User> arg)
        {
            user = (arg.Entities as IList<User>)[0];
            OnPropertyChanged("User");
        }

        public User User
        {
            get { return user; }
            set
            {
                user = value;
                OnPropertyChanged("User");
            }
        }

        public ICommand Click { get; private set; }

        public void OnClick(object arg)
        {
            m_errors.Clear();
            if (User.Age > 100)
            {
                m_errors.Add(new ValidationSummaryItem("年龄不能大雨100"));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler pceh = PropertyChanged;
            if (pceh != null)
            {
                pceh(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    }

最后的运行结果为:

image

备注:要让实体类在SL端支持EDIT,必须要让DomainService针对该实体类支持开放GUID接口。如下:

    [EnableClientAccess()]
    public class DomainServiceUser : DomainService
    {
        [Query]
        public IList<User> GetUsers()
        {
            return new List<User>()
            {
                new User() { Name = "huzhonghua", Age = 99 }
            };
        }

        public User GetAUser()
        {

            return new User() { Name = "luminji", Age = 98 };
        }

        [Delete]
        public void DeleteUser(User user)
        {
            throw new NotImplementedException();
        }

        [Insert]
        public void InsertUser(User user)
        {
            throw new NotImplementedException();
        }

        [Update]
        public void UpdateUser(User user)
        {
            throw new NotImplementedException();
        }

    }

本文源码下载:SilverlightApplication2001061902.zip

Creative Commons License本文基于 Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 http://www.cnblogs.com/luminji(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。
目录
相关文章
|
12月前
|
JavaScript 架构师
关于 SAP UI5 SimpleForm 控件里的 ColumnsL 和 labelSpanXL 的测试
关于 SAP UI5 SimpleForm 控件里的 ColumnsL 和 labelSpanXL 的测试
|
11月前
|
容器
SAP UI5 Form 和 Simple Form 的设计规范
SAP UI5 Form 和 Simple Form 的设计规范
very important tip - when to add / in binding path Fiori
very important tip - when to add / in binding path Fiori
100 0
very important tip - when to add / in binding path Fiori
SAP UI5 Simple form rendering
Created by Wang, Jerry, last modified on Mar 05, 2016
135 0
SAP UI5 Simple form rendering
how is SAP UI5 Model.setProperty implemented
Created by Wang, Jerry, last modified on Jul 09, 2015
103 0
how is SAP UI5 Model.setProperty implemented
|
XML 数据格式
SAP Form print tool
Sent: Thursday, October 30, 2008 8:05 PM
SAP Form print tool
SAP UI5 binding syntax - model name + + attribute name
Created by Wang, Jerry, last modified on May 22, 2015
122 0
SAP UI5 binding syntax - model name + + attribute name
SAP UI5 control id generation by jQuery
Created by Wang, Jerry, last modified on May 21, 2015
151 0
SAP UI5 control id generation by jQuery
SAP UI5 Label related stuff and accessibility研究
Created by Wang, Jerry, last modified on May 15, 2015
SAP UI5 Label related stuff and accessibility研究
|
C#
WPF中使用Data Annotations验证Model
原文:WPF中使用Data Annotations验证Model .NET Framework中System.ComponentModel.DataAnnotations提供了很多属性来验证对象的属性。
1159 0