Winform开发框架之通用高级查询模块

最近一直忙于公司的事情,虽然一直在做一些相关的技术研究,但是很久没能静下心来好好写写博客文章了,想想也有半个月之多了,这半个月来,也一直致力于改善我的WInform开发框架,使得自己及客户使用起来更加方便,更加友好,更加高效。本篇文章就是介绍最近框架改善的其中一个闪光点"通用高级查询模块",高级查询模块,在很多程序模块中都很常见,也是给客户扩展查询的一个很好的补充,由于我一直希望我的Winform开发框架能够精益求精,所以做了这个模块,希望对今后我自己所有的项目以及框架本身,都能高效的使用。

1、通用高级查询模块的用途及介绍

既然称之为通用查询模块,那么他就不能与具体的表字段有耦合关系,但是要实现具体的查询,必须通过某种方式进行属性传递,实现更直观友好的字段查询功能。高级查询模块,在很多完善的程序上都会提供,用于满足用户对特定的字段,添加特定的条件进行,因为一般情况下,由于版面的限制界面上查询的内容比较有限,只是把一些很常见、重要的字段作为查询输入,如果表字段比较多,那么对有些特殊的字段就无所适从。

例如,我一个病人资料管理系统的界面如下所示

这个界面已经放置的比较多输入控件进行查询了,如果放置更多,超过3行就会导致感觉比较臃肿了。所以合理的应该是把不常用的放到高级查询里面,这样用户需要更多条件的定制,可以在高级查询中对每个字段都能进行搜索,常规的在主界面即可查询。

我的高级查询查询模块也是基于这个道理,因此,在主界面增加一个高级查询按钮入口,如上图所示,单击后,显示一个所有字段的列表,如下界面。

这样所有表的字段均可进行查询了,但是我们注意到,这个界面并没有输入条件的地方,没错,这个只是显示条件内容,浮在主界面的查询列表上面,如果设置条件后,主界面的列表会根据高级查询条件进行查询,这样也是符合客户实际的期望的。

在介绍输入条件的时候,我们注意到,查询输入,基本上可以分为几类:其一是常规的文本类型,使用文本框替代即可;其二是下拉列表类型,用户从列表下面选择内容;其三是日期类型,需要用户指定开始日期和结束日期;其四是数字类型,需要用户指定起始和结束的数值。

刚才说到,上面的高级查询界面,只是显示,但用户双击列表或者按下回车键,那么根据字段类型,弹出对应上面说到的四种输入框,用于接收用户的输入。

1)常规的文本类型条件输入界面:

下拉列表类型条件输入界面:

日期类型条件输入界面:

数字类型条件输入界面:

输入以上几种条件后,高级查询界面里面会显示友好的条件内容,确保用户能够看懂输入的条件,如下所示是输入几个不同类型的条件的显示内容。

日期类型和数字类型,为了方便,都可以单独输入其中的一部分作为条件进行高级查询。

为了方便,高级查询模块的内容,可以进行查询后在主界面列表中显示,也可以使用传统的查询按钮进行查询,使用传统查询按钮后,高级查询条件将失效,但是保留用户的输入,第二次打开界面后,会加载之前的输入条件,这样比较人性化一些,用户可以随时自由切换两种查询方式。

对于一些字段多的历史资料表,这种高级查询模式更加能够发挥其作用,用户可以在几十个甚至上百个字段中选择合适的条件,然后输入进行查询,这样能够使得客户更加有认同感,例如下面是一个病人历史档案资料表的高级查询界面,里面很多字段,使用这个扩展查询就是最合适不过了。

2、通用高级查询模块的实现思路

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);
            }

这样我们就能把传统查询功能和高级查询功能合并一起,发挥最大的作用,是我们的程序能够尽量满足客户的需求,获得更加好的反馈和支持了。

本文转自博客园伍华聪的博客,原文链接:Winform开发框架之通用高级查询模块,如需转载请自行联系原博主。

时间: 2024-08-01 01:33:43

Winform开发框架之通用高级查询模块的相关文章

Winform开发框架之通用附件管理模块

在做Winform项目的时候,一直有一个梦想,就是希望把所有的组件模块组合即可组装成一个完整的项目系统(或者至少可以大部分完成).这即使是梦想,我也一直为之奋斗,每前进一步,我们离梦想就靠近一步.因此,本着这个梦想,我一路走来,开发整理了一系列的组件模块,包括底层的公用类库.Winform分页控件.通用的适应多数据库的查询组件,以及相对高层次一点的组件模块:通用权限管理系统.通用字典管理模块.通用程序自动更新模块.以及本篇随笔介绍的通用附件管理模块,当然还会有更多的组件模块会吸引我继续朝着梦想前

Winform开发框架之通用自动更新模块

