vs2005+ActiveReportsNet2
承接上节,最终出来的效果是这样的(见图),可以看出布局不能满足我们平时帐票的要求,至少各个TextBox之间不能有间距吧?
<!--[if !vml]-->
<!--[endif]-->
希望有的效果:
<!--[if !vml]--><!--[endif]-->
最简单的方法,当然是直接在rpt界面上拖动控件,把布局弄好就可以了,这对于简单的报表当然没有问题。需要布局的有:textbox等控件,pageHeader/footer,GroupHeader/Footer,Detail等。
不过这样会有很多问题:
(1),首先,比较容易误操作,尤其是在一些比较精密的帐票中,用户是不会允许哪怕1毫米的误差。
(2),还有,假如直接在rpx上布局的话,会把模版弄的很紧凑,几个控件比较难区分,假如我用代码来布局的话,模版上的布局就只需要大致有个样子,看起来清楚多了。
(3),更重要的是如果有子模版的情况下。SubReport的宽度是很难确定的。主模版上的SubReport的控件宽度应该是和它对应的子模版(subX.rpx)的宽度相等的,但你在画主模版的时候,是确定不了子模版的宽度的(子模版可能会分栏,但ColumnCount我们有时候是不能确定的,要根据传入的数据源确定的)
在我们的项目中,一般要求在代码中控制布局(除了一些特殊的特注帐票)。
1,ok,下面就说说用代码控制布局的具体方法:(字数比较多,可以结合步骤2的代码来看)
(1),创建各个主/子模版,把上面的控件大致布局好(恩,装个样子就可以了)
先处理子模版。无子模版直接到(7)。
(2),设置子模版上所有控件的宽度。见注1。
(3),对子模版上的所有控件排位置。
(4),设置子模版的groupHeader/groupFooter,detail(包括height,detail的columnCount)(用于布局),以及PrintWidth(用于打印出实际的宽度)
(5),子模版创建一个ReadOnly Property ReportWidth(该属性return的就是该模版具体要打印出来的宽度,比如sub1.rpx的宽度是2个textbox的总宽度;sub2.rpx的宽度是txtScore的宽度)。
这里也可以把分栏考虑进去,这样的话,sub1的宽度就是2个textbox宽度*1;sub2的宽度就是txtScore.width*2。
考虑分栏的话,需要注意的是,我们举例的这张帐票,page1的sub2只有1列,而page2却有2列,所以宽度不能简单的用txtScore.widh*2。最好这里不考虑分栏,而把分栏放到主模版里的GroupHeader1_Format事件中处理。(不过我们项目中每页的分栏数不同的帐票好像很少有)
本例在sub report的reportwidth中没有考虑分栏。
(6),子模版如果分栏,并且栏数不确定(根据用户的选择,栏数是动态的)。则创建一个public属性RealColumnCount,主模版根据数据源,把真正的ColumnCount传给子模版。
如果字模版分栏,但是是固定的,(比如栏是月份,固定12),大可以把detail的columnCount设死。
处理主模版
(7),在GroupHeader1_format事件中设置子报表。
(8),设置主模版上所有控件的宽度(不包括subReport控件)。见注1。
(9),如果有subreport的话,计算它们的宽度。
1)如果不分栏,宽度就是子模版的ReportWidth
2)如果分栏,但栏数是静态固定的(比如12),则宽度就是子模版ReportWidth*12
3)如果分栏,栏数是动态的,但每页都是一样的(比如A,B 2班,任意门科目的成绩, 虽然,科目数目不确定,但每班要么都显示3门,要么都显示4门)
这种情况,先在ReportStart事件中通过数据源算出columnCount,然后宽度就是子模版ReportWidth*columnCount
4)如果分栏,栏数是动态的,而且每页都可能不一样,比如本例,page1只有1列,page2 却有2列。
这种情况,可以把subReport的宽度放到GroupHeader1_Format事件中去计算,计算方法同3),这样的话,每页都会把subReport的宽度重新算一下。
本例的情况属于4)。一般可以用3)的话,尽量不用4),4)由于每个page都要重绘,效率比较低。
(10),对主模版上的所有控件排位置。(如果subReport是在GroupHeader1_format事件中计算的,则,排位置也要在该事件中处理,并不绝对)
(11),设置主模版的PageHeader/PageFooter,GroupHeader/GroupFooter,Detail。以及整个模版的PrintWidth。
注1:我们项目中对各种用途的控件宽度都有规定的,比如生徒氏名8单位宽度,科目名6单位宽度。具体宽度写在公共类里了,到时调用一下就可以了。
本文就略过这步了,textbox和label宽度就直接在画面上拉。
2,接下去,我们实际来做!
本节的代码由上节的代码修改而成,前面的模版、控件创建,数据源准备等都跳过。
直接看看具体子模版和主模版中的代码(代码中我会用Setp x来指明对应的具体步骤):
1)Sub1.vb
<!--[if !vml]-->
<!--[endif]-->
Private
Sub
sub1_ReportStart(
ByVal
sender
As
Object
,
ByVal
e
As
System.EventArgs)
Handles
Me
.ReportStart
'
<--------Step (2)---------->
'
Set contols' width
'
me.lblID.Width=xxx
'
'
'
<--------Step (2)---------->
'
<--------Step (3)---------->
'
Arrange contols
Me
.lblID.Left
=
0
Me
.lblID.Top
=
0
Me
.lblName.Left
=
Me
.lblID.Left
+
Me
.lblID.Width
Me
.lblName.Top
=
Me
.lblID.Top
Me
.txtID.Left
=
0
Me
.txtID.Top
=
0
Me
.txtName.Left
=
Me
.txtID.Left
+
Me
.txtID.Width
Me
.txtName.Top
=
Me
.txtName.Top
'
<--------Step (3)---------->
'
<--------Step (4)---------->
'
Set all sections & PrintWidth
Me
.PageHeader.Height
=
0
Me
.GroupHeader1.Height
=
Me
.lblID.Height
Me
.Detail.Height
=
Me
.txtID.Height
'
Set columnCount
Me
.Detail.ColumnCount
=
1
'
Set PrintWidth
Me
.PrintWidth
=
Me
.lblName.Left
+
Me
.lblName.Width
'
<--------Step (4)---------->
End Sub
'
<--------Step (5)---------->
Public
ReadOnly
Property
ReportWidth()
As
Single
Get
Return
Me
.txtID.Width
+
Me
.txtName.Width
End
Get
End Property
'
<--------Step (5)---------->
2)sub2.vb
<!--[if !vml]-->
<!--[endif]-->
需要注意,这里我把detail的columnCount改成1了,在代码中去修改它。
3)主模版rpt1.vb
<!--[if !vml]-->
<!--[endif]-->
这里的代码有点复杂,主要由于每页都要动态求sub2的宽度,否则GroupHeader1_Format事件中只需要生成子报表,其他布局全可以写在rpt1_ReportStart事件中了。
Private
Sub
GroupHeader1_Format(
ByVal
sender
As
Object
,
ByVal
e
As
System.EventArgs)
Handles
GroupHeader1.Format
'
<--------Step (7)---------->
'
subReport1
Dim
sub1
As
New
sub1
Me
.subReport1.Report
=
sub1
'
為子報表設置数据源
Dim
dvStudent
As
DataView
=
New
DataView(
CType
(
Me
.DataSource, DataSet).Tables(
0
),
"
Class='
"
&
Me
.Fields(
"
Class
"
).Value.ToString
&
"
'
"
,
""
, DataViewRowState.CurrentRows)
Me
.subReport1.Report.DataSource
=
dvStudent
'
subReport2
Dim
sub2
As
New
sub2
Me
.subReport2.Report
=
sub2
'
為子報表設置数据源
Dim
dvScore
As
DataView
=
New
DataView(
CType
(
Me
.DataSource, DataSet).Tables(
1
),
"
Class='
"
&
Me
.Fields(
"
Class
"
).Value.ToString
&
"
'
"
,
""
, DataViewRowState.CurrentRows)
With
CType
(
Me
.subReport2.Report, sub2)
.DataSource
=
dvScore
'
Set subReport's ColumnCount
.RealColumnCount
=
CInt
(dvScore.Count
/
dvStudent.Count)
End
With
'
<--------Step (7)---------->
'
<--------Step (9)---------->
Me
.subReport1.Width
=
sub1.ReportWidth
Me
.subReport2.Width
=
sub2.ReportWidth
*
CInt
(dvScore.Count
/
dvStudent.Count)
'
<--------Step (9)---------->
'
<--------Step (10)---------->
Me
.subReport2.Left
=
Me
.subReport1.Left
+
Me
.subReport1.Width
Me
.subReport2.Top
=
Me
.subReport1.Top
'
<--------Step (10)---------->
'
<--------Step (11)---------->
'
Set PrintWidth
Me
.PrintWidth
=
Me
.subReport2.Left
+
Me
.subReport2.Width
'
<--------Step (11)---------->
End Sub
Private
Sub
rpt1_ReportStart(
ByVal
sender
As
Object
,
ByVal
e
As
System.EventArgs)
Handles
Me
.ReportStart
'
<--------Step (8)---------->
'
Set contols' width
'
me.lblClass.Width=xxx
'
'
'
<--------Step (8)---------->
'
<--------Step (10)---------->
'
Arrange Controls.
Me
.lblClass.Left
=
0.5
Me
.lblClass.Top
=
0.5
Me
.txtClassNo.Left
=
Me
.lblClass.Left
+
Me
.lblClass.Width
Me
.txtClassNo.Top
=
Me
.lblClass.Top
Me
.subReport1.Left
=
Me
.lblClass.Left
Me
.subReport1.Top
=
Me
.lblClass.Top
+
Me
.lblClass.Height
'
<--------Step (10)---------->
'
<--------Step (11)---------->
'
Set all sections & PrintWidth
Me
.PageHeader.Height
=
Me
.Label1.Height
Me
.Detail.Height
=
0
'
<--------Step (11)---------->
End Sub
3,最终的效果。
<!--[if !vml]-->
<!--[endif]-->
<!--[if !vml]-->
Private
Sub
sub2_ReportStart(
ByVal
sender
As
Object
,
ByVal
e
As
System.EventArgs)
Handles
Me
.ReportStart
'
<--------Step (2)---------->
'
Set contols' width
'
me.lblSubject.Width=xxx
'
'
'
<--------Step (2)---------->
'
<--------Step (3)---------->
'
Arrange contols
Me
.lblSubject.Left
=
0
Me
.lblSubject.Top
=
0
Me
.txtScore.Left
=
0
Me
.txtScore.Top
=
0
'
<--------Step (3)---------->
'
<--------Step (4)---------->
'
Set all sections & PrintWidth
Me
.PageHeader.Height
=
0
Me
.GroupHeader1.Height
=
Me
.lblSubject.Height
Me
.Detail.Height
=
Me
.txtScore.Height
'
Set columnCount
Me
.Detail.ColumnCount
=
Me
._realColumnCount
'
Set PrintWidth
Me
.PrintWidth
=
(
Me
.txtScore.Left
+
Me
.txtScore.Width)
*
Me
._realColumnCount
'
<--------Step (4)---------->
End Sub
'
<--------Step (5)---------->
Public
ReadOnly
Property
ReportWidth()
As
Single
Get
Return
Me
.txtScore.Width
End
Get
End Property
'
<--------Step (6)---------->
Private
_realColumnCount
As
Int32
=
1
Public
Property
RealColumnCount()
As
Int32
Get
Return
_realColumnCount
End
Get
Set
(
ByVal
value
As
Int32)
Me
._realColumnCount
=
value
End
Set
End Property
'
<--------Step (6)---------->