文章目录
技术内容一、新建DEVEXPRESS的RIBBON based项目1、安装SQLITE FOR EF2、添加EF拦截器的类SqliteInterceptor修改程序配置 App.config
二、添加spire.doc三、完善界面1、ribbon工具栏增按钮2、添加devexpress的tabcontrol控件作为窗体容器3、添加两个方法,功能是在tab中打开或显示子窗口4、为tabcontrol的CloseButtonClick添加代码:5、其它按钮事件绑定
四、添加其它窗体1、添加新建记录窗口 FrmNew写好POCO类添加窗体控件并绑定
2、添加一个显示列表窗体FrmList放置gridcontrol,为gridview设置属性:如果希望通过gridview的editform来进行编辑:显示主从表数据数据绑定表格右键菜单本窗体可以作为添加记录窗体,也可以做为修改记录窗体。
3、添加输出窗口FrmPrint
五、打包1、图标设置2、特定文件与生成3、NSIS安装脚本
技术内容
数据库:SQLITE。以SQLITE作为数据库,具有下支持XP,不用安装SQL等好处CODE FIRST:配置自由,不受建立数据库之累EF:使用方便,代码简单 DEVEXPRESS:界面美观,功能强大多窗体:以XtraTabControl作为容器,支持多窗体加载gridControl:列表功能强大NSIS打包:最常用的打包工具
一、新建DEVEXPRESS的RIBBON based项目
新项目为了兼容XP,合用.net4.0,安装其它组件时注意依赖情况
1、安装SQLITE FOR EF
安装如下 Package: System.Data.SQLite SQLite.CodeFirstSy
2、添加EF
项目中添加ADO.NET实体数据,选空CODE FIRST模型,创建数据上下文 MYDB.cs。 OnModelCreating方法必须被重写,以建立数据表格,这一步在用LOCALDB时是不用的 另外,因为EF解释contains或indexOf成的CHARINDEX函数在sqlite里面并不支持,所以要加一个拦截器实现函数
public class MYDB : DbContext
{
public MYDB() : base("name = MYDB")
{
System
.Data
.Entity
.Infrastructure
.Interception
.DbInterception
.Add(new SqliteInterceptor());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder
)
{
var initializer
= new SqliteDropCreateDatabaseWhenModelChanges<MYDB>(modelBuilder
);
Database
.SetInitializer(initializer
);
}
public DbSet
<Person
> People
{ get; set; }
}
拦截器的类SqliteInterceptor
public class SqliteInterceptor : IDbCommandInterceptor
{
private static Regex replaceRegex
= new Regex(@"\(CHARINDEX\((.*?),\s?(.*?)\)\)\s*?>\s*?0");
public void NonQueryExecuted(DbCommand command
, DbCommandInterceptionContext
<int> interceptionContext
)
{
}
public void NonQueryExecuting(DbCommand command
, DbCommandInterceptionContext
<int> interceptionContext
)
{
}
public void ReaderExecuted(DbCommand command
, DbCommandInterceptionContext
<DbDataReader
> interceptionContext
)
{
}
public void ReaderExecuting(DbCommand command
, DbCommandInterceptionContext
<DbDataReader
> interceptionContext
)
{
ReplaceCharIndexFunc(command
);
}
public void ScalarExecuted(DbCommand command
, DbCommandInterceptionContext
<object> interceptionContext
)
{
}
public void ScalarExecuting(DbCommand command
, DbCommandInterceptionContext
<object> interceptionContext
)
{
ReplaceCharIndexFunc(command
);
}
private void ReplaceCharIndexFunc(DbCommand command
)
{
bool isMatch
= false;
var text
= replaceRegex
.Replace(command
.CommandText
, (match
) =>
{
if (match
.Success
)
{
string paramsKey
= match
.Groups
[1].Value
;
string paramsColumnName
= match
.Groups
[2].Value
;
foreach (DbParameter param
in command
.Parameters
)
{
if (param
.ParameterName
== paramsKey
.Substring(1))
{
param
.Value
= string.Format("%{0}%", param
.Value
);
break;
}
}
isMatch
= true;
return string.Format("{0} LIKE {1}", paramsColumnName
, paramsKey
);
}
else
return match
.Value
;
});
if (isMatch
)
command
.CommandText
= text
;
}
}
修改程序配置 App.config
<entityFramework>
...
<providers>
<provider invariantName="System.Data.SQLite" (type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
....
<connectionStrings>
<add name="MYDB" connectionString="data source=.\sampledb.db" providerName="System.Data.SQLite" />
</connectionStrings>
</configuration>
二、添加spire.doc
nuget包中添加freespire.doc支持,可以实现doc输出功能
三、完善界面
1、ribbon工具栏增按钮
新增三个LARGE按钮,设好图标,功能btnList显示列表,btnNew新建记录,btnPrint打印列表
2、添加devexpress的tabcontrol控件作为窗体容器
拖一个imagecollection控件,设上一组图片,用于在tab左侧显示小图 设为dock,填充满窗体 ClosePageButtonShowMode属性设为InAllTabPageHeaders,在所有tab页上显示关闭按钮 ShowTabHeader设为TRUE显示tab头
3、添加两个方法,功能是在tab中打开或显示子窗口
窗口如果已加载,就切换过去,如果没有加载,就新建立一个子窗口
public void Add_TabPage(string tabText
, Type classType
)
{
if (!this.openTabCheck(this.tabMdiContener
, tabText
))
{
var myForm
=(DevExpress
.XtraEditors
.XtraForm
)classType
.Assembly
.CreateInstance(classType
.FullName
);
var tabpage
= this.tabMdiContener
.TabPages
.Add(tabText
);
tabpage
.Appearance
.Header
.Options
.UseFont
= true;
tabpage
.ImageOptions
.Image
= (Image
)imageCollection1
.Images
[0];
this.tabMdiContener
.SelectedTabPage
= tabpage
;
myForm
.FormBorderStyle
= FormBorderStyle
.None
;
myForm
.TopLevel
= false;
myForm
.Width
= tabpage
.Width
;
myForm
.Height
= tabpage
.Height
- 25;
myForm
.Show();
myForm
.Parent
= tabpage
;
myForm
.Anchor
= (AnchorStyles
)((AnchorStyles
.Top
| AnchorStyles
.Bottom
) | AnchorStyles
.Left
) | AnchorStyles
.Right
;
}
}
private bool openTabCheck(XtraTabControl tabControl
, string tabName
)
{
for (int i
= 0; i
< tabControl
.TabPages
.Count
; i
++)
{
if (tabControl
.TabPages
[i
].Text
== tabName
)
{
tabControl
.SelectedTabPageIndex
= i
;
return true;
}
}
return false;
}
4、为tabcontrol的CloseButtonClick添加代码:
private void tabMdiContener_CloseButtonClick(object sender
, EventArgs e
)
{
var EArg
= (DevExpress
.XtraTab
.ViewInfo
.ClosePageButtonEventArgs
)e
;
string name
= EArg
.Page
.Text
;
foreach (XtraTabPage apage
in this.tabMdiContener
.TabPages
)
{
if (apage
.Text
== name
)
{
((Form
)apage
.Controls
[0]).Close();
tabMdiContener
.TabPages
.Remove(apage
);
apage
.Dispose();
return;
}
}
}
5、其它按钮事件绑定
btnList.click:加载frmlist窗体作为子窗口,显示列表
private void barbtnList_ItemClick(object sender
, DevExpress.XtraBars.ItemClickEventArgs e
)
{
this.Add_TabPage("核算记录",typeof(FrmList
));
}
btnNew.click与btnPrint.click不再赘述
四、添加其它窗体
1、添加新建记录窗口 FrmNew
本窗口实现对象数据的添加
写好POCO类
选写一个POCO类:
public class DBTestClass
{
public int ID
{ get; set; }
public string name
{ get; set; }
public int age
{ get; set; }
}
在MYDB.CS中注册
public virtual DbSet
<DBTestClass
> DBTestClasses
{ get; set; }
添加窗体控件并绑定
FrmNew窗体中添加控件两个TEXTBOX ,一个errorprovider错误提示控件,一个databindingsource控件,一个保存按钮明一个DBTestClass 变量dbTestClass并实例化FORMLOAD中将bindingsource的datasource设成这个dbTestClass对象把TEXTBOX的属性面板中,设备高级数据绑定,text绑到bindingsource的name与age上,age格式设为数字保存按钮编写事件:
private void btnSave_Click(object sender
, EventArgs e
)
{
if (this.texbox1
.Text
== string.Empty
)
{
errorProvider1
.SetError(this.txbCompanyName
, "不得为空!");
return;
}
this.bindingSource1
.EndEdit();
db
.DBTestClass
.Add(this.dbTestClass
);
db
.SaveChanges();
MessageBox
.Show("保存成功!");
}
2、添加一个显示列表窗体FrmList
该窗体实现列表显示数据库内容,可以删除、弹窗修改记录、重新加载数据
放置gridcontrol,为gridview设置属性:
this.gridView1
.OptionsBehavior
.Editable
= false;
this.gridView1
.OptionsBehavior
.ReadOnly
= true;
this.gridView1
.OptionsCustomization
.AllowFilter
= false;
this.gridView1
.OptionsFind
.AlwaysVisible
= true;
this.gridView1
.OptionsFind
.FindNullPrompt
= "输入搜索...";
this.gridView1
.OptionsFind
.ShowClearButton
= false;
this.gridView1
.OptionsFind
.ShowFindButton
= false;
this.gridView1
.OptionsView
.ShowGroupPanel
= false;
this.gridView1
.OptionsMenu
.EnableColumnMenu
=false;
this.gridView1
.OptionsCustomization
.AllowFilter
=false;
this.gridView1
.OptionsSelection
.EnableAppearanceFocusedCell
= false;
this.gridView1
.OptionsSelection
.EnableAppearanceFocusedRow
= false;
如果希望通过gridview的editform来进行编辑:
editform是devexpress中gridcontrol的一个修改行数据的行为模式,避免再开一个窗体修改数据,尤其是数据格式比较简单的时候。 开启editform模式:
this.gridView1
.OptionsBehavior
.AllowAddRows
= DevExpress
.Utils
.DefaultBoolean
.True
;
this.gridView1
.OptionsBehavior
.EditingMode
= DevExpress
.XtraGrid
.Views
.Grid
.GridEditingMode
.EditFormInplace
;
this.gridView1
.OptionsBehavior
.EditorShowMode
= DevExpress
.Utils
.EditorShowMode
.Click
;
另外,如果在加载表格数据时,需要对数据进行过滤,需要将过滤出来的数据重新构造bindinglist,然后绑室databindingsource。
this.objBindingSource
.DataSource
= new BindingList<DBTestClass>(db
.DBTestClass
.Where(p
=> p
.CompanyID
== this.companyID
).ToList());
但这样一来,新增的行数据无法保存,需要另外进行处理,比如在editform的rowupdated事件中添加处理方法:
private void gridView1_RowUpdated(object sender
, DevExpress.XtraGrid.Views.Base.RowObjectEventArgs e
)
{
this.objBindingSource
.EndEdit();
var p
= (DBTestClass
)e
.Row
;
if (p
.ID
== 0)
{
db
.DBTestClass
.Add(p
);
}
db
.SaveChanges();
}
显示主从表数据
列表中有时会有一些字段是集合型,比如“部门员工”employee字段,如果想显示部分员工名字,就需要写一下view的CustomColumnDisplayText事件
private void gridView1_CustomColumnDisplayText(object sender
, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e
)
{
if (e
.Column
.FieldName
== "employee")
{
var b
= e
.Value
;
if (b
== null) return;
var d
= (HashSet
<employee
>)b
;
e
.DisplayText
= "";
foreach (var f
in d
)
{
e
.DisplayText
= f
.name
+ ",";
}
}
}
或是在数据源初始化时就把员工姓名提取出来放在字段中。
数据绑定
选中GRID,右上角,data source wizard,按向导设置好数据绑定,我一般选择通过bindingsource进行绑定。系统自动在窗体初始化代码中加入数据绑定代码。 在designer中调整列的表现形式。
表格右键菜单
添加barManager1,这是用于管理devexpress的工具栏、菜单栏、右键菜单、状态栏的添加popupMenu1,进入designer,添加项(删除、修改、刷新),添加事件处理程序在gridview的RowClick中添加右键弹出菜单功能:
private void gridView1_RowClick(object sender
, DevExpress.XtraGrid.Views.Grid.RowClickEventArgs e
)
{
if (e
.Button
== MouseButtons
.Right
&& ModifierKeys
== Keys
.None
)
{
Point p
= new Point(Cursor
.Position
.X
, Cursor
.Position
.Y
);
popupMenu1
.ShowPopup(p
);
}
}
本窗体可以作为添加记录窗体,也可以做为修改记录窗体。
声明几个变量:
public int id
;
public DBTestClass dbTestClass
;
public bool isNew
= true;
有时需要在本窗体中添加一个退出功能,实现子窗体的自我关闭:
var parentTabCtl
= (DevExpress
.XtraTab
.XtraTabControl
)this.Parent
.Parent
;
var page
= parentTabCtl
.SelectedTabPage
;
parentTabCtl
.TabPages
.Remove(page
);
this.Close();
如果在建立或修改记录时需要用到一对多,比如为部门depart选择员工employee,可以用checkedcomboboxedit控件。 在修改时需要初始化赋值(假设部门员工列表存在(list)depart.employee中:
this.checkboxdepart
.Properties
.DataSource
= db
.employee
.ToList();
this.checkboxdepart
.Properties
.DisplayMember
= "name";
this.checkboxdepart
.Properties
.ValueMember
= "ID";
this.checkboxdepart
.SetEditValue(string.Join(",", this.depart
.employee
.Select(p
=> p
.ID
).ToArray()));
读取选择结果checkboxDepart_EditValueChanged事件:
if (this.checkboxdepart
.EditValue
== this.checkboxdepart
.OldEditValue
) return;
var selectedMen
=this.checkboxdepart
.EditValue
.ToString().Split(',');
this.depart
.employee
= new List<employee>();
foreach(var menid
in selectedMen
)
{
this.depart
.employee
.Add(db
.employee
.Find(int.Parse(menid
)));
}
}
3、添加输出窗口FrmPrint
public class PrintRst
{
public OtherCost otherCost
;
public void Print()
{
if (this.otherCost
== null) return;
startPrint();
}
public void Print(int id
)
{
using (var db
= new MYDB())
{
var cost
= db
.OtherCosts
.Find(id
);
if (cost
== null) return;
this.otherCost
= cost
;
}
startPrint();
}
private void startPrint()
{
Document document
= new Document();
Section section
= document
.AddSection();
section
.AddParagraph();
var paraTitle
= section
.AddParagraph();
paraTitle
.Format
.HorizontalAlignment
= Spire
.Doc
.Documents
.HorizontalAlignment
.Center
;
TextRange txtRange
= paraTitle
.AppendText("核算报表");
txtRange
.CharacterFormat
.FontName
= "宋体";
txtRange
.CharacterFormat
.Bold
= true;
txtRange
.CharacterFormat
.FontSize
= 24;
section
.AddParagraph();
addText(section
, "名称", otherCost
.CompanyName
);
addText(section
, "联系人", otherCost
.CompanyName
);
var docFilename
= @"WordTable" + GetTimeStamp() + ".docx";
document
.SaveToFile(docFilename
);
System
.Diagnostics
.Process
.Start(docFilename
);
}
private void addText(Section section
, string desc
, string value)
{
var paragraph
= section
.AddParagraph();
paragraph
.Format
.BeforeSpacing
= 20;
var txtRange
= paragraph
.AppendText(desc
+ ":" + value);
txtRange
.CharacterFormat
.FontName
= "宋体";
txtRange
.CharacterFormat
.FontSize
= 16;
}
private string GetTimeStamp()
{
TimeSpan ts
= DateTime
.UtcNow
- new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert
.ToInt64(ts
.TotalSeconds
).ToString();
}
}
五、打包
1、图标设置
找一个图标PNG文件,256X256以上尺寸,在http://ico.duduxuexi.com/网站生成ICO文件,复制到项目中。主窗体ICO设为此ICO项目属性中在“应用程序”面板,图标和清单,将ICO文件设好,这个图标即是程序运行时的图标
2、特定文件与生成
在项目中添加部分文件:lincens.txt文件写好,这是安装时的用户协议文件,readme或help文件写好,dotNetFx40_Client_x86_x64.exe文件也复制到项目中。三个文件的属性面板中,生成操作设为“无”,复制到输出目录设为“如果较新就复制” 执行“生成项目”在debug目录生成程序文件
3、NSIS安装脚本
用HM NIS EDIT生成安装脚本,完善内容,并添加.net检测,保存脚本到debug目录,脚本选项选中“转为相对路径” 编译、安装看一下。没问题的话,脚本文件复制到项目目录,同样生成操作设为“无”,复制到输出目录设为“如果较新就复制”,这样在debug目录清空的情况下,每次生成还会复制过去。以后再修改程序,最多改一下文件列表