1:SL的数据验证和WPF的不同
首先,很遗憾,SL中不再存在ValidationRules,要在SL中绑定时验证数据,并且显示在UI,只能依赖于NotifyOnValidationError=True, ValidatesOnExceptions=True这两个属性,如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuYGO1UWOzkDMjBDNjRjZzADOzUDO2IGOkhDN2gjMzYDMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
》。
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效果图:
到此位置的源码下载为:
SilverlightApplication20110618.zip5:解决方案之使用ValidationSummary
使用ValidationSummary,需要我们引入程序集System.Windows.Controls.Data.Input,在UI前台,我们需要安置一个ValidationSummary:
接着,我们让前台的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));
}
}
}
最后的运行结果为:
备注:要让实体类在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 Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名
http://www.cnblogs.com/luminji(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。