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

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

那么有无一种介于两者之间的方法,来实现效率的最优化,并且能够统一利用好一个导入的界面呢,在开发领域,只要能想到的,一般也能做到,由于工作的需要,在我的Winform开发框架中引入了一个通用的数据导入模块,来实现这个既是统一,又是变化的业务需求,首先我们来看看能大致的模块功能介绍图,如下所示。

然后我们再来看看实际的导入模块操作界面,如下图所示

在最底的状态栏里面,但我们保存数据的时候,会调用后台线程进行数据保存,并显示数据导入的进度状态,由于是采用后台线程处理,不会阻塞当前的界面,在多文档的Winform开发框架界面中,可以切换到其他业务界面进行其他处理,不影响整体界面操作。

既然是导入界面统一,它肯定封装了一些常规操作,同时提供一些属性或者接口给外部调用对象进行操作,这样才能实现有机的统一,我们来看看具体的实现代码是如何的。

1)定义事件处理

        public delegate bool SaveDataHandler(DataRow dr);
        public event SaveDataHandler OnDataSave;
        public event EventHandler OnRefreshData;

首先我们定义一个数据保存(单行)的处理事件,然后也定义一个数据保存后,刷新主体列表的数据刷新事件,这两个都是提供给调用者实现的逻辑。

我们在这个通用的导入数据窗体,需要的就是利用后台线程调用整个逻辑进行处理数据的导入及后续的刷新操作,如下所示。

        private BackgroundWorker worker = null;

        public delegate bool SaveDataHandler(DataRow dr);
        public event SaveDataHandler OnDataSave;
        public event EventHandler OnRefreshData;

        public FrmImportExcelData()
        {
            InitializeComponent();

            this.gridView1.OptionsBehavior.AutoPopulateColumns = true;
            worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.progressBar1.Value = e.ProgressPercentage;
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.progressBar1.Visible = false;
            this.progressBar1.Value = 0;

            if (OnRefreshData != null)
            {
                OnRefreshData(null, null);
            }

            string tips = e.Result as string;
            if (!string.IsNullOrEmpty(tips))
            {
                MessageDxUtil.ShowTips(tips);
                if (tips == "操作成功")
                {
                    this.gridControl1.DataSource = null;
                }
            }
        }

2)设置显示不同的模板文件

由于导入数据操作要应用于不同的业务数据,那么他们的模板肯定也不同,因此需要提供一个接口给外部,实现模板文件的修改及打开操作。

        /// <summary>
        /// 设置导入模板标题,及文件路径
        /// </summary>
        /// <param name="title"></param>
        /// <param name="filePath"></param>
        public void SetTemplate(string title, string filePath)
        {
            this.lnkExcel.Text = title;
            this.lnkExcel.Tag = filePath;
        }

        private void lnkExcel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            try
            {
                string templateFile = this.lnkExcel.Tag.ToString();
                Process.Start(templateFile);
            }
            catch (Exception)
            {
                MessageDxUtil.ShowWarning("文件打开失败");
            }
        }

3)显示Excel数据

