该窗体是保证系统使用的安全性,所以实现起来就要考虑很多因素。
对于用户名,我无法知道该用户使用的名称,但是能够了解到该用户给计算机设定的用户名,于是我采用了windows系统函数“getusernamea”来获取用户名,上图所示的用户名“summer”是我的计算机名称。
该窗体的代码如下:
private declare function getusername lib "advapi32.dll" alias "getusernamea" (byval lpbuffer as string, nsize as long) as long
public ok as boolean
private sub form_load()
dim sbuffer as string
dim lsize as long
sbuffer = space$(255)
lsize = len(sbuffer)
call getusername(sbuffer, lsize) ‘调用api 函数得到计算机用户名
if lsize > 0 then
txtusername.text = left$(sbuffer, lsize)
else
txtusername.text = vbnullstring
end if
end sub
private sub cmdcancel_click() ‘取消操作
ok = false
me.hide
end sub
private sub cmdok_click() ‘判断是否输入了正确的密码
'todo: create test for correct password
'check for correct password
if txtpassword.text = "123" then
ok = true
me.hide
else
msgbox "invalid password, try again!", , "login"
txtpassword.setfocus
txtpassword.selstart = 0
txtpassword.sellength = len(txtpassword.text)
end if
end sub
下图是该系统的主窗体:
图5.3主窗体示图
为了美观,我加入了一幅图片,并在窗体的下方放置了状态栏控件,用来显示本系统的版本信息、日期和时间,以方便用户的使用。
系统中的主要操作项是由菜单来实现的,这样的好处是纯中文信息,不会带来使用上的不便。因为功能模块的数量很多,如果使用工具栏来实现,一时带来记忆上的复杂性,二是容易产生操作上的失误。
对于主窗体,我们能按照正规商业软件的形式进行设计,我们考虑了软件帮助文档,虽然由于时间有限我们没能将帮助文档的内容填充进去,但是我们宏观上的设计并没有出现疏忽。
还有版权的问题,我们提出了版权的问题,这样在今后的使用和维护中不会产生法律问题,我们认为在一些细节上我们能考虑到,这对我们将来是有很多好处的。
在系统使用中,最常用的是显示信息,就如下图进货单设置信息窗体:
其中最为醒目的就是表格的显示了,几乎所有功能模块中都有这个控件的使用,而且在任何信息管理系统中,它都是非常主要的,所以我们详细介绍一下。
图5.4表格使用示图
5.4.2 表格控件总览:
在vb开发环境中,表格控件在界面开发元素中占有重要的地位。它不仅有外观整洁、表达形式规范的优点,而且更重要的是它较高的信息表现率(就是相对于其他控件来说能够表达更多的信息),随着信息时代的到来,它的应用将更加的广泛。
那么在vb平台下,如何操作这一功能强大的控件元素呢?事实上我们知道,vb平台下面的表格控件是相当丰富的,总结下来提供了4种类型:microsoft data bound grid control、microsoft datagrid control、microsoft flexgrid control、microsoft hierarchial flexgrid control.
这四种表格各有其特点,下面我们只简单介绍我们使用的microsoft flexgrid control。
这里重点介绍这种控件,因为在实际开发中,这两控件应用的场合更多一些,它不仅能够反映数据,而且也能把数据的修改信息反映到数据库中去,所以弥补了上述这种控件的不足。
如果数据不需要修改,那么可以进行绑定操作,其方法跟前面介绍的完全一样,就是通过设置datasource属性来完成数据的显示工作。但是实际开发中,需要对整个表格控件更为灵活的显示控制。
在给出例程之前,有必要对这一控件进行比较详尽的认识:这种控件允许将文本或者图像放置于每个单元格之中,控件的row与col属性允许用户在代码中指定当前行和列,当然也可通过操作鼠标和键盘来改变这两个属性,而text属性指明当前单元格的文本。如果单元格的文本太长而不能完全显示出来的话,可以通过将wordwrap属性设置为true来达到显示的目的。下面将比较重要的属性小结如下:
datasource---------用来指定需要绑定的数据源,比如data控件。
cellpicture----------用来设定当前单元格的图像,便于显示该图像。此属性不能在设计时使用。
col,row---------------设定当前列和当前行,注意它们均是从0开始的,如果同时设定它们,可以指定当前的单元格。设计时也不能使用。
cols,rows---------------设置表格控件总的列数和行数。
hwnd---------------表格句柄,可以结合windows api对表格控件进行更高级的控制。
text---------------指定当前的单元格的文本内容。
textmatrix(i,j)-------此属性比较重要,它用来指定第i行和第j列所确定的单元格的文本内容。它等价于下面的代码:
mshflexgrid1.rows =i
mshflexgrid1.cols =j
mshflexgrid1.text =指定的字符串
wordwrap-------为ture时可以在当前单元格换行显示,否则如果要显示的字符的长度超过列宽,那么就不能显示完全。
以上只是介绍了表格控件经常使用的属性,还有很多其他重要的属性这里限于篇幅就不详细讲解了。我们它主要显示后台数据库中的信息,这样非常直观,我们将在附录部分给出该窗体的完整代码。
5.4.3 模块功能的具体实现
该模块的添加和修改功能通过调用一个共同窗体来实现。这种方法可以使软件更简洁,更节省空间。
添加功能由下面的代码来实现:
private sub cmdadd_click()
gintdhmode = 1
frmdh1.show 1
end sub
修改功能由下面的代码来实现:
private sub cmdmodify_click()
dim intcount as integer
if frmdh.msglist.rows > 1 then
gintdhmode = 2
intcount = msglist.row
if intcount > 0 then
frmdh1.txtsql = "select * from dh where dh_no ='" & trim(msglist.textmatrix(intcount, 1)) & "'"
frmdh1.show 1
else
msgbox "警告", vbokonly + vbexclamation, "请首先选择需要修改的纪录!"
end if
end if
end sub
图5.5 保存窗体示图
这个相同的窗体实现的是保存功能,代码如下:
private sub cmdsave_click()
dim intcount as integer
dim smeg as string
dim msgtext as string
for intcount = 1 to 8
if trim(txtitem(intcount) & " ") = "" then
select case intcount
case 1
smeg = "订货单号"
case 2
smeg = "供应商代码"
case 3
smeg = "商品代码"
case 4
smeg = "有效期限"
case 5
smeg = "数量"
case 6
smeg = "订货单价"
case 7
smeg = "折扣比例"
case 8
smeg = "总金额"
end select
smeg = smeg & "不能为空!"
msgbox smeg, vbokonly + vbexclamation, "警告"
txtitem(intcount).setfocus
exit sub
end if
next intcount
for intcount = 0 to 2
if trim(combo1(intcount) & " ") = "" then
select case intcount
case 0
smeg = "业务员"
case 1
smeg = "供应商名称"
case 2
smeg = "商品名称"
end select
smeg = smeg & "不能为空!"
msgbox smeg, vbokonly + vbexclamation, "警告"
combo1(intcount).setfocus
exit sub
end if
next intcount
if not isdate(format(trim(txtitem(4)), "yyyy-mm-dd")) then
msgbox "药品的有效期限请输入日期!", vbokonly + vbexclamation, "警告"
txtitem(4).setfocus
exit sub
end if
for intcount = 5 to 7
if not isnumeric(trim(txtitem(intcount))) then
smeg = "请输入数字!"
msgbox smeg, vbokonly + vbexclamation, "警告"
txtitem(intcount).setfocus
exit sub
end if
next intcount
'添加判断是否有相同的id记录
if gintdhmode = 1 then
txtsql = "select * from dh where dh_no ='" & trim(txtitem(1)) & "'"
set mrc = executesql(txtsql, msgtext)
if mrc.eof = false then
msgbox "已经存在此订单编号的记录!", vbokonly + vbexclamation, "警告"
txtitem(1).setfocus
exit sub
end if
mrc.close
end if
'先删除已有记录
txtsql = "delete from dh where dh_no ='" & trim(txtitem(1)) & "'"
set mrc = executesql(txtsql, msgtext)
'再加入新记录
txtsql = "execute dh_setup '"
txtsql = txtsql & trim(txtitem(0)) & "','"
txtsql = txtsql & trim(combo1(0).itemdata(combo1(0).listindex)) & "','"
for intcount = 1 to 3
txtsql = txtsql & trim(txtitem(intcount)) & "','"
next intcount
for intcount = 4 to 8
txtsql = txtsql & trim(txtitem(intcount)) & "','"
next intcount
txtsql = txtsql & trim(txtitem(9)) & "'"
set mrc = executesql(txtsql, msgtext)
if gintdhmode = 1 then
msgbox "添加记录成功!", vbokonly + vbexclamation, "添加记录"
for intcount = 0 to 9
txtitem(intcount) = ""
next intcount
for intcount = 0 to 2
combo1(intcount).listindex = 0
next intcount
txtitem(0) = format(now, "yyyy-mm-dd")
mblchange = false
unload frmdh
frmdh.txtsql = "select dh_no,in_date,ywman,gfdm,wzdm,cxend,sl,in_danj,i_zk,i_zke,bz from dh"
frmdh.show
elseif gintdhmode = 2 then
unload me
unload frmdh
frmdh.txtsql = "select dh_no,in_date,ywman,gfdm,wzdm,cxend,sl,in_danj,i_zk,i_zke,bz from dh"
frmdh.show
end if
end sub
整个系统基本上都是由上述三种功能组合来实现,所以仔细分析上述代码是理解本系统的捷径。还有一个功能模块就是打印报表。
下面让我们来介绍一下报表对象的使用。
报表如图所示:
图5.6 报表示图
首先介绍一下datareport对象的几个常用属性。一是datasource,用于设置一个数据源,通过该数据源,数据使用者被绑定到一个数据库;二是datamember,从datasource提供的几个数据成员中设置一个特定的数据成员;三是leftmargin、rightmargin、topmargin、bottommargin等,用于指定报表的左右上下的页边距;四是sections,即datareport的报表标头、页标头、细节、页脚注、报表脚注5个区域,如果加上分组(可以有多层分组),则增加一对区域,即分组标头、分组脚注。其中datasource一般是一个数据环境或是adodb.connection类型的变量,而datamember则对应数据环境中的command或是adodb.recordset类型的变量,推荐使用数据环境及command,页边界大家肯定都很清楚,下面我主要介绍以下sections,这也是datareport的精髓所在。
sections是一个集合,您可以为每一个section指定名称,也可以用其缺省的索引,从上到下依次为1、2…。每个section均有height和visible属性,您可以在一定条件下使一个section不可见。在section中可以放置各种报表控件,其中rptlabel、rptimage、rptshape和rptline可以放在任意的section中,用于输出各种文字、图形及表格线;rpttextbox只能放在细节中,一般用于绑定输出datamemeber提供的数据字段;rptfunction只能被放置在分组注脚中,用于输出使用各种内置函数计算出的合计、最大值、最小值、平均值、记数等等。上述报表控件中常用公共属性有用于控制位置及高度宽度的top、left、height、width和控制可见性的visible;其中rpttextbox还有datafield、datamember、dataformat及font属性;其他属性不再多说。
然后介绍一下我的使用经验。一是对想控制的报表控件按类型有规律的命名;二是用rptshape的矩形框做表格线框,比用rptline画框省事多了,只有斜线才使用rptline;三是报表标题及报表中的表头文字、日期及页码用rptlabel,其中caption属性支持转义字符,%d为长格式日期,%d为短格式日期,%p为总页数,%p为当前页码;四是对固定报表在设计窗口直接将报表控件摆放到位,对于活报表,应首先考虑报表最大的情形,将足够的控件分别放置在不同区域,位置大小可以不必深究,然后在报表输出前用vba代码对所有控件的属性进行调整,包括位置、高度、宽度、字体、对齐方式、显示格式、可见性等等,相应的对section也应根据情况调整其高度和可见性。
报表是常用的控件,所以我想有必要介绍一下它的实现过程。
首先是连接数据库
with 数据环境.rscommand名
if .state = adstateopen then .close
.source = sql语句
.open 打开想输出的数据库数据项以便输出
end with
with 报表名
.datasource=数据环境
.datamember=command名 这两行也可固定设好而不必每次设置
设置页表头部分(rpttlabel…为报表控件名)
.sections(2).controls("rptlabelpage").caption = "共%p页第%p页"
.sections(2).controls("rptlabeldate").caption = "打印日期:%d"
.sections(3).controls("rptlabel1").left=…
设置细节部分(rptshapex、rpttextboxx为报表控件名)
.sections(3).controls("rptshape1").left=…
.sections(3).controls("rptshape1").top=…
.sections(3).controls("rptshape1").height=…
.sections(3).controls("rptshape1").width=…
.sections(3).controls("rpttextbox1").datamember=command名
.sections(3).controls("rpttextbox1").datafield=字段1
.sections(3).controls("rpttextbox1").font.name=…
.sections(3).controls("rptshapen").visible=false
.sections(3).controls("rpttextboxn").visible=false
. sections(3).height=计算出的或固定的细节高度
动态调整报表标题(rptlabeltitle为报表标签控件名)
.sections(2). controls("rptlabeltitle").left=…
这样做的优点是报表设计时简单,调整方便、随意,只需更改一点代码,而不必为了一点点的修改而费神的在设计窗口调整半天。
该系统最后一个主要的功能就是查询了,下面是查询的实现:
图5.7 查询功能示图
private sub cmdok_click()
dim dbegindate as string
dim denddate as string
dim sqsql as string
sqsql="select inh.in_date,inh.ywman,inh.inh_no,dm_gf.dm,dm_gf.mc,dm_ck.dm,dm_ck.mc,dm_wz.dm,dm_wz.mc,inh.cxend,inh.sl,inh.in_danj,inh.i_zk,inh.i_zke,inh.jsfk,inh.jz,inh.yf,inh.bz from inh inner join dm_gf on inh.gfdm = dm_gf.dm inner join dm_ck on inh.ckdm = dm_ck.dm inner join dm_wz on inh.wzdm = dm_wz.dm where inh.ywman = '" & trim(combo1(0).itemdata(combo1(0).listindex)) & "'"
if chkdate.value = vbchecked then
dbegindate = format(cdate(cboyear(0) & "-" & cbomonth(0) & "-" & cboday(0)), "yyyy-mm-dd")
denddate = format(cdate(cboyear(1) & "-" & cbomonth(1) & "-" & cboday(1)), "yyyy-mm-dd")
sqsql = sqsql & " and inh.in_date >= '" & dbegindate & " '"
sqsql = sqsql & " and inh.in_date <= '" & denddate & " '"
end if
if trim(sqsql) = "" then
msgbox "请设置查询条件!", vbokonly + vbexclamation, "警告"
exit sub
else
frminquireywy.txtsql = sqsql
me.hide
frminquireywy.show 0
end if
end sub
以上就是我的毕业设计的全部功能,其他模块的编写就是上述几个功能的合集。整个过程中就是添加、修改和删除以及表格控件和报表的使用,这让我很好的了解到真正的软件是可以完整的实现用户的要求,而不是用华丽的外表和不同的实现方法来取悦用户。试想用data控件、ado控件和ado对象分别来实现不同模块的添加、修改和删除功能,没有任何实际的意义,反而给自己带来编码的复杂性。让我更是消除了我的困惑。因为我一直都认为我知道得太少,不能够完成工作上的复杂任务,但实际上,这个毕业设计当中的大部分知识和工具的使用我以前都是知道的,只是没有想到完成一个软件就是几个知识点的重复利用,这也让我明白了软件开发中重用的真正意义。
当我按照软件工程的步骤进行开发时,我都没有想到会有什么不同发现,没想到,把详细设计完整的做好,竟然没有花费我太多的时间来编码,基本上是很顺利的完成了。编码让我有了如鱼得水的感觉,突然间,我发现我已经可以将大学里学习的知识融合在一起了,这时我才发现,每个课程都是很有用的,编码只是其中最为简单的部分,向软件工程等课程背后所包含的宏观思想才是真正需要我们掌握的。作者: 罗里 时间: 2006-6-6 15:06