Elijah

治愈系代码研究基地
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[传智播客学习日记]使用事件来制作登陆验证控件

Posted on 2011-10-30 19:58  Elijah  阅读(737)  评论(2编辑  收藏  举报

昨天是在传智播客学习的第九天,我们威武的赵晓虎老师讲解了事件的应用,下面结合自己的一些经验和理解,把事件在自定义控件中的效用总结了一下,欢迎指正!

事件,就是封装后的委托,如果把委托看成字段,那事件就是属性了,其实就是把委托做了个阉割而已,阉割后的委托只能+=和-=。

这次要实现的效果是,自己定义一个控件,上面有两个textbox,分别叫txtLoginId和txtLoginPwd,还有个叫btmLogin的按钮,控件的用途是让用户输入的用户名和密码,然后点登陆。如果用户名和密码正确,登陆成功,则两个textbox变为绿色,失败则变为红色。

首先建立一个用户自定义控件,把零部件拖进去。然后建立一个客户程序(主程序)的窗口Form1,把刚定义好的这个自定义控件拖进去。

我们给验证控件的文件起名叫UcLogin.cs。由于程序是在点下按钮时触发的,所以要针对控件中按钮的btmLogin_Click事件编程。首先在btmLogin_Click中定义两个变量来存储两个输入框中的内容。

1 string loginId = txtLoginId.Text.Trim();
2 string loginPwd = txtLoginPsd.Text;

(不直接把两个Text传进去也是一种解耦。)

由于用户名和密码很可能是从数据库或者程序外部获得的,所以为了程序的可扩展性,绝对不能把判断逻辑写死在控件当中,控件只不过是一个控件而已,具体要拿来干嘛,还是由用户来定的。所以要使用事件这个知识点,由客户程序(Form1)来触发这个事件的代码。

既然要用到事件,就需要声明一个委托(因为事件就是封装后的委托)。新建文件Delegates.cs,我们要定义一个委托,为了把山寨做到最佳品质,加入object sender来装X!代码如下:

1 public delegate void UserInputValidatingDelegate(object sender, ______);

这个委托的返回值为void,因为C#自带的那些事件都是void,事件不需要返回值的,高仿嘛!在这里,我们不但需要把用户输入的用户名和密码传进来,也需要把判断是否成功的bool量传进来。由于我们要传三个参数,不妨把这三个参数封装成一个类免得啰嗦(因为实际应用中这里可能不是3个参数,而是30个!),在这个横线的地方传一个实例进去。

在新文件中定义一个public类MyEventArgs:

1 public class MyEventArgs
2 {
3 public string id { get; set; }
4 public string pwd { get; set; }
5 public bool IsValid { get; set; }
6 }


于是这个完整的委托就是:

1 public delegate void UserInputValidatingDelegate(object sender, EventArgs e);

(是不是跟C#自带的超级高仿啊?)

这样一来我们就可以在控件里取得用户输入的字符串之后添加一个事件了。首先声明事件:

1 public event UserInputValidatingDelegate Validating;

需要提的一嘴是,这里起的Validating跟系统自带的重名(还是山寨),当然你可以用关键字new一下,也可以不new。

然后,一个好习惯,

1 if (Validating != null)

如果事件不为空的话,我们就可以在这个if块里执行我们的事件处理代码了。

首先要new一个MyEventArgs对象eventArgs来接受用户输入的信息:

1 MyEventArgs eventArgs = new MyEventArgs();
2 eventArgs.id = loginId;
3 eventArgs.pwd = loginPwd;
4 eventArgs.IsValid = false;

这里一定是默认false登录失败!

传进用户输入的信息之后,由控件进行判断:

 1 if (eventArgs.IsValid == true)
2 {
3 this.txtLoginId.BackColor = Color.Green;
4 this.txtLoginPsd.BackColor = Color.Green;
5 }
6 else
7 {
8 this.txtLoginId.BackColor = Color.Red;
9 this.txtLoginPsd.BackColor = Color.Red;
10 }


但是这个eventArgs.IsValid怎么就等于true了呢?关键就在这,在上面两段代码中间,我们需要一套“判断方法”来取得IsValid的值,但是正如一开始所说的,我们不应该把判断逻辑写在控件里,这样程序就“写死了”,事件和委托也就没有意义了,控件也没有意义了。也就是说中间少的这部分代码段应该交给客户程序(控件的使用者)来完成。

于是,我们应该把MyEventArgs类的eventArgs对象整体发送给主窗体Form1,由客户程序来处理。

还记得前面这一行吗:

1 public event UserInputValidatingDelegate Validating;

这声明了一个事件,现在,在上面两端代码的中间,我们插入代码:

1 Validating(this, eventArgs);

这里的this其实就是object sender,是“这个控件”的意思。
有没有发现这一行事件调用就相当于一个“代码段的占位符”呢?程序运行到这行,就跳到别的地方去了,完事后再回来。

假定我们这里用户名和密码是admin和88888888(这只是个例子),在主窗体的Form1_Load方法中,首先我们要给事件里“打包”一个方法进去,这个方法就是刚才所说的“判断方法”。

1 ucLogin1.Validating += new UserInputValidatingDelegate(ucLogin1_Validating);


ucLogin1_Validating就是我们在主窗体里定义的判断方法,

1 void ucLogin1_Validating(object sender, MyEventArgs e)
2 {
3 if (e.id.ToLower() == "admin" && e.pwd.Equals("88888888"))
4 {
5 e.IsValid = true;
6 }
7 }


至此大功告成!在点击控件按钮后,首先把值拿进控件内部来,然后控件把这些信息封装,通过事件发送给客户程序(主窗体)来处理,判断逻辑写在主窗体里,判断完了(把MyEventArgs e里的IsValid值敲定了),再丢给控件,然后控件再负责给自己变色。

这套思路的精华就在于,如果说主程序是一根晾衣服杆的话,那么主程序里给控件的事件绑定方法的那一行就相当于往晾衣服杆上装了一个“衣服架”,至于如何判断,就是所挂的“毛巾”。如果客户的判断逻辑需要变动,完全不需要修改控件的程序,实现了“对修改封闭,对扩展开放”的面向对象原则。事件在这里把程序的主逻辑和次逻辑分开,把各个对象的职能抽离开来,实现单一化。试想如果不用事件,把程序写死的话,一旦需求有变动或者进行版本升级,整个程序就需要重新编译部署,就相当于给板凳刷漆要把板凳全拆开一样。利用委托和事件,大大降低了程序后期维护的成本,这也是为什么要使用面向对象的原因之一。

完整代码:

 1 //From1.cs
2 namespace 验证控件
3 {
4 public partial class Form1 : Form
5 {
6 public Form1()
7 {
8 InitializeComponent();
9 }
10
11 private void Form1_Load(object sender, EventArgs e)
12 {
13 ucLogin1.Validating += new UserInputValidatingDelegate(ucLogin1_Validating);
14 }
15
16 void ucLogin1_Validating(object sender, MyEventArgs e)
17 {
18 if (e.id.ToLower() == "admin" && e.pwd.Equals("88888888"))
19 {
20 e.IsValid = true;
21 }
22 }
23 }
24 }
25
26 //Delegates.cs
27 namespace 验证控件
28 {
29 public delegate void UserInputValidatingDelegate(object sender, MyEventArgs e);
30 }
31
32 //MyEventArgs.cs
33 namespace 验证控件
34 {
35 public class MyEventArgs
36 {
37 public string id { get; set; }
38 public string pwd { get; set; }
39 public bool IsValid { get; set; }
40 }
41 }
42
43 //UcLogin.cs
44 namespace 验证控件
45 {
46 public partial class UcLogin : UserControl
47 {
48 public UcLogin()
49 {
50 InitializeComponent();
51 }
52
53 public event UserInputValidatingDelegate Validating;
54
55 private void btmLogin_Click(object sender, EventArgs e)
56 {
57 string loginId = txtLoginId.Text.Trim();
58 string loginPwd = txtLoginPsd.Text;
59
60 if (Validating != null)
61 {
62 MyEventArgs eventArgs = new MyEventArgs();
63 eventArgs.id = loginId;
64 eventArgs.pwd = loginPwd;
65 eventArgs.IsValid = false;
66
67 Validating(this, eventArgs);
68
69 if (eventArgs.IsValid == true)
70 {
71 this.txtLoginId.BackColor = Color.Green;
72 this.txtLoginPsd.BackColor = Color.Green;
73 }
74 else
75 {
76 this.txtLoginId.BackColor = Color.Red;
77 this.txtLoginPsd.BackColor = Color.Red;
78 }
79 }
80
81
82 }
83 }
84 }