winform+wcf搭建商户管理系统(2)-用户登录界面设计
1.UI设计
winform不像Wpf那样,可以通过各层的叠加,来实现各种炫酷的特效。在网上找了一个三方类库,很好的解决了这个问题。
如图,通过三方控件LayeredSkin.dll实现的一个类似于QQ的登录界面
首先引用LaryeredSkin,然后新增一个Form窗体(这里命名为FormLogin),然后让它继承LayeredForm
public partial class FormLogin : LayeredForm
这里的树叶和云彩都能够移动(通过Timer+GDI+实现)
private void FormLogin_Load(object sender, EventArgs e) { yezi = new Bitmap(90, 80);//先把叶子画在稍微大一点的画布上,这样叶子旋转的时候才不会被裁掉一部分 using (Graphics g = Graphics.FromImage(yezi)) { g.DrawImage(Resources.yezi3, 10, 0); } timer1.Start(); //读取账户信息 string result = AccountHelper.ReadAccount(); if (string.IsNullOrEmpty(result) == false) { string[] temp = result.Split(','); if (temp != null && temp.Length == 2) { txtAccount.Text = temp[0]; txtPassword.Text = temp[1]; } } }
protected override void OnLayeredPaint(LayeredSkin.LayeredEventArgs e) { Graphics g = e.Graphics; if (cloudX > this.Width - Cloud.Width) {//云的飘动 cloudX = 0; } else { cloudX += 0.5f; } g.DrawImage(Cloud, cloudX, 0);//把云绘制上去 if (angle > 10) {//控制旋转方向 RotationDirection = false; } if (angle < -10) { RotationDirection = true; } if (RotationDirection) { angle += 1; } else { angle -= 1; } using (Image temp = LayeredSkin.ImageEffects.RotateImage(yezi, angle, new Point(25, 3))) { g.DrawImage(temp, 140, 70);//绘制叶子 } base.OnLayeredPaint(e); } private void timer1_Tick(object sender, EventArgs e) { LayeredPaint(); GC.Collect(); }
加上窗体的移动事件,来实现窗体的拖动
private void FormMoveMouseDown(object sender, MouseEventArgs e) { LayeredSkin.NativeMethods.MouseToMoveControl(this.Handle); }
到这里UI的设计就已经完成了
2.登录和注册逻辑
一般的登录窗体都会有一个窗口的登录和注册逻辑(查询和新增)
1.登录逻辑
首先在客户端进行初步的空值或者字符验证
if (string.IsNullOrEmpty(txtAccount.Text)) { layeredLabel5.Text = "用户名不能为空!"; layeredLabel5.ForeColor = Color.Red; txtAccount.Focus(); return; } if (string.IsNullOrEmpty(txtPassword.Text)) { layeredLabel5.Text = "密码不能为空!"; layeredLabel5.ForeColor = Color.Red; txtPassword.Focus(); return; }
然后这里调用服务端的账号密码验证方法,来确定登录是否正确,同时返回用户信息,以供后面调用
bool result = _client.TLoginCheckLogin(out message, out dto, txtAccount.Text, txtPassword.Text);
2.保存密码
这里通过本地文本的读写来实现密码的保存和自动填充功能
//保存账号信息 if (cbRemind.Checked) AccountHelper.WriteAccount(txtAccount.Text, txtPassword.Text);
在用户的加载事件中,已经贴出了自动读取保存的用户信息
3.用户的注册
这里分为员工信息和账号信息两类信息。(这两类信息在服务端表现为一一对应关系)
public class T_Login : Entity { private string _loginid; public string LoginId { get { return _loginid; } set { this.Id = value; this._loginid = value; } } // LoginId (Primary key) public string LoginName { get; set; } // LoginName public string LoginPsw { get; set; } // LoginPsw public string Role { get; set; } // Role public string EmployeeId { get; set; } // EmployeeId public virtual T_Employee TEmployee { get; set; } // FK_T_Login_T_Employee 外键关系 }
对于上节提到的DDD架构的业务逻辑而言,我们可以通过先增加外键表(员工信息表),再增加主键表(登录信息表)来新增登录信息。也可以应用架构本身的聚合根特性,来同时添加。第一种需要调用2次服务,第二种只需要在服务中传两个实体,然后聚合到主键实体,Commit就行了。
第一种方法
private void btnRegist_Click(object sender, EventArgs e) { string message = string.Empty; if (!ConfirmNull(out message)) { labelWarning.Text = message; labelWarning.ForeColor = Color.Red; return; } if ((bool)txtAccount.Tag == false) { TEmployeeDTO emp = new TEmployeeDTO(); emp.EmployeeId = Guid.NewGuid().ToString(); emp.EmployeeName = txtName.Text; emp.EmployeePhone = txtTel.Text; emp.EmployeeSex = coboSex.Text; emp.EntryData = dateTimePicker1.Value; emp.IdCard = txtIdCard.Text.Replace("-", ""); emp.EmployeeAge = int.Parse(dmpAge.Text); emp.EntryImage = pictureStreamBox1.GetPictureStream(); bool result = _client.TEmployeeAdd(emp); TLoginDTO login = new TLoginDTO(); login.EmployeeId = emp.EmployeeId; login.LoginId = Guid.NewGuid().ToString(); login.LoginName = txtAccount.Text; login.LoginPsw = txtPsw.Text; login.Role = coboRole.Text; result &= _client.TLoginAdd(login); if (result) { labelWarning.ForeColor = Color.Green; labelWarning.Text = "注册成功!"; _account = login.LoginName + "," + login.LoginPsw; Thread.Sleep(2000); this.DialogResult = DialogResult.OK; } else { labelWarning.Text = "注册失败!"; labelWarning.ForeColor = Color.Red; } }
第二种方法
public bool Add(TLoginDTO dto,TEmployeeDTO employeeDTO) { try { var entity = dto.ProjectedAs<T_Login>(); entity.TEmployee = employeeDTO.ProjectedAs<T_Employee>(); if (_repository.Insert(entity) > 0) { return true; } else { return false; } } catch (Exception ex) { throw ex; } }
这里推荐使用第二种方法。效率高,同时不易产生脏数据
3.线程之间的关系
winform界面在程序入口Main函数中启用登录界面的时候,就将登录界面设定为主线程。登录成功之后,需要将登录界面隐藏,显示出我们需要的主界面。
所以,不能直接将登录界面干掉(Close),不然,整个程序将完全关闭,相当于Application.Exit().
有两种方法可以解决:1.关闭登录界面之前,通过System.Diagnostics.Process.Start来启动MainForm,然后关闭。2.将登录界面Hide,在MainForm关闭的时候,将主线程关闭。
4.总结