【基於WinForm+Access區域網共享資料庫的專案總結】之篇一:WinForm開發總體概述與技術實現
【小記】:最近基於WinForm+Access資料庫完成一個法律諮詢管理系統。本系統要求類似網頁後臺管理效果,並且基於區域網內,完成多客戶端操作同一資料庫,根據許可權不同分別執行不同功能模組。核心模組為級聯統計型別管理、資料庫諮詢資料扇形統計、樹的操作、諮詢資料的管理、手寫分頁、Excel資料的匯出、多使用者操作伺服器資料等。並支援多使用者同時操作,遠端連線資料庫且對資料IP資訊的修改。開發過程中特別對介面的要求和事後多使用者操作顯得略為麻煩。自此,本專案得以完善交付,然後對其進行小結。依舊採用整個框架認識,核心知識逐個梳理分析,以便於二次開發和需要之程式設計師共享。
篇一:WinForm開發總體概述與技術實現
【開篇】執行效果圖示和功能介紹
1 登陸資訊圖示和功能概述
註解:本登陸介面可實現遠端連線資料庫,點選:伺服器設定窗體進行向下展開,在IP地址配置處輸入正確的IP地址(前提需要遠端連線伺服器)。其原理為,App.config檔案進行遠端連線資料庫配置,通過連線IP地址下共享資料夾中的Access資料檔案訪問。具體遠端實現程式碼在篇二進行展開。並且通過對App.config檔案操作進行IP地址的本地儲存。
2 主窗體功能模組圖示與功能概述
註解:管理員登陸進入主頁面可以實現對系統的諮詢管理、列印使用者報表和諮詢報表、對多組合資料扇形圖的實現、使用者管理、諮詢型別級聯修改和刪除、以及個人資訊的修改。律師進入頁面後只能對我要諮詢操作。整個資料控制元件的顯示效果和佈局方式,手寫分頁、以及如何實現類似網頁版效果,後面逐步解析。
【總結】核心知識點梳理
1 如何實現窗體摺疊效果?如何讀取連線字串的IP地址?
if (this.Height == 325) { button2.Text = "伺服器設定 ↑"; this.Height = 465; } else { button2.Text = "伺服器設定 ↓"; this.Height = 325; }
注:通過設定窗體高度和文字資訊進行摺疊
//把連線資料庫的字串分割,並填充至文字框 try { XDocument doc = XDocument.Load(file); //Provider=Microsoft.ACE.OLEDB.12.0;Data Source=//10.130.16.136/db/Legal.accdb string connstring = doc.Elements("configuration").Elements("connectionStrings").Elements("add").Attributes("connectionString").FirstOrDefault().Value; string[] array1 = connstring.Split(';'); string str = array1[1].Split('=')[1];// ->//10.130.16.136/db/Legal.accdb string[] newarray = str.Split('/'); this.textBox3.Text = newarray[2].Split('/')[0].ToString(); } catch { }
注:通過操作App.config檔案,並且進行字串擷取獲取IP地址
-------------------------------------------------------------------------------------------------------------------------------------------------------
2 如何進行連線遠端資料庫IP地址的設定與儲存?
if (textBox3.Text != "") {
string file = @"C:\db\App.config";
XDocument doc = XDocument.Load(file); //sql資料庫資料庫配置 //string connstring = "initial catalog=Highway;Data Source=" + this.textBox3.Text + ";uid=" + this.textBox4.Text + ";pwd=" + this.textBox5.Text + ";"; //access資料庫遠端配置 //Provider=Microsoft.ACE.OLEDB.12.0;Data Source=//10.130.16.136/db/Legal.accdb string connstring = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=//" + textBox3.Text.ToString()+ "/db/Legal.accdb"; doc.Elements("configuration").Elements("connectionStrings").Elements("add").Attributes("connectionString").FirstOrDefault().SetValue(connstring); doc.Save(file); //由於DBHelperSQL中的ConnectionString(公共靜態變數)未被修改引發的bug XDocument xdoc = XDocument.Load(file); string val = doc.Elements("configuration").Elements("connectionStrings").Elements("add").Attributes("connectionString").FirstOrDefault().Value; SQLHelper.connstr = val; }
注:在文字框中修改IP地址,然後通過操作本地App.config檔案,進行修改儲存操作。但是儲存後要及時修改配置連線字串中的資料。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 如何進行窗體間傳值?
子窗體:
if (dt.Rows[0][1].ToString() == textBox1.Text.ToString() && dt.Rows[0][2].ToString() == textBox2.Text.ToString()) { int id = Convert.ToInt32(dt.Rows[0]["ID"].ToString()); string type = dt.Rows[0][3].ToString(); string name = this.textBox1.Text.ToString(); ; this.Hide(); Fm_Main fm = new Fm_Main(id, name, type); fm.Show(); this.Hide(); }
父窗體:
#region 建構函式接收使用者名稱、管理許可權
private string Name;
private string Type;
private int ID;
public Fm_Main() { }
public Fm_Main(int id,string name, string type)
{
InitializeComponent();
this.Name = name;
this.Type = type;
this.ID = id;
}
#endregion
注:在服父窗體寫個建構函式,並且定義其屬性,然後傳參賦值即可。但是在建構函式中新增如下語句: InitializeComponent();否則執行不是自己預期的效果。
4 父窗體如何作為容器,顯示子窗體?
//繪製的方式OwnerDrawFixed表示由窗體繪製大小也一樣 this.tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed; this.tabControl1.Padding = new System.Drawing.Point(CLOSE_SIZE, CLOSE_SIZE); this.tabControl1.DrawItem +=new DrawItemEventHandler(tabControl1_DrawItem); this.tabControl1.MouseDown += new MouseEventHandler(tabControl1_MouseDown);
#region 繪製tabControl的關閉按鈕,以及關閉功能的實現 private void tabControl1_DrawItem(object sender, DrawItemEventArgs e) { try { Rectangle myTabRect = this.tabControl1.GetTabRect(e.Index); //先新增TabPage屬性 e.Graphics.DrawString(this.tabControl1.TabPages[e.Index].Text, this.Font, SystemBrushes.ControlText, myTabRect.X + 4, myTabRect.Y + 4); //再畫一個矩形框 using (Pen p = new Pen(Color.White)) { myTabRect.Offset(myTabRect.Width - (CLOSE_SIZE + 3), 2); myTabRect.Width = CLOSE_SIZE; myTabRect.Height = CLOSE_SIZE; e.Graphics.DrawRectangle(p, myTabRect); } //填充矩形框 Color recColor = e.State == DrawItemState.Selected ? Color.White : Color.White; using (Brush b = new SolidBrush(recColor)) { e.Graphics.FillRectangle(b, myTabRect); } //畫關閉符號 using (Pen objpen = new Pen(Color.Black)) { //使用圖片 Bitmap bt = new Bitmap(image); Point p5 = new Point(myTabRect.X, 4); e.Graphics.DrawImage(bt, p5); //e.Graphics.DrawString(this.MainTabControl.TabPages[e.Index].Text, this.Font, objpen.Brush, p5); } e.Graphics.Dispose(); } catch (Exception) { } } //滑鼠作用事件設定 void tabControl1_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { int x = e.X, y = e.Y; //計算關閉區域 Rectangle myTabRect = this.tabControl1.GetTabRect(this.tabControl1.SelectedIndex); myTabRect.Offset(myTabRect.Width - (CLOSE_SIZE + 3), 2); myTabRect.Width = CLOSE_SIZE; myTabRect.Height = CLOSE_SIZE; //如果滑鼠在區域內就關閉選項卡 bool isClose = x > myTabRect.X && x < myTabRect.Right && y > myTabRect.Y && y < myTabRect.Bottom; if (isClose == true) { this.tabControl1.TabPages.Remove(this.tabControl1.SelectedTab); } } } const int CLOSE_SIZE = 15; //tabPage標籤圖片 Bitmap image = new Bitmap(@"C:\db\btnClose.png"); #endregionView Code
注:首先在窗體載入方法加入以上事件,並且對選項卡關閉進行畫圖。
5 子窗體怎樣載入在父窗體的Panel面板中,且類似網頁效果?
//使用者管理 private void Moth_User() { if (ErgodicModiForm("tbuse", tabControl1) == true) { tbUser = new TabPage("使用者管理"); tbUser.Name = "tbuse"; tabControl1.Controls.Add(tbUser); UserManage form = new UserManage(); form.TopLevel = false; form.FormBorderStyle = FormBorderStyle.None; form.WindowState = FormWindowState.Maximized; form.BackColor = Color.White; form.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; form.Show(); tbUser.Controls.Add(form); } tabControl1.SelectedTab = tbUser; } //判斷選項卡是否存在 private Boolean ErgodicModiForm(string MainTabControlKey, TabControl objTabControl) { //遍歷選項卡判斷是否存在該子窗體 foreach (Control con in objTabControl.Controls) { TabPage tab = (TabPage)con; if (tab.Name == MainTabControlKey) { return false;//存在 } } return true;//不存在 }View Code
注:通過在父窗體有部分panel面板中,新增
6 檢視的平鋪、垂直、層疊效果的實現?
#region 檢視顯示 private void 平鋪ToolStripMenuItem1_Click(object sender, EventArgs e) { LayoutMdi(MdiLayout.TileHorizontal); } private void 垂直ToolStripMenuItem1_Click(object sender, EventArgs e) { LayoutMdi(MdiLayout.TileVertical); } private void 層疊ToolStripMenuItem1_Click(object sender, EventArgs e) { LayoutMdi(MdiLayout.Cascade); } #endregionView Code
7 樹節點如何導航到父窗體的panel面板?
private void treeView1_AfterSelect_1(object sender, TreeViewEventArgs e) { try { switch (e.Node.Text.ToString())//字串 { case "法律服務中心"://注意字串夾引號,以下均是 Moth_Apply(); break; case "我要諮詢"://注意字串夾引號,以下均是 Moth_Apply(); break; case "使用者報表匯出"://注意字串夾引號,以下均是 Moth_Print(); break; case "諮詢報表匯出"://注意字串夾引號,以下均是 Moth_Print1(); break; case "統計圖"://注意字串夾引號,以下均是 Moth_Graph(); break; case "二級統計圖"://注意字串夾引號,以下均是 Moth_TwoGraph(); break; case "三級統計圖"://注意字串夾引號,以下均是 Moth_ThreeGraph(); break; case "使用者查詢"://注意字串夾引號,以下均是 Moth_User(); break; case "律師"://注意字串夾引號,以下均是 Moth_lvshi(); break; case "管理員"://注意字串夾引號,以下均是 Moth_guanli(); break; } } catch { return; } }View Code
注:前面5中的方法,在7中遍歷呼叫即可!
8 如何隱藏樹中部分節點?
public void HideNotes() { for (int i = 0; i < this.treeView1.Nodes.Count; i++) { FetchNode(this.treeView1.Nodes[i]);//遞迴根節點的所有子節點 } } private void FetchNode(TreeNode node) { //nodeList.Add(node); for (int i = 0; i < node.Nodes.Count; i++) { if (node.Text == "報表") { treeView1.Nodes.Remove(node.Nodes[0]); } if (node.Text == "法律服務中心") { treeView1.Nodes.Remove(node.Nodes[2]); } if (node.Text == "統計圖") { treeView1.Nodes.Remove(node); } else { FetchNode(node.Nodes[i]); } } }View Code
9 如何實現打包後的窗體類似qq效果,最小化可以在工作列,點選顯示原狀態?
//類似qq最小化 private void Fm_Main_SizeChanged(object sender, EventArgs e) { if (this.WindowState == FormWindowState.Minimized) { this.Hide(); this.notifyIcon1.Visible = true; } } private void notifyIcon1_Click(object sender, EventArgs e) { this.Visible = true; this.WindowState = FormWindowState.Maximized; }View Code
10 手寫窗體分頁效果?
public int pageSize = 10; //每頁記錄數 public int recordCount = 0; //總記錄數 public int pageCount = 0; //總頁數 public int currentPage = 0; //當前頁 public DataTable dtSource = new DataTable(); //載入窗體分頁顯示資料 private void LoadPage() { //資料庫操作獲得DataTable,獲取總頁數 string sql = "select * from L_consult order by ID desc"; dtSource = SQLHelper.GetTableData(sql); recordCount = dtSource.Rows.Count; pageCount = (recordCount / pageSize); if ((recordCount % pageSize) > 0) { pageCount++; } //邏輯判斷頁數顯示 if (currentPage < 1) currentPage = 1; if (currentPage > pageCount) currentPage = pageCount; int beginRecord; int endRecord; DataTable dtTemp; dtTemp = dtSource.Clone(); beginRecord = pageSize * (currentPage - 1); if (currentPage == 1) beginRecord = 0; endRecord = pageSize * currentPage; if (currentPage == pageCount) endRecord = recordCount; for (int i = beginRecord; i < endRecord; i++) { dtTemp.ImportRow(dtSource.Rows[i]); } dataGridView2.DataSource = dtTemp; lblcount.Text = recordCount.ToString(); lblpagecount.Text = pageCount.ToString(); lblpage.Text = pageSize.ToString(); txtindexpage.Text = currentPage.ToString(); } //首頁 private void bntfrist_Click(object sender, EventArgs e) { currentPage = 1; LoadPage(); } //上一頁 private void bntpriove_Click(object sender, EventArgs e) { currentPage--; LoadPage(); } //下一頁 private void bntnext_Click(object sender, EventArgs e) { currentPage++; LoadPage(); } //尾頁 private void bntlast_Click(object sender, EventArgs e) { currentPage = pageCount; LoadPage(); } //跳轉 private void bntGO_Click(object sender, EventArgs e) { if (txtgo.Text == "") { int pageN = 1; } else { int pageN = Convert.ToInt32(txtgo.Text); currentPage = pageN; LoadPage(); } }View Code
注:本功能初始想採用分頁控制元件,後來多次使用失敗,總是報錯。索性自己手寫一個分頁控制元件。需要五個button按鈕和2個文字框。
11 程式碼事件 設定資料控制元件外觀?
/// <summary> /// 用來記錄先前的顏色值 /// </summary> Color colorTmp = Color.White; /// <summary> /// 記錄滑鼠形狀 /// </summary> Cursor cursorTmp = Cursor.Current; //設定資料控制元件外觀 private void dataGridView2_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) { for (int i = 0; i < dataGridView2.Rows.Count; i++) { if (i % 2 == 0) { dataGridView2.Rows[i].DefaultCellStyle.BackColor = Color.Bisque; ; } else { dataGridView2.Rows[i].DefaultCellStyle.BackColor = Color.White; } } } //滑鼠劃過 private void dataGridView2_CellMouseEnter(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex >= 0) { colorTmp = dataGridView2.Rows[e.RowIndex].DefaultCellStyle.BackColor; dataGridView2.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Silver; if (e.ColumnIndex == 1)//改變第二列滑鼠形狀 { cursorTmp = this.Cursor; this.Cursor = Cursors.Hand; } } } //滑鼠離開 private void dataGridView2_CellMouseLeave(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex >= 0) { dataGridView2.Rows[e.RowIndex].DefaultCellStyle.BackColor = colorTmp; if (e.ColumnIndex == 1) { this.Cursor = cursorTmp; } } }View Code
12 執行刪改獲取當前資料主鍵?
int index = dataGridView2.CurrentRow.Index; int EmpId = Convert.ToInt32(dataGridView2.Rows[index].Cells[0].Value.ToString());
13 資料型別管理,實現級聯刪除?
//級聯刪除 private void button2_Click(object sender, EventArgs e) { if (dataGridView1.SelectedRows.Count > 0) { if (dataGridView1.Rows.Count > 0) { int index = dataGridView1.CurrentRow.Index; int EmpId = Convert.ToInt32(dataGridView1.Rows[index].Cells[0].Value.ToString()); if (MessageBox.Show("刪除後將不能恢復!", "提示資訊", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK) { //刪除A表 string asql = "delete from A_type where ID=" + EmpId;//刪除父級別型別 //刪除B表 string sqlaname = "select name from A_type where ID="+EmpId;//條件關聯刪除子表 DataTable dt = MYHelper.SQLHelper.GetTableData(sqlaname); string aname = dt.Rows[0]["name"].ToString(); string sqlbname = "select bname from B_type where aname='" + aname + "'";//條件關聯刪除子表 DataTable dtb = MYHelper.SQLHelper.GetTableData(sqlbname); int c=0; for (int i = 0; i < dtb.Rows.Count; i++) { string bname = dtb.Rows[i][0].ToString(); string csql = "delete from C_type where bname='" + bname + "'";//刪除父級別型別 c = SQLHelper.ExecuteQuery(csql); } string bsql = "delete from B_type where aname='" + aname+"'";//刪除父級別型別 //刪除C表 int b = SQLHelper.ExecuteQuery(bsql); int a = SQLHelper.ExecuteQuery(asql); if (a > 0&&b>0&&c>0) { MessageBox.Show("刪除成功!"); this.LoadPage(); } } } } }View Code
14 資料型別管理,實現級聯修改?
//級聯修改 private void button4_Click(object sender, EventArgs e) { try { if (IsNull() == true) { int index = dataGridView1.CurrentRow.Index; int EmpId = Convert.ToInt32(dataGridView1.Rows[index].Cells[0].Value.ToString()); //修改a表 string asql = "update A_type set name='" + txttype1.Text.ToString() + "' where ID=" + EmpId; string sqlaname = "select name from A_type where ID=" + EmpId;//條件關聯子表 DataTable dt = MYHelper.SQLHelper.GetTableData(sqlaname); string aname = dt.Rows[0]["name"].ToString(); int a = SQLHelper.ExecuteQuery(asql); DataTable dta = MYHelper.SQLHelper.GetTableData(sqlaname); string aaname = dta.Rows[0]["name"].ToString(); //修改b表 string bsql = "update B_type set aname='" + aaname + "' where aname='" + aname + "'"; int b = SQLHelper.ExecuteQuery(bsql); if (a > 0&&b>0) { MessageBox.Show("新增成功!"); this.LoadPage(); this.Clear(); } else { MessageBox.Show("新增失敗!"); } } } catch (Exception ex) { MessageBox.Show(ex.Message + ex.StackTrace); } }View Code
15 Access資料庫連線語句,如何實現遠端連線?
App.config本地配置連線語句:
<add name="sql" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source='C:\Users\bncPc\Desktop\Form_legal\Form_legal\db\Legal.accdb'" />
App.config遠端配置連線語句:
<add name="sql" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=//10.130.16.137/db/Legal.accdb" />
16 Access資料庫操作,封裝執行方法?
//讀取配置檔案中的連線字串 public static string connstr = ConfigurationManager.ConnectionStrings["sql"].ConnectionString; // public static string connstr [email protected]"Provider=MS Remote; Remote Server=http://10.130.16.135; Remote Provider=Microsoft.ACE.OLEDB.12.0;Data Source='C:\Users\bncPc\Desktop\Form_legal\Form_legal\db\Legal.accdb'"; //static string connstr = "data source=ORCL;User Id=system;Password=orcl"; // static string connstr = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=//10.130.16.135/db2/Legal.accdb;Persist Security Info=False" "; //public static string connstr = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=//10.130.16.135/db2/Legal.accdb"; public static bool TestConn() { OleDbConnection conn = new OleDbConnection(connstr); bool flag = true; if (conn != null) { try { conn.Open(); flag = true; } catch { flag=false; return flag; } } else { flag=