我们在数据导入的时候,最好提供一个数据的显示界面给客户,方便对导入数据的核对,这样可以提高体验效果以及对数据的核对操作,减少出错的几率。具体的实现代码如下所示。数据显示的操作,可以通过操作Excel数据库的方式进行读取,然后显示数据。(其中有些接口API来自我的共用类库,需要可以到我的随笔中了解相关的类库使用。

      private void btnBrowse_Click(object sender, EventArgs e)
        {
            string file = FileDialogHelper.OpenExcel();
            if (!string.IsNullOrEmpty(file))
            {
                this.txtFilePath.Text = file;

                ViewData();
            }
        }

        private void ViewData()
        {
            if (this.txtFilePath.Text == "")
            {
                MessageDxUtil.ShowTips("请选择指定的Excel文件");
                return;
            }

            try
            {
                string connectString = string.Format(connectionStringFormat, this.txtFilePath.Text);
                string firstSheet = ExcelHelper.GetExcelFirstTableName(connectString);

                myDs.Tables.Clear();
                myDs.Clear();
                this.gridControl1.DataSource = null;                

                OleDbConnection cnnxls = new OleDbConnection(connectString);
                OleDbDataAdapter myDa = new OleDbDataAdapter(string.Format("select * from [{0}]", firstSheet), cnnxls);
                myDa.Fill(myDs, "【导入表】");

                this.gridControl1.DataSource = myDs.Tables[0];
                this.gridView1.PopulateColumns();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

4)调用者给出保存数据的逻辑

由于是通用的数据导入操作,因此公用的导入界面,只能抛出相应的事件给外部进行数据保存的逻辑处理,数据导入页面只需要负责总体逻辑,具体的保存逻辑交给调用者实现,这样各司其职,共同把事情做好。下面是调用者(药品信息显示窗体中),对数据导入的操作逻辑实现。我们可以看到,它需要指定模板文件、数据刷新操作、数据保存操作,其他的交给通用数据导入界面进行处理即可。

       private string moduleName = "药品目录";
        private void btnImport_Click(object sender, EventArgs e)
        {
            string templateFile = string.Format("{0}-模板.xls", moduleName);
            FrmImportExcelData dlg = new FrmImportExcelData();
            dlg.SetTemplate(templateFile, System.IO.Path.Combine(Application.StartupPath, templateFile));
            dlg.OnDataSave += new FrmImportExcelData.SaveDataHandler(ExcelData_OnDataSave);
            dlg.OnRefreshData += new EventHandler(ExcelData_OnRefreshData);
            dlg.ShowDialog();
        }

        void ExcelData_OnRefreshData(object sender, EventArgs e)
        {
            BindData();
        }

        bool ExcelData_OnDataSave(DataRow dr)
        {
            bool success = false;
            DrugDetailInfo info = new DrugDetailInfo();
            info.DrugNo = dr["药品编码"].ToString();
            info.DrugName = dr["药品名称"].ToString();
            info.Manufacture = dr["制造商"].ToString();
            info.Formulations = dr["剂型"].ToString();
            info.Specification = dr["规格"].ToString();
            info.Unit = dr["药品单位"].ToString();
            info.Note = dr["备注信息"].ToString();
            info.StockQuantity = ConvertHelper.ToInt32(dr["库存量"].ToString(), 0);

            info.EditTime = DateTime.Now;
            info.Editor = Portal.gc.LoginInfo.Name;
            info.Dept_ID = Portal.gc.LoginInfo.Dept_ID;
            success = BLLFactory<DrugDetail>.Instance.Insert(info);
            return success;
        }

到这里,通用数据导入的操作基本上就结束了,我的处理方式是否和你的想法吻合呢,或者有更好的实现方式?

不过大家的总体思想,肯定是殊途同归,抽象封装统一的部分,并提供个性化的逻辑给外部进行处理,这样就可以实现综合的统一,提高整体的使用效率,较少今后维护的成本。

在这里顺便说一下,数据导出的操作,因为既然有导入,应该也有导出,所以我们也需要实现。它的操作代码不是很复杂,只需要把数据按照导入模板约定的字段名称导出即可,记得要和导入模板一致。

       private void btnExport_Click(object sender, EventArgs e)
        {
            string file = FileDialogHelper.SaveExcel(string.Format("{0}.xls", moduleName));
            if (!string.IsNullOrEmpty(file))
            {
                List<DrugDetailInfo> list = BLLFactory<DrugDetail>.Instance.GetAll();
                DataTable dtNew = DataTableHelper.CreateTable("序号|int,药品编码,药品名称,制造商,剂型,规格,药品单位,备注信息,库存量");
                DataRow dr;
                for (int i = 0; i < list.Count; i++)
                {
                    dr = dtNew.NewRow();
                    dr["序号"] = i + 1;
                    dr["药品编码"] = list[i].DrugNo;
                    dr["药品名称"] = list[i].DrugName;
                    dr["制造商"] = list[i].Manufacture;
                    dr["剂型"] = list[i].Formulations;
                    dr["规格"] = list[i].Specification;
                    dr["药品单位"] = list[i].Unit;
                    dr["备注信息"] = list[i].Note;
                    dr["库存量"] = list[i].StockQuantity;
                    dtNew.Rows.Add(dr);
                }

                try
                {
                    string error = "";
                    AsposeExcelTools.DataTableToExcel2(dtNew, file, out error);
                    if (!string.IsNullOrEmpty(error))
                    {
                        MessageDxUtil.ShowError(string.Format("导出Excel出现错误:{0}", error));
                    }
                    else
                    {
                        if (MessageDxUtil.ShowYesNoAndTips("导出成功,是否打开文件?") == System.Windows.Forms.DialogResult.Yes)
                        {
                            System.Diagnostics.Process.Start(file);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex);
                    MessageDxUtil.ShowError(ex.Message);
                }
            }

以上就是我的通用数据导入导出操作,其实利用代码生成工具Database2Sharp,选定表后,自动生成的WInform界面中,就已经自动生成以上导入、导出Excel的功能代码了,已经极大简化了重复输入代码的可能性了,只需要把界面调整一下就基本上OK了,以上一家之言,欢迎拍砖或者共同探讨。

本文转自博客园伍华聪的博客,原文链接:Winform开发框架之通用数据导入导出操作,如需转载请自行联系原博主。

时间: 2024-10-04 09:29:22

Winform开发框架之通用数据导入导出操作的相关文章

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

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

循序渐进开发WinForm项目(5)--Excel数据的导入导出操作

随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我们每天面对的太多东西了,觉得很多都稀松平常了,即使很细微的地方,可能我们都已经形成习惯了.反过来,如果我们切换到其他领域,如IOS.android,那么开始我们可能对里面很多设计的规则不甚了解,开始可能也是一头雾水. 本篇继续上一篇<循序渐进开发WinForm项目(4)--Winform界面模块的集

数据库数据导入导出系列之五 C#实现动态生成Word(转)

1. 一个控制台例子,实现动态生成Word. 首先,添加引用:COM->Microsoft Word 11.0 Object Library. View Code 2. 介绍几篇牛人写的关于操作Word的文章 [分享]一段导出到word模版的代码 http://www.cnblogs.com/goody9807/archive/2005/08/25/222526.html 再谈word2003编程 http://www.cnblogs.com/Andmm/archive/2008/06/18/1

操作几万条,甚至几十万条数据导入导出 用什么方式比较好?除了数据库外,用NPOI好像不行,有没有大神操作过几十万条EXCEL数据的?

问题描述 操作几万条,甚至几十万条数据导入导出用什么方式比较好?(这里指的导入导出是数据导入到程序里,进行一些修改操作,然后再导出目前操作5000条数据是没问题的,但是超过1W条就报错了,)报了一个这样的错误:其他信息:Exception:WrongLocalheadersignature:0x5757575A我觉得应该是长度受限制了,但是又没有什么好的方法解决除了数据库外,用NPOI好像不行,有没有大神操作过几十万条EXCEL数据的?是怎么解决的求指教 解决方案 解决方案二:没人给回复呀?解决

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

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

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

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

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

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

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

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

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

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