SilverLight Tip 1 : Validation

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

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

image

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

2:一般情况下的验证

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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,而这个实体类,是在服务器端的,形如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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类型在客户端的代理形式为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/// <summary>
/// The 'User' entity class.
/// </summary>
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中建立一一映射的关系,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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添加错误项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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接口。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[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

posted @   陆敏技  阅读(1039)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
Web Counter
Coupon for Contacts
点击右上角即可分享
微信分享提示