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.總結