在网络化的环境中,特别是基于互联网发布的Winform程序,程序的自动更新功能是比较重要的操作,这样可以避免挨个给使用者打电话.发信息通知或者发送软件等,要求其对应用程序进行升级.实现程序的自动更新,在后期发布维护过程中,一个可以快速相应修正相关的Bug或者增加功能,二个可以避免给使用客户隔三差五的发送修改好的程序,省心省力.本文也是基于这个思想,在Winform开发框架中再引入一个自动升级更新的通用模块,这个自动升级的通用模块除了具备一般的功能外,可以通过配置程序标题.升级路径方式等方式,实现

Winform开发框架之通用短信邮件通知模块

在做Winform项目的时候,一直有一个梦想,就是希望把所有的组件模块组合即可组装成一个完整的项目系统(或者至少可以大部分完成).在之前介绍的<Winform开发框架之通用附件管理模块>里面介绍了我的Winform开发框架的版图,里面包含了我对Winform模块化的一系列规划的组件,组件尽可能是适用于大多数的业务环境组合,以达到最大程度的重用和高效开发. Winform开发框架是我集多年开发经验以及积累而成,很多细节之处润物细无声,但却是精粹心得所至,很多地方都希望是精益求精,力求把框架中的模

Winform开发框架之通用人员信息管理

我在随笔<Winform开发框架之通用短信邮件通知模块>和<Winform开发框架之通用附件管理模块>等多篇文章中都有介绍我总体的一个Winform开发框架的规划,其实就是开发一个集成度高的Winform开发框架,然后尽可能开发一些日常项目用到的模块,我称之为通用模块(可重复利用,或稍微调整可以适应项目需求),这样就形成了一个良好的生态体系,能够极快提高开发效率,同时也能规范化项目产品的开发工作,松耦合的集成能提供更高层的整合和利用.本文主要介绍我的Winform开发框架体系里面的

Winform开发框架之通用人员信息管理实现代码介绍

我在上一篇<Winform开发框架之通用人员信息管理>随笔中介绍了这个通用人员信息管理的大致实现界面和思路,本篇就其中的实现细节做进一步的分析和共享,希望大家对其中的实现代码进行一个了解,并希望多多提出宝贵意见.通用人员信息管理模块,这个模块其实在很多场合都可能用到,如企业员工管理.科室员工管理等等,这些要求登记人员详细资料及图片等信息的系统模块. 1.项目框架布局 以上几个模块分开是为了适应更多的项目需要,如可能用到WCF模块,那么实体类需要独立引用.但是如果是纯粹的Winform模块,以最

Winform开发框架之通用数据导入导出操作的事务性操作完善

1.通用数据导入导出操作模块回顾 在我的Winfrom开发框架里面,有一个通用的导入模块,它在默默处理这把规范的Excel数据导入到不 同的对象表里面,一直用它来快速完成数据导入的工作.很早在随笔<Winform开发框架之通用数据导入 导出操作>里面就很全面的介绍过它的相关功能了,在代码生成工具Database2Sharp里面,生成的 Winfrom界面代码也已经把它的调用代码放进去了,因此使用起来真是很好,很开心. 在不断的项目实践中,发现使用基于Sqlite的客户端作为单机版的操作也越来越

Winform开发框架之通用定时服务管理

做项目的时候,或多或少需要和其他外部系统或者接口进行数据交互,有些是单向的获取,有些可能是修改状态后再写回去,不管如何,这个都可以称之为数据同步操作,如人员信息同步.业务数据同步.第三方接口数据同步等等. 数据同步涉及到一个同步时间的问题,一般不敏感的数据,一天或者一周左右同步一次就可以了,有些可能需要间隔更短一点. 同步的逻辑不同,有些可能写数据库就可以了,有些可能需要访问WebService或者其他接口,然后在进行数据获取,保存等操作,回写的时候,也一般是调用WebService这样的接口修

Winform开发框架之通用定时服务管理2---如何开发定时服务应用

在上篇随笔<Winform开发框架之通用定时服务管理>介绍了我的框架体系中,通用定时服务管理模块的设计以及一些相关功能的展示.我们在做项目的时候,或多或少需要和其他外部系统或者接口进行数据交互,有些是单向的获取,有些是双向的操作.这个定时操作(可能是间隔的时间,也可以能是定在某一个时刻,也可以能是让它在某天某时刻运行),那么这就需要定时服务程序来管理了,通常我们把他寄宿在Windows服务里面(这也是一种最佳的方式),这种方式最好的地方,就是它的生命周期可以随着电脑的启动而启动,而且很少需要用

Winform开发框架之通用数据导入导出操作

做了很多Winform的项目,对于数据导入,一直也有自己的理解,由于一般的业务系统,经常性的数据导入时很正常的业务需求,因为毕竟使用Excel来操作数据也很方便,或者由于系统之间的数据交换需要,我们需要提供一个入口给客户导入所需要的数据.但是导入数据的时候,不同的业务数据对应不同的Excel文件,很难做到统一,但如果是每个业务模型,都创建一个不同的导入界面来操作Excel数据,又会觉得可能某种程度上重复劳动,增加开发及维护成本. 那么有无一种介于两者之间的方法,来实现效率的最优化,并且能够统一利