天天看點

SilverLight Tip 1 : Validation

1:SL的資料驗證和WPF的不同

首先,很遺憾,SL中不再存在ValidationRules,要在SL中綁定時驗證資料,并且顯示在UI,隻能依賴于NotifyOnValidationError=True, ValidatesOnExceptions=True這兩個屬性,如下:

SilverLight Tip 1 : Validation
如果要檢視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效果圖:

SilverLight Tip 1 : Validation

到此位置的源碼下載下傳為:

SilverlightApplication20110618.zip

5:解決方案之使用ValidationSummary

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

SilverLight Tip 1 : Validation

接着,我們讓前台的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));
            }
        }

    }      

最後的運作結果為:

SilverLight Tip 1 : Validation

備注:要讓實體類在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
SilverLight Tip 1 : Validation

本文基于

Creative Commons Attribution 2.5 China Mainland License

釋出,歡迎轉載,演繹或用于商業目的,但是必須保留本文的署名

http://www.cnblogs.com/luminji

(包含連結)。如您有任何疑問或者授權方面的協商,請給我留言。