最近一直忙于公司的事情,虽然一直在作一些相关的技术研究,可是好久没能静下心来好好写写博客文章了,想一想也有半个月之多了,这半个月来,也一直致力于改善个人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); }
这样咱们就能把传统查询功能和高级查询功能合并一块儿,发挥最大的做用,是咱们的程序可以尽可能知足客户的需求,得到更加好的反馈和支持了。