最近一直忙於公司的事情,雖然一直在作一些相關的技術研究,可是好久沒能靜下心來好好寫寫博客文章了,想一想也有半個月之多了,這半個月來,也一直致力於改善個人WInform開發框架,使得本身及客戶使用起來更加方便,更加友好,更加高效。本篇文章就是介紹最近框架改善的其中一個閃光點"通用高級查詢模塊",高級查詢模塊,在不少程序模塊中都很常見,也是給客戶擴展查詢的一個很好的補充,因爲我一直但願個人Winform開發框架可以精益求精,因此作了這個模塊,但願對從此我本身全部的項目以及框架自己,都能高效的使用。sql
既然稱之爲通用查詢模塊,那麼他就不能與具體的表字段有耦合關係,可是要實現具體的查詢,必須經過某種方式進行屬性傳遞,實現更直觀友好的字段查詢功能。高級查詢模塊,在不少完善的程序上都會提供,用於知足用戶對特定的字段,添加特定的條件進行,由於通常狀況下,因爲版面的限制界面上查詢的內容比較有限,只是把一些很常見、重要的字段做爲查詢輸入,若是表字段比較多,那麼對有些特殊的字段就無所適從。數據庫
例如,我一個病人資料管理系統的界面以下所示框架
這個界面已經放置的比較多輸入控件進行查詢了,若是放置更多,超過3行就會致使感受比較臃腫了。因此合理的應該是把不經常使用的放到高級查詢裏面,這樣用戶須要更多條件的定製,能夠在高級查詢中對每一個字段都能進行搜索,常規的在主界面便可查詢。ide
個人高級查詢查詢模塊也是基於這個道理,所以,在主界面增長一個高級查詢按鈕入口,如上圖所示,單擊後,顯示一個全部字段的列表,以下界面。函數
這樣全部表的字段都可進行查詢了,可是咱們注意到,這個界面並無輸入條件的地方,沒錯,這個只是顯示條件內容,浮在主界面的查詢列表上面,若是設置條件後,主界面的列表會根據高級查詢條件進行查詢,這樣也是符合客戶實際的指望的。ui
在介紹輸入條件的時候,咱們注意到,查詢輸入,基本上能夠分爲幾類:其一是常規的文本類型,使用文本框替代便可;其二是下拉列表類型,用戶從列表下面選擇內容;其三是日期類型,須要用戶指定開始日期和結束日期;其四是數字類型,須要用戶指定起始和結束的數值。this
剛纔說到,上面的高級查詢界面,只是顯示,但用戶雙擊列表或者按下回車鍵,那麼根據字段類型,彈出對應上面說到的四種輸入框,用於接收用戶的輸入。spa
1)常規的文本類型條件輸入界面:3d
下拉列表類型條件輸入界面:code
日期類型條件輸入界面:
數字類型條件輸入界面:
輸入以上幾種條件後,高級查詢界面裏面會顯示友好的條件內容,確保用戶可以看懂輸入的條件,以下所示是輸入幾個不一樣類型的條件的顯示內容。
日期類型和數字類型,爲了方便,均可以單獨輸入其中的一部分做爲條件進行高級查詢。
爲了方便,高級查詢模塊的內容,能夠進行查詢後在主界面列表中顯示,也可使用傳統的查詢按鈕進行查詢,使用傳統查詢按鈕後,高級查詢條件將失效,可是保留用戶的輸入,第二次打開界面後,會加載以前的輸入條件,這樣比較人性化一些,用戶能夠隨時自由切換兩種查詢方式。
對於一些字段多的歷史資料表,這種高級查詢模式更加可以發揮其做用,用戶能夠在幾十個甚至上百個字段中選擇合適的條件,而後輸入進行查詢,這樣可以使得客戶更加有認同感,例以下面是一個病人歷史檔案資料表的高級查詢界面,裏面不少字段,使用這個擴展查詢就是最合適不過了。
1)獲取數據庫字段名稱和類型列表
首先前面說到,我是根據字段類型進行不一樣的控件顯示並輸入的,所以須要一個通用的函數獲取字段的名稱和類型,做爲參考,因爲這個功能比較通用,所以把它放在個人Winform開發框架的數據庫基類的抽象類裏面,全部的數據庫訪問類均繼承不用重複寫代碼。
/// <summary> /// 獲取表的字段名稱和數據類型列表。 /// </summary> /// <returns></returns> public DataTable GetFieldTypeList() { DataTable dt = DataTableHelper.CreateTable("ColumnName,DataType"); DataTable schemaTable = GetReaderSchema(tableName); if (schemaTable != null) { foreach (DataRow dr in schemaTable.Rows) { string columnName = dr["ColumnName"].ToString().ToUpper(); string netType = dr["DataType"].ToString().ToLower(); DataRow row = dt.NewRow(); row["ColumnName"] = columnName; row["DataType"] = netType; dt.Rows.Add(row); } } return dt; } /// <summary> /// 獲取指定表的元數據,包括字段名稱、類型等等 /// </summary> /// <param name="tableName">數據庫表名</param> /// <returns></returns> private DataTable GetReaderSchema(string tableName) { DataTable schemaTable = null; string sql = string.Format("Select * FROM {0}", tableName); Database db = CreateDatabase(); DbCommand command = db.GetSqlStringCommand(sql); try { using (IDataReader reader = db.ExecuteReader(command)) { schemaTable = reader.GetSchemaTable(); } } catch (Exception ex) { LogTextHelper.Error(ex); } return schemaTable; }
2)設置高級查詢的字段顯示(有時候可能須要過濾部分字段)
3)指定下拉列表的數據內容
剛纔說到,有一些下拉列表字段的輸入,那麼下拉列表也就須要指定裏面的列表內容了,高級查詢模塊,指定下拉列表的代碼以下所示。
dlg.AddColumnListItem("MidVideo", GetFieldListItem("MidVideo")); dlg.AddColumnListItem("InDiagnosis", GetFieldListItem("InDiagnosis")); dlg.AddColumnListItem("DirectorSurgeon", GetFieldListItem("DirectorSurgeon")); dlg.AddColumnListItem("TumorPart", GetFieldListItem("TumorPart")); dlg.AddColumnListItem("LeaveDiagnosis", GetFieldListItem("LeaveDiagnosis")); dlg.AddColumnListItem("IsFirstTime", GetFieldListItem("IsFirstTime")); dlg.AddColumnListItem("Professor", GetFieldListItem("Professor"));
對於一些固定的列表項目,咱們也能夠經過下面代碼進行綁定。
dlg.AddColumnListItem("Sex", new List<CListItem>() { new CListItem("男"), new CListItem("女") }); dlg.AddColumnListItem("HasReferral", new List<CListItem>() { new CListItem("是"), new CListItem("否") });
4)和普通查詢功能並存
爲了使得傳統查詢按鈕,和高級查詢可以並存,咱們須要存儲一個高級查詢的查詢對象,但傳統查詢的時候,咱們把高級查詢對象設置爲空便可屏蔽高級查詢的條件了。
/// <summary> /// 高級查詢條件語句對象 /// </summary> private SearchCondition advanceCondition; /// <summary> /// 根據查詢條件構造查詢語句 /// </summary> private string GetConditionSql() { //若是存在高級查詢對象信息,則使用高級查詢條件,不然使用主表條件查詢 SearchCondition condition = advanceCondition; if (condition == null) { condition = new SearchCondition(); condition.AddCondition("BedNo", this.txtBedNo.Text.Trim(), SqlOperator.Like); condition.AddCondition("TumorPart", this.txtTumorPart.Text.Trim(), SqlOperator.Like); condition.AddCondition("MidVideo", this.txtMidVideo.Text.Trim(), SqlOperator.Like); condition.AddCondition("Name", this.txtName.Text.Trim(), SqlOperator.Like); condition.AddCondition("HospitalNo", this.txtHospitalNo.Text.Trim(), SqlOperator.Like); condition.AddCondition("IDNumber", this.txtIDNumber.Text.Trim(), SqlOperator.Like); condition.AddCondition("InDiagnosis", this.txtInDiagnosis.Text.Trim(), SqlOperator.Like); condition.AddCondition("LeaveDiagnosis", this.txtLeaveDiagnosis.Text.Trim(), SqlOperator.Like); condition.AddCondition("LeaveSpecimens", this.txtLeaveSpecimens.Text.Trim(), SqlOperator.Like); condition.AddCondition("Professor", this.txtProfessor.Text.Trim(), SqlOperator.Like); condition.AddCondition("Note", this.txtNote.Text.Trim(), SqlOperator.Like); condition.AddCondition("Pathology", this.txtPathology.Text.Trim(), SqlOperator.Like); condition.AddCondition("IsFirstTime", this.txtIsFirstTime.Text.Trim(), SqlOperator.Like); .................. } string where = condition.BuildConditionSql().Replace("Where", ""); return where; }
/// <summary> /// 查詢數據操做 /// </summary> private void btnSearch_Click(object sender, EventArgs e) { advanceCondition = null;//必須重置查詢條件,不然可能會使用高級查詢條件了 BindData(); }
5)根據類型轉換不一樣的窗體
爲了輸入方便,我定義了幾個不一樣輸入內容的窗體,而後根據不一樣的字段類型,構造不一樣的窗體並進行顯示,這樣比較合理顯示並接收客戶的條件輸入。
private void gridControl1_MouseDoubleClick(object sender, MouseEventArgs e) { int[] rowSelected = this.gridView1.GetSelectedRows(); if (rowSelected.Length == 0) return; string fieldName = this.gridView1.GetFocusedRowCellValue("字段").ToString(); string fieldDisplay = this.gridView1.GetFocusedRowCellValue("字段名稱").ToString(); FieldType fieldType = (FieldType)this.gridView1.GetFocusedRowCellValue("字段類型"); string fieldValue = this.gridView1.GetFocusedRowCellValue("查詢條件值").ToString(); #region 根據類型轉換不一樣的窗體 FrmQueryBase dlg = null; if (fieldType == FieldType.Text) { dlg = new FrmQueryTextEdit(); } else if (fieldType == FieldType.Numeric) { dlg = new FrmQueryNumericEdit(); } else if (fieldType == FieldType.DateTime) { dlg = new FrmQueryDateEdit(); } else if (fieldType == FieldType.DropdownList) { dlg = new FrmQueryDropdown(); } #endregion
6)主窗體的事件響應
因爲彈出的高級查詢窗體,裏面的查詢以及清除操做,都是經過主窗體的事件進行處理,所以,彈出的高級查詢窗體,只須要調用相應的事件便可。
dlg.DataClear += new FrmQueryBase.DataClearEventHandler(dlg_DataClear); if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { //更新查詢界面顯示 UpdateFieldValue(fieldName, dlg.ReturnValue, dlg.ReturnDisplay); //更新父窗體的數據顯示 ProcessDataSearch(null, null); }
這樣咱們就能把傳統查詢功能和高級查詢功能合併一塊兒,發揮最大的做用,是咱們的程序可以儘可能知足客戶的需求,得到更加好的反饋和支持了。