【小记】:最近基于WinForm+Access数据库完成一个法律咨询管理系统。本系统要求类似网页后台管理效果,并且基于局域网内,完成多客户端操作同一数据库,根据权限不同分别执行不同功能模块。核心模块为级联统计类型管理、数据库咨询数据扇形统计、树的操作、咨询数据的管理、手写分页、Excel数据的导出、多用户操作服务器数据等。并支持多用户同时操作,远程连接数据库且对数据IP信息的修改。开发过程中特别对界面的要求和事后多用户操作显得略为麻烦。自此,本项目得以完善交付,然后对其进行小结。依旧采用整个框架认识,核心知识逐个梳理分析,以便于二次开发和需要之程序员共享。
篇二:WinForm开发扇形图统计和Excel数据导出
【开篇】本章概述
开发过程简介:应客户需求,在法律咨询系统中以扇形图形式进行数据统计。需求如下:(图表需要统计的数据 各咨询类型小类的数量、以及占大类的百分比,可随意组合生成。例如,(以民事 - 婚姻家庭 - 抚养为例)所有民事的占总案件的比例,婚姻家庭占所有民事的比例,抚养占所有婚姻家庭的比例。)现在核心问题在于两点,第一、如果单级易于把握,但是多级无限组合似乎有难度;第二、web和wpf等开发都有现成的控件或者组件应用经验,而winfrom的相关控件不熟悉;第三、只能通过网上查询相关资料,具体查询结果我做个总结,以供需要之人,免得浪费时间。
一、扇形统计图解决思路:(以下资料中包含扇形图、饼状图、折线图)
1 下个MSChart控件,里面可以直接用
2 C#编写的图像:http://www.cnblogs.com/ziyiFly/archive/2008/09/24/1297841.html
3 比较简洁:http://netdz.blog.163.com/blog/static/2106990252012725104054804/
4 参考:http://jingyan.baidu.com/article/e75aca858b6630142edac6de.html
但是,最终以上思路还没有解决问题。因为我采用的直接C#代码绘制图片显示方式,然后图片pictureBox控件中显示。这样会出现至上而下流形式的输出。使得页面布局效果很差。最终自己改进后,采用两个pictureBox控件,一个用于输出图片流,另一个用于输出文字流。实现效果如下(三级实现,下面显示2级的效果,因为后台核心代码基本一致)
程序编写思路:
1、窗体页面放置两个pictureBox控件显示图片和数据信息.
2、构造Cartogram统计图类,然后构造方法,最后讲控件名和查询数据文本传参。Cartogram.CreateTestImage(pictureBox1, pictureBox2, comboBox1.Text);
3、在统计方法中首先模糊查询获取总的条目,然后将类型表中所有那列类型数据保存在数组中,以便下面遍历。
public static ArrayList BindB(string type) { ArrayList al = new ArrayList(); string sql = "select * from B_type where aname=‘" + type + "‘"; DataSet ds = MYHelper.SQLHelper.GetSetData(sql); if (ds.Tables[0].Rows.Count > 0) { for (int j = 0; j < ds.Tables[0].Rows.Count; j++) { al.Add(ds.Tables[0].Rows[j]["bname"].ToString()); } } return al; }
4、再进行遍历数组,查到每个类型的条目,然后比总数获取百分比进行在圆图上比例显示。到此每个类型所占比例的统计图构造完成,那么怎么打印数目和百分比?
int childtype = 0; ArrayList coun = new ArrayList(); for (int i = 0; i < a.Count; i++) { //子事件总数 childtype = MYHelper.SQLHelper.ExecuteScalar("Select count(*) From L_consult Where type Like ‘" + "%" + a[i].ToString() + "%" + "‘"); //画占扇形图的比例 objgraphics.FillPie((SolidBrush)colors[i], pierect, currentdegree, Convert.ToSingle(childtype) / Total * 360); currentdegree += Convert.ToSingle(childtype) / Total * 360; coun.Add(childtype); }
5、依旧采用遍历方式,将上面遍历的每个类型条目输出,然后除总数目转化成百分比。最后实例化两个图片对象进行输出
for (int i = 0; i < a.Count; i++) { kuandu += 20; y += 20; objgraphics1.DrawString(a[i] + "事件: " + (coun[i]), fontlegend, blackbrush, x, kuandu); objgraphics1.FillRectangle((SolidBrush)colors[i], 20, y, 10, 10); objgraphics1.DrawString("所占比率: " + Convert.ToString((Convert.ToSingle(coun[i]) / Total) * 100) + " %", fontlegend, blackbrush, 200, kuandu); }
6、网页输出和窗体输出的区别?即本代码适应于网页和窗体
网页输出图片流:
Response.ContentType = "image/Jpeg"; objbitmap.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg); objgraphics.Dispose(); objbitmap.Dispose();
窗体输出图片流:
pictureBox1.Image = objbitmap;
pictureBox2.Image = objbitmap1;
完整统计图源码:
static string date = DateTime.Now.ToString("yyyy-MM-dd"); public static ArrayList BindA() { ArrayList al = new ArrayList(); string sql = "select * from A_type"; DataSet ds = MYHelper.SQLHelper.GetSetData(sql); if (ds.Tables[0].Rows.Count > 0) { for (int j = 0; j < ds.Tables[0].Rows.Count; j++) { al.Add(ds.Tables[0].Rows[j]["name"].ToString()); } } return al; } public static ArrayList BindB(string type) { ArrayList al = new ArrayList(); string sql = "select * from B_type where aname=‘" + type + "‘"; DataSet ds = MYHelper.SQLHelper.GetSetData(sql); if (ds.Tables[0].Rows.Count > 0) { for (int j = 0; j < ds.Tables[0].Rows.Count; j++) { al.Add(ds.Tables[0].Rows[j]["bname"].ToString()); } } return al; } public static ArrayList BindC(string type) { ArrayList al = new ArrayList(); string sql = "select * from C_type where bname=‘" + type + "‘"; DataSet ds = MYHelper.SQLHelper.GetSetData(sql); if (ds.Tables[0].Rows.Count > 0) { for (int j = 0; j < ds.Tables[0].Rows.Count; j++) { al.Add(ds.Tables[0].Rows[j]["cname"].ToString()); } } return al; } //一级统计图 public static void CreateTestImage(PictureBox pictureBox1, PictureBox pictureBox2) { ArrayList a = BindA(); //把连接字串指定为一个常量 float Total = MYHelper.SQLHelper.ExecuteScalar("select count(*) from L_consult");//获取总的条目数 #region //设置字体,fonttitle为主标题的字体 Font fontlegend = new Font("verdana", 9); Font fonttitle = new Font("verdana", 10, FontStyle.Bold); //背景宽 int width = 350; int bufferspace = 15; int legendheight = fontlegend.Height * 12 + bufferspace; //高度 int titleheight = fonttitle.Height + bufferspace; int height = width + legendheight + titleheight + bufferspace;//白色背景高 int pieheight = width; Rectangle pierect = new Rectangle(0, titleheight, width, pieheight); //加上各种随机色 ArrayList colors = new ArrayList(); Random rnd = new Random(); for (int i = 0; i < 50; i++) colors.Add(new SolidBrush(Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255)))); //创建一个bitmap实例 Bitmap objbitmap = new Bitmap(width, height); Graphics objgraphics = Graphics.FromImage(objbitmap); Bitmap objbitmap1 = new Bitmap(width, height); Graphics objgraphics1 = Graphics.FromImage(objbitmap1); //画一个白色背景 objgraphics.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height); //画一个亮黄色背景 objgraphics.FillRectangle(new SolidBrush(Color.Beige), pierect); //以下为画饼图(有几行row画几个) float currentdegree = 0.0f; #endregion int childtype = 0; ArrayList coun = new ArrayList(); for (int i = 0; i < a.Count; i++) { //子事件总数 childtype = MYHelper.SQLHelper.ExecuteScalar("Select count(*) From L_consult Where type Like ‘" + "%" + a[i].ToString() + "%" + "‘"); //画子事件总数 objgraphics.FillPie((SolidBrush)colors[i], pierect, currentdegree, Convert.ToSingle(childtype) / Total * 360); currentdegree += Convert.ToSingle(childtype) / Total * 360; coun.Add(childtype); } //以下为生成主标题 SolidBrush blackbrush = new SolidBrush(Color.Black); SolidBrush bluebrush = new SolidBrush(Color.Blue); string title = " 律师事务所统计饼状图: " + "\n \n\n"; StringFormat stringFormat = new StringFormat(); stringFormat.Alignment = StringAlignment.Center; stringFormat.LineAlignment = StringAlignment.Center; objgraphics.DrawString(title, fonttitle, blackbrush, new Rectangle(0, 10, width, titleheight), stringFormat); //列出各字段与得数目 objgraphics1.DrawRectangle(new Pen(Color.White, 2), 0, 0, 400, 400); objgraphics1.DrawString("---------------咨询事件统计信息---------------------", fontlegend, bluebrush, 0, 10); objgraphics1.DrawString("统计单位: " + "律师事务所", fontlegend, blackbrush, 40, 40); objgraphics1.DrawString("统计年份: " + date, fontlegend, blackbrush, 40, 70); objgraphics1.DrawString("事件咨询总数: " + Convert.ToString(Total), fontlegend, blackbrush, 40, 100); int kuandu = 100; int y = 100; int x = 40; for (int i = 0; i < a.Count; i++) { kuandu += 30; y += 30; objgraphics1.DrawString(a[i] + "事件: " + (coun[i]), fontlegend, blackbrush, x, kuandu); objgraphics1.FillRectangle((SolidBrush)colors[i], 20, y, 10, 10); objgraphics1.DrawString("所占比率: " + Convert.ToString((Convert.ToSingle(coun[i]) / Total) * 100) + " %", fontlegend, blackbrush, 160, kuandu); } pictureBox1.Image = objbitmap; pictureBox2.Image = objbitmap1; } //二级统计图 public static void CreateTestImage(PictureBox pictureBox1, PictureBox pictureBox2, string type) { ArrayList a = BindB(type); //把连接字串指定为一个常量 float Total = MYHelper.SQLHelper.ExecuteScalar("Select count(*) From L_consult Where type Like ‘" + "%" + type + "%" + "‘");//获取总的条目数 #region //设置字体,fonttitle为主标题的字体 Font fontlegend = new Font("verdana", 9); Font fonttitle = new Font("verdana", 10, FontStyle.Bold); //背景宽 int width = 350; int bufferspace = 15; int legendheight = fontlegend.Height * 12 + bufferspace; //高度 int titleheight = fonttitle.Height + bufferspace; int height = width + legendheight + titleheight + bufferspace;//白色背景高 int pieheight = width; Rectangle pierect = new Rectangle(0, titleheight, width, pieheight); //加上各种随机色 ArrayList colors = new ArrayList(); Random rnd = new Random(); for (int i = 0; i < 50; i++) colors.Add(new SolidBrush(Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255)))); //创建一个bitmap实例 Bitmap objbitmap = new Bitmap(width, height); Graphics objgraphics = Graphics.FromImage(objbitmap); Bitmap objbitmap1 = new Bitmap(width, height); Graphics objgraphics1 = Graphics.FromImage(objbitmap1); //画一个白色背景 objgraphics.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height); //画一个亮黄色背景 objgraphics.FillRectangle(new SolidBrush(Color.Beige), pierect); //以下为画饼图(有几行row画几个) float currentdegree = 0.0f; #endregion int childtype = 0; ArrayList coun = new ArrayList(); for (int i = 0; i < a.Count; i++) { //子事件总数 childtype = MYHelper.SQLHelper.ExecuteScalar("Select count(*) From L_consult Where type Like ‘" + "%" + a[i].ToString() + "%" + "‘"); //画占扇形图的比例 objgraphics.FillPie((SolidBrush)colors[i], pierect, currentdegree, Convert.ToSingle(childtype) / Total * 360); currentdegree += Convert.ToSingle(childtype) / Total * 360; coun.Add(childtype); } //以下为生成主标题 SolidBrush blackbrush = new SolidBrush(Color.Black); SolidBrush bluebrush = new SolidBrush(Color.Blue); string title = " 律师事务所统计饼状图: " + "\n \n\n"; StringFormat stringFormat = new StringFormat(); stringFormat.Alignment = StringAlignment.Center; stringFormat.LineAlignment = StringAlignment.Center; objgraphics.DrawString(title, fonttitle, blackbrush, new Rectangle(0, 10, width, titleheight), stringFormat); //列出各字段与得数目 objgraphics1.DrawRectangle(new Pen(Color.White, 2), 0, 0, 500, 500); objgraphics1.DrawString("---------------咨询事件统计信息---------------------", fontlegend, bluebrush, 0, 10); objgraphics1.DrawString("统计单位: " + "律师事务所", fontlegend, blackbrush, 40, 40); // objgraphics1.DrawString("【统计时间:】" + date, fontlegend, blackbrush, 40, 40); objgraphics1.DrawString("事件咨询总数: " + Convert.ToString(Total), fontlegend, blackbrush, 40, 60); int kuandu = 60; int y = 60; int x = 40; for (int i = 0; i < a.Count; i++) { kuandu += 20; y += 20; objgraphics1.DrawString(a[i] + "事件: " + (coun[i]), fontlegend, blackbrush, x, kuandu); objgraphics1.FillRectangle((SolidBrush)colors[i], 20, y, 10, 10); objgraphics1.DrawString("所占比率: " + Convert.ToString((Convert.ToSingle(coun[i]) / Total) * 100) + " %", fontlegend, blackbrush, 200, kuandu); } pictureBox1.Image = objbitmap; pictureBox2.Image = objbitmap1; } //三级统计图 public static void CreateThreeImage(PictureBox pictureBox1, PictureBox pictureBox2, string bname) { ArrayList a = BindC(bname); //把连接字串指定为一个常量 float Total = MYHelper.SQLHelper.ExecuteScalar("Select count(*) From L_consult Where type Like ‘" + "%" + bname + "%" + "‘");//获取总的条目数 #region //设置字体,fonttitle为主标题的字体 Font fontlegend = new Font("verdana", 9); Font fonttitle = new Font("verdana", 10, FontStyle.Bold); //背景宽 int width = 350; int bufferspace = 15; int legendheight = fontlegend.Height * 12 + bufferspace; //高度 int titleheight = fonttitle.Height + bufferspace; int height = width + legendheight + titleheight + bufferspace;//白色背景高 int pieheight = width; Rectangle pierect = new Rectangle(0, titleheight, width, pieheight); //加上各种随机色 ArrayList colors = new ArrayList(); Random rnd = new Random(); for (int i = 0; i < 80; i++) colors.Add(new SolidBrush(Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255)))); //创建一个bitmap实例 Bitmap objbitmap = new Bitmap(width, height); Graphics objgraphics = Graphics.FromImage(objbitmap); Bitmap objbitmap1 = new Bitmap(width, height); Graphics objgraphics1 = Graphics.FromImage(objbitmap1); //画一个白色背景 objgraphics.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height); //画一个亮黄色背景 objgraphics.FillRectangle(new SolidBrush(Color.Beige), pierect); //以下为画饼图(有几行row画几个) float currentdegree = 0.0f; #endregion int childtype = 0; ArrayList coun = new ArrayList(); for (int i = 0; i < a.Count; i++) { //子事件总数 childtype = MYHelper.SQLHelper.ExecuteScalar("Select count(*) From L_consult Where type Like ‘" + "%" + a[i].ToString() + "%" + "‘"); //画通过人数 objgraphics.FillPie((SolidBrush)colors[i], pierect, currentdegree, Convert.ToSingle(childtype) / Total * 360); currentdegree += Convert.ToSingle(childtype) / Total * 360; coun.Add(childtype); } //以下为生成主标题 SolidBrush blackbrush = new SolidBrush(Color.Black); SolidBrush bluebrush = new SolidBrush(Color.Blue); string title = " 律师事务所统计饼状图: " + "\n \n\n"; StringFormat stringFormat = new StringFormat(); stringFormat.Alignment = StringAlignment.Center; stringFormat.LineAlignment = StringAlignment.Center; objgraphics.DrawString(title, fonttitle, blackbrush, new Rectangle(0, 10, width, titleheight), stringFormat); //列出各字段与得数目 objgraphics1.DrawRectangle(new Pen(Color.White, 2), 0, 0, 400, 500); objgraphics1.DrawString("---------------咨询事件统计信息---------------------", fontlegend, bluebrush, 0, 10); objgraphics1.DrawString("统计单位: " + "律师事务所", fontlegend, blackbrush, 40, 40); // objgraphics1.DrawString("【统计时间:】" + date, fontlegend, blackbrush, 40, 40); objgraphics1.DrawString("事件咨询总数: " + Convert.ToString(Total), fontlegend, blackbrush, 40, 60); int kuandu = 60; int y = 60; int x = 40; for (int i = 0; i < a.Count; i++) { kuandu += 20; y += 20; objgraphics1.DrawString(a[i] + "事件: " + (coun[i]), fontlegend, blackbrush, x, kuandu); objgraphics1.FillRectangle((SolidBrush)colors[i], 20, y, 10, 10); objgraphics1.DrawString("所占比率: " + Convert.ToString((Convert.ToSingle(coun[i]) / Total) * 100) + " %", fontlegend, blackbrush, 220, kuandu); } pictureBox1.Image = objbitmap; pictureBox2.Image = objbitmap1; }
二、对当前页数据和全部数据导出的功能实现
首先咱们还是看看效果,然后我说下思路,最后查看源码:
注释:当用户点击导出当前页,则以Excel形式导出当前页信息,当用户关闭时候,提示是否保存。这样既可以达到查询效果,也可以根据需求进行保存。当然导出所有页效果一样。
思路:在方法中传入两个参数(数据源控件名DataGridView,布尔值flag),在方法中对DataGridView进行遍历打印出标头,然后在对各列数据进行填充。最后通过控件器flag控制,如果flag==true,则进行数据保存关闭Excel,如果false直接关闭
补充:有时候,在导出Excel时候,不希望直接显示关闭时保存,即如上方法。希望直接弹窗,提示保存物理路径进行保存。在如下代码,将两种形式实现方法都进行粘贴出来。
方法一、关闭时保存
#region DataGridView数据显示到Excel /// <summary> /// 打开Excel并将DataGridView控件中数据导出到Excel /// </summary> /// <param name="dgv">DataGridView对象 </param> /// <param name="isShowExcle">是否显示Excel界面 </param> /// <remarks> /// add com "Microsoft Excel 11.0 Object Library" /// using Excel=Microsoft.Office.Interop.Excel; /// </remarks> /// <returns> </returns> public bool DataGridviewShowToExcel(DataGridView dgv, bool isShowExcle) { try { if (dgv.Rows.Count == 0) return false; //建立Excel对象 Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application(); excel.Application.Workbooks.Add(true); excel.Visible = isShowExcle; //生成字段名称 for (int i = 0; i < dgv.ColumnCount; i++) { excel.Cells[1, i + 1] = dgv.Columns[i].HeaderText; } //填充数据 for (int i = 0; i < dgv.RowCount; i++) { for (int j = 0; j < dgv.ColumnCount; j++) { if (dgv[j, i].ValueType == typeof(string)) { excel.Cells[i + 2, j + 1] = "‘" + dgv[j, i].Value.ToString(); } else { excel.Cells[i + 2, j + 1] = dgv[j, i].Value.ToString(); } } } return true; } catch { return false; } } #endregion
方法二、直接提示保存
#region DateGridView导出到csv格式的Excel /// <summary> /// 常用方法,列之间加\t,一行一行输出,此文件其实是csv文件,不过默认可以当成Excel打开。 /// </summary> /// <remarks> /// using System.IO; /// </remarks> /// <param name="dgv"></param> public void DataGridViewToExcel(DataGridView dgv) { SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "Execl files (*.xls)|*.xls"; dlg.FilterIndex = 0; dlg.RestoreDirectory = true; dlg.CreatePrompt = true; dlg.Title = "保存为Excel文件"; if (dlg.ShowDialog() == DialogResult.OK) { Stream myStream; myStream = dlg.OpenFile(); StreamWriter sw = new StreamWriter(myStream, System.Text.Encoding.GetEncoding(-0)); string columnTitle = ""; try { //写入列标题 for (int i = 0; i < dgv.ColumnCount; i++) { if (i > 0) { columnTitle += "\t"; } columnTitle += dgv.Columns[i].HeaderText; } sw.WriteLine(columnTitle); //写入列内容 for (int j = 0; j < dgv.Rows.Count; j++) { string columnValue = ""; for (int k = 0; k < dgv.Columns.Count; k++) { if (k > 0) { columnValue += "\t"; } if (dgv.Rows[j].Cells[k].Value == null) columnValue += ""; else columnValue += dgv.Rows[j].Cells[k].Value.ToString().Trim(); } sw.WriteLine(columnValue); } sw.Close(); myStream.Close(); } catch (Exception e) { MessageBox.Show(e.ToString()); } finally { sw.Close(); myStream.Close(); } } } #endregion
【篇中】技术梳理
1、如何点击按钮显示弹窗保存或者加载文件?
SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "Execl files (*.xls)|*.xls"; dlg.FilterIndex = 0; dlg.RestoreDirectory = true; dlg.CreatePrompt = true; dlg.Title = "保存为Excel文件"; if (dlg.ShowDialog() == DialogResult.OK) { 。。。。。 }
2、如何遍历数据控件的列名并写入Excel?
for (int i = 0; i < dgv.ColumnCount; i++) { if (i > 0) { columnTitle += "\t"; } columnTitle += dgv.Columns[i].HeaderText; } sw.WriteLine(columnTitle);
3、如何遍历数据逐行写入Excel?
//写入列内容 for (int j = 0; j < dgv.Rows.Count; j++) { string columnValue = ""; for (int k = 0; k < dgv.Columns.Count; k++) { if (k > 0) { columnValue += "\t"; } if (dgv.Rows[j].Cells[k].Value == null) columnValue += ""; else columnValue += dgv.Rows[j].Cells[k].Value.ToString().Trim(); } sw.WriteLine(columnValue); } sw.Close(); myStream.Close();
4、怎么使用集合保存数据?
public static ArrayList BindA() { ArrayList al = new ArrayList(); string sql = "select * from A_type"; DataSet ds = MYHelper.SQLHelper.GetSetData(sql); if (ds.Tables[0].Rows.Count > 0) { for (int j = 0; j < ds.Tables[0].Rows.Count; j++) { al.Add(ds.Tables[0].Rows[j]["name"].ToString()); } } return al; }
5、怎样显示随机颜色?
Random rnd = new Random(); for (int i = 0; i < 50; i++) colors.Add(new SolidBrush(Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255))));
【篇末】项目小结
到此为止,本篇介绍了窗体显示扇形图的思想和代码。并且提供了支持网页和窗体共享的通用源码,在这个过程中推荐了几遍相关的技术文档。然后又针对导出Excel不同效果的分析与总结,结合第一遍实现上整天核心点已经讲完。其他简单的功能没有在文章中赘述。下章主要介绍,在配置远程连接数据库遇到问题,以及如何解决问题,关于vpn和ftp是摸索中遇到的,到时候一并介绍。另外如何在服务器分享文件,控制文件读写权限。客户端如何远程连接服务器数据库等关于配置安装问题。