近期帮朋友开发了一个小型WINFORM的软件,即在软件窗体中显示相关表格的数据。由于长时期未操作WINFORM了,有点手生,尤其是对于EXCEL表格的操作,在没有第三方控件辅助的基础上,更感到了吃力。 但所幸功夫不负有心人, 终于让我找到了相关的解决方案。 现总结一下此次项目开发的经验教训,以便后期借鉴。
1.winform虽然目前已不再是主流的开发技术, 但对于小型的需求,仍不失为一个不错的选择,尤其是对于小型单机办公型应用程序,其界面的友好性,及相关控件操作的简易性,很适合开发小型的桌面应用程序。
2.闪屏现象解决方案。由于在窗体中加入大量图片,以及绘图等操作,在程序启动或加载某个界面时出现屏幕闪烁的问题。为了解决这种现象,可以在窗体构造函数中加入如下代码,以解决闪屏现象。其中第一行一般为程序自带代码, 后面几行主要是解决屏幕变化出现的闪烁现象,即进行一定的缓冲操作。
InitializeComponent();
this.DoubleBuffered = true;//设置本窗体
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲
3.窗体容器panel的使用, 即如果需要对窗体背景图片进行分割,可以利用容器控件进行适当的操作。
4.基本控件的使用,如control,label,textbox等等。
5.datagridview控件的使用。对于没有格式要求的数据显示,datagridview及listview可以做为基本的显示控件,且可以通过调整相关的属性,如控件头部隐藏,边框隐藏,填充窗体的方式等进行简单的格式调整,但对于合并单元格等操作,但一般需要进行绘图等复杂操作,相应的代码较为复杂。
5.1 datagridview的读取数据操作。
string str = @"d:\cpcx\cpqlc.xls";//也可以通过打开文件对话框进行选择文件。
DataTable dt = ExcelUp(File); // 将excel文件读取到DATATABLE
dataGridView1.AutoGenerateColumns = true; //设置datagridview自动增加列,即新添加的数据自动新添加列。
dataGridView1.DataSource = dt; //设置数据源。
public DataTable ExcelUp(string fileName)
{
string filePath = fileName;//读取excel文件路径;
DataTable dt = GetDataTable("Sheet1", filePath);
return dt;
}
protected DataTable GetDataTable(string strSheetName, string strExcelFileName)
{
//源的定义,即ADO的读取数据操作。
//string strConn = "Provider=Microsoft.Jet.OleDb.4.0;" + "data source=" + Server.MapPath("ExcelFiles/MyExcelFile.xls") + ";Extended Properties='Excel 8.0; HDR=Yes; IMEX=1'";//此连接只能操作Excel2007之前(.xls)文件
string strConn = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;" + "Data Source={0};" + "Extended Properties='Excel 8.0;HDR=NO;IMEX=1';", strExcelFileName);//此连接可以操作.xls与.xlsx文件 (支持Excel2003 和 Excel2007 的连接字符串).
//备注: "HDR=yes;"是说Excel文件的第一行是列名而不是数据,"HDR=No;"正好与前面的相反。
// "IMEX=1 "如果列中的数据类型不一致,使用"IMEX=1"可必免数据类型冲突。
特别注意
Extended Properties='Excel 8.0;HDR=yes;IMEX=1'
A: HDR ( HeaDer Row )设置
若指定值为Yes,代表 Excel 档中的工作表第一行是栏位名称
若指定值為 No,代表 Excel 档中的工作表第一行就是資料了,沒有栏位名称
B:IMEX ( IMport EXport mode )设置
IMEX 有三种模式,各自引起的读写行为也不同,容後再述:
0 is Export mode
1 is Import mode
2 is Linked mode (full update capabilities)
我这里特别要说明的就是 IMEX 参数了,因为不同的模式代表著不同的读写行为:
当IMEX=0 时为“汇出模式”,这个模式开启的 Excel 档案只能用来做“写入”用途。
当 IMEX=1 时为“汇入模式”,这个模式开启的 Excel 档案只能用来做“读取”用途。
当 IMEX=2 时为“连結模式”,这个模式开启的 Excel 档案可同时支援“读取”与“写入”用途。
//Sql语句
string strExcel = string.Format("select * from [{0}$]", strSheetName);
//定义存放的数据表
DataSet ds = new DataSet();
//连接数据源
OleDbConnection conn = new OleDbConnection(strConn);
try
{
conn.Open();
//适配到数据源
OleDbDataAdapter adapter = new OleDbDataAdapter(strExcel, strConn);
adapter.Fill(ds, strSheetName);
}
catch (Exception e)
{
throw e;
}
finally
{
conn.Close();
}
return ds.Tables[strSheetName];
}
5.2 datagridview单元格绘制操作。
5.3 listview的读取数据操作。
listViewData.Items.Clear();
listViewData.Columns.Clear();
listViewData.Columns.Add("", 0, HorizontalAlignment.Center);
listViewData.Columns.Add("序号",40, HorizontalAlignment.Right);
listViewData.Columns.Add("快递单号",120, HorizontalAlignment.Center);
listViewData.Columns.Add("修改重量",80, HorizontalAlignment.Right);
var i =0;var strShuju ="";//取得的字符串
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Text)) { strShuju= (string)iData.GetData(DataFormats.Text); }
if (strShuju.IndexOf("\r\n") == -1)return;
var iShuju = strShuju.Length; //字符串总长度
while (strShuju != "")
{
try {
var myItem =new ListViewItem(); myItem.SubItems.Add((++i).ToString());var strRow = strShuju.Substring(0, strShuju.IndexOf("\r\n"));// 每行字符串
var iRow = strShuju.Substring(0, strShuju.IndexOf("\r\n")).Length;//每行字符串长度
var iRow1 = 0; //每行字符串长度
if (iRow == 0)
break;
iRow1 = iRow + 1;
iRow = iRow + 2;
iShuju = iShuju - iRow;
strShuju = strShuju.Substring(iRow, iShuju);
strRow = strRow + "\t";while (strRow !="") {var strColumn = strRow.Substring(0, strRow.IndexOf("\t"));//每段字符串
var iColumn = strColumn.Substring(0, strRow.IndexOf("\t")).Length;//每段字符串长度
if (iRow1 == 0) break;
iRow1 = iRow1 - iColumn -1;
strRow = strRow.Substring(iColumn +1, iRow1);
myItem.SubItems.Add(strColumn); }
listViewData.Items.Add(myItem);
}
catch (ArgumentOutOfRangeException)
{ break; } }
5.4 listview单元格绘制操作。
6.窗体布局的动态调整, 一般编程时,各个窗体及控件的大小及位置都是固定好了,这样也可以减少程序运行时的负担,即加快运行速度。但对于部分程序,窗体的大小需要根据运行屏幕的尺寸进行改变,这时就需要编码进行动太调整窗体大小了,一般代码如下,当然,窗体中的其它控件也可以进行类似的调整。
Rectangle rect = Screen.GetWorkingArea(this);//获取屏幕尺寸。
this.Height = rect.Height;//设置窗体尺寸。
this.Width = rect.Width;
7.窗体样式的动态调整。如果需要在程序运行时独占屏幕,且不出现状态栏,最大/小化按钮,及关闭按钮等样式,可以进行以下代码编程。
if (this.WindowState == FormWindowState.Maximized) //判断窗体是否最大化
{
this.FormBorderStyle = FormBorderStyle.None;// 窗体的边框及标题栏的状态
}
else
{
this.FormBorderStyle = FormBorderStyle.None;//
this.WindowState = FormWindowState.Maximized;//
}
8. VS平台智能提示异常问题的解决。即从开始->Microsoft Visual Studio 2008->Visual Studio Tools->Visual Studio 2008 命令提示,进入Common7\IDE,然后输入:devenv.exe /setup /resetuserdata /resetsettings或devenv.exe/resetsettings。
9.窗体切换操作代码:Forms f=new Forms(); f.Show(); this.Hide();
10.复杂表格界面的实现。由于VS自带的控件只能实现简单的图表界面,故在实现复杂界面时,如果堆积简单的LABEL,TEXTBOX等控件,则不仅费时,也加重程序运行负担,故需要借用第三方控件, 而大部分简便的控件都是收费的,如水晶报表,DevExpress,NOPI等,但是微软自已的控件dsoframer, 也可以实现类似功能,但操作可能相对复杂,即在调整界面时需要调整控件及EXCEL表格各自的相关属性,以实现项目要求。但dsoframer的使用一般先进行注册,即可以从微软下载后进行本机注册,也可以借助集成的注册程序进行注册。
10.1注册一下DSOFramer.ocx
操作:将DSOFramer.ocx复制到C:/windows/system32目录下,
开始->运行->regsvr32 DSOFramer.ocx , 系统会提示DSOFramer.ocx中的DllRegisterServer成功。
10.2添加DSOFramer.ocx到你的项目中,即添加COM组件到VS,即可在工具箱中发现安装成功。一般安装后需要重启,以便顺利使用。
10.3 dsoframer的主要属性, 即ActivationPolicy属性可以设置打开的文件是否处于激活状态;其它表头,边框调整等。
10.4 dsoframer的打开文件事件。即axFramerControl1.Open("文件名");
10.5由于dsoframer打开文件的方式是独占方式,故为了控制文件及时关闭,需要在关闭控件所在窗体时显式关闭该控件。即this.axFramerControl1.Close();
另外,由于该控件是直接打开的EXCEL文件,同时无法设置EXCEL的单元格背景色,故与效果图有些许差别。但相对于单元格错位来说,也基本达标吧。
11.有些项目在屏幕尺寸上有特殊要求,因此,如果开发平台的屏幕尺寸太小,可以在程序中定义一个尺寸基准变量,后续所有的尺寸以此为基础,而在正式提交前,可以更改此变量,从而更改所有的尺寸信息,当然,这也会增加程序启动的负担。
12. C#中是没有所谓的全局变量的.要构造出类的属性作为全局变量。
即可以在Globale类中写:public static string user = ""; //定义变量 user ="abcd";//赋值构造public string User { get { return user ; } set { user =value; }//定义属性。
这样就可以其他窗体访问此User MessageBox.Show (GlobalParams .User); //直接访问. 显示.结果:"abcd"
GlobalParams frm=new GlobalParams (); frm.User="efg"; //修改该静态变量的值 MessageBox.Show (GlobalParams .User); //直接访问. 显示.结果:"efg"
13.另外,在进行操作EXCEL时,注意相关的变量名不要与系统关键字等重复。如某一列的列名不能是password.
14.获得指定文件的目录:Path.GetDirectoryName(str);
15.创建文件。File.Create (String, Int32, FileOptions) 。
如果是创建EXCEL文件。则操作如下。
Microsoft.Office.Interop.Excel.Application excelApp;//声明创建EXCEL的操作程序的变量,即API。
excelApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook excelBook = excelApp.Workbooks.Add(1);//实际创建EXCEL。
Microsoft.Office.Interop.Excel.Worksheet excelSheet = (Microsoft.Office.Interop.Excel.Worksheet)excelBook.Worksheets[1];//创建EXCEL中的工作表。
excelApp.Visible = false;// EXCEL是否可见。
string path = Path.GetDirectoryName(str);//获取某个文件的目录。
excelBook.SaveAs(将EXCEL保存为。
excelSheet.Cells[1, 1] = "username";//给EXCEL标题栏赋值。
excelSheet.Cells[1, 2] = "pwdword";
excelSheet.Cells[1, 3] = "status";
excelSheet.Cells[1, 4] = "classno";
excelSheet.Cells[1, 5] = "cardno";
excelSheet.Cells[1, 6] = "loginno";
excelApp.DisplayAlerts = false;// 是否显示保存对话框。
excelApp.SaveWorkspace();//将EXCEL内容存储。
excelApp.Quit();//EXCEL程序退出。
16.SQL中的ExecuteNonQuery()只返回受影响的行数,因此,若进行查询操作,则一直返回0;此时需要进行ExecuteScalar()操作,以判断是否获取到值了。同时,由于SQL是一条记录一条记录地进行查询,因此,ExecuteScalar()返回的是所查询记录的第一个字段。 若需要获取其它字段,需要将记录复制到其他。
17. dataset中数据的访问格式:ds.Tables[0].Rows[0]["字段"]。
18.遍历某个文件夹下的文件方法。
string str=Path.GetDirectoryName(oper.Str);
DirectoryInfo TheFolder = new DirectoryInfo(str);
foreach (FileInfo NextFile in TheFolder.GetFiles())
19.在以EXCEL存储信息时,需要考虑文件的容量,从而需要考虑在容量不够时新建新的文件,从而引发了数据操作的复杂性。但也无非是多个条件判断而已。
20.C#中怎样取路径中的文件名?
FileInfo fi = new FileInfo("C:\\text.txt");
string strName = fi.Name; // text.txt
21.于C#中操作EXCEL后,关闭对象但EXCEL进程仍然存在的解决办法。设置excelApp.DisplayAlerts = false,就是不提问任何提示,这样再关闭EXCEL对象时,就不会有保存提示,也就不会卡住了.
经试验,此方法不完全正确,需要改进。
在利用SQL进行EXCEL查询,修改,插入,删除等操作时,无需建立EXCEL操作对象,只需建立连接,并及时关闭即可,因此,在此过程中没有多余的EXCEL进程存在的。
但若涉及到EXCEL对象,则需要及时关闭。即(EXCEL对象的名称)appname.quit();即可有效地关闭EXCEL对象及其进程。
若未有效关闭EXCEL进程,则无论系统中含有几个EXCEL进程,则若在程序外打开EXCEL,往往打开两个一样的界面,即原来有一个隐藏的EXCEL显示出来了。
但比较困难的是,若有此操作代码未有效关闭EXCEL进程,刚在后续关闭时较困难。可以手动在系统的进程管理中关闭,也可以在编写更复杂一些的代码进行关闭。