分页加载与流水账
本节的内容,可以参考CaseStudy目录下的文件:分页流水账.Table
关于流水账的设计,请参考:高效率的流水账
由于分页加载往往意味着大数据量、网络环境、多用户等元素,所以上述的流水账设计方案将不再适用,需要重新 设计。
好消息是分页加载同时也意味着每次加载的数据量有限,我们可以充分利用这个优势,在每次加载数据后重算余额,其实这样的设计更加符合实际应用的需求。
示例一
同样假定有个简单的流水帐式的表格,希望在分页加载的情况下,输入收入和支出,能够自动计算出余额:
设计步骤:
1、删除原来的余额列,然后在项目事件Initialize中设置代码:
DataTables("例子一").DataCols.Add("余额",Gettype(Double)) '添加余额列,用于动态显示余额
技巧:
A、由于余额是每次加载数据后动态计算得出的,所以这里应该采用表达式列,而不是数据列;这样不仅可以节省存储空间,更重要的是,不会因为重算余额导致系统误认为此行被修改了,需要保存。
B、当然这个表达式列和我们平时的表达式列不同,因为没有给它设置表达式;没有设置表达式的表达式列,可以用代码向该列赋值,希望大家能记住这一点。
C、也许有人会问,为什么要在Initialize事件,而不是AfterOpenProject事件添加列?这是因为在Initialize事件添加的列,可以保存列宽和列位置。
2、将DataColChanged事件设为:
Select
Case e.DataCol.Name
Case
"收入","支出"
Dim dr
As DataRow
Dim drs
As List(of
DataRow)
dr = e.DataTable.Find("[_SortKey]
< " & e.DataRow("_SortKey"),
"[_SortKey] Desc")
'找出上一行
If dr
Is Nothing
Then
'如果没有找到上一行,说明本行就是第一行
'计算之前的结存
Dim jk
As Double
Dim ck
As Double
jk =
DataTables("例子一").SQLCompute("Sum(收入)","[_SortKey]
< " & e.DataRow("_SortKey"))
ck =
DataTables("例子一").SQLCompute("Sum(支出)","[_SortKey]
< " & e.DataRow("_SortKey"))
e.DataRow("余额")
= e.DataRow("收入")
- e.DataRow("支出")
+ jk -ck
dr =
e.DataRow
End If
drs =
e.DataTable.Select("[_SortKey]
>= " & dr("_SortKey"),
"[_SortKey]")
For i
As Integer =
1 To
drs.Count -
1
'重算余下行的余额
drs(i)("余额")
= drs(i-1)("余额")
+ drs(i)("收入")
- drs(i)("支出")
Next
End
Select
上述代码的原理:
A、如果修改的是第一行,那么就先用SQLCompute方法,从后台计算出此行之前的结存,第一行的收入减去支出,再加上之前的结存,就是第一行的余额。
B、如果不是第一行数据,那么该行的收入减去支出,再加上上一行的结存,就是此行的余额。
可以看出,由于有了SQLCompute等后台数据处理行数,设计这么一个分页加载的流水账,变得轻松起来了。
3、表事件AfterMoveRow的代码保持不变:
Dim
Key As Decimal4、表事件DataRowDeleting的代码同样保持不变:
e.DataRow("收入")
= 0
e.DataRow("支出")
= 0
5、设置表事件AfterLoad的代码为:
If
e.DataTable.DataRows.count
> 0
Then
Dim
dr As
DataRow = DataTables("例子一").Find("","[_SortKey]")
e.DataTable.DataCols("支出").RaiseDataColChanged(dr)
End
If
这段代码在每次加载数据后执行,模拟拟第一行的支出发生变化, 刷新已加载行的余额,这是非常关键的一步。
6、最后AfterOpenproject事件代码设置为:
With
DataTables("例子一")
.LoadOver = "_SortKey"
'设置分页加载依据列
.LoadFilter =
"" '清除加载条件
End
With
Forms("窗口1").Open()
'打开设计好的分页加载窗口
提示: 由于我们的流水账是根据_SortKey的顺序计算的,所以分页加载的语句列必须设置为_SortKey列。
示例二
对于下面这中区分产品的分页加载流水账(参考示例文件中的例子二):
只需将DataColChanged事件代码改为:
Select
Case e.DataCol.Name
Case "产品","入库","出库"
Dim dr
As DataRow
Dim mr
As DataRow =
e.DataRow
Dim drs
As List(of
DataRow)
Dim
jk As
Integer
Dim ck
As Integer
dr = e.DataTable.Find("[_SortKey]
< " & mr("_SortKey") &
" And [产品]
= '"
& mr("产品")
& "'",
"[_SortKey] Desc")
If dr
Is Nothing
Then
'如果是第一行,计算结存
jk =
DataTables("例子二").SQLCompute("Sum(入库)","[_SortKey]
< " & mr("_SortKey") &
" And
产品
= '"
& mr("产品")
& "'")
ck =
DataTables("例子二").SQLCompute("Sum(出库)","[_SortKey]
< " & mr("_SortKey") &
" And
产品
= '"
& mr("产品")
& "'")
mr("库存")
= e.DataRow("入库")
- e.DataRow("出库")
+ jk - ck
dr =
mr
End If
drs =
e.DataTable.Select("[_SortKey]
>= " & dr("_SortKey")
&
" And [产品]
= '"
& dr("产品")
& "'",
"[_SortKey]")
For i
As Integer =
1 To
drs.Count -
1
drs(i)("库存")
= drs(i-1)("库存")
+ drs(i)("入库")
- drs(i)("出库")
Next
If e.DataCol.Name
= "产品"
AndAlso e.OldValue
IsNot Nothing
AndAlso e.OldValue
<> e.NewValue
Then
'如果修改的是产品列
dr = e.DataTable.Find("[_SortKey]
< " & mr("_SortKey") &
" And [产品]
= '"
& e.OldValue
& "'",
"[_SortKey] Desc")
'找出旧产品的上一行
If dr
Is Nothing
Then
'如果不存在上一行,那么本行就是旧产品的第一行,由于已经被修改为新产品,需要找出旧产品现在的第一行
dr =
e.DataTable.Find("[产品]
= '"
& e.OldValue
& "'",
"[_SortKey]")
'找出旧产品现在的第一行
If dr
IsNot Nothing
Then
'如果找到,计算库存
jk =
DataTables("例子二").SQLCompute("Sum(入库)","[_SortKey]
< " & mr("_SortKey") &
" And
产品
= '"
& e.OldValue
& "'")
ck =
DataTables("例子二").SQLCompute("Sum(出库)","[_SortKey]
< " & mr("_SortKey") &
" And
产品
= '"
& e.OldValue
& "'")
dr("库存")
= dr("入库")
- dr("出库")
+ jk - ck
End
If
End
If
If
dr IsNot Nothing
Then
drs =
e.DataTable.Select("[_SortKey]
>= " & dr("_SortKey")
&
" And [产品]
= '"
& dr("产品")
& "'",
"[_SortKey]")
For i
As Integer =
1 To
drs.Count -
1
drs(i)("库存")
= drs(i-1)("库存")
+ drs(i)("入库")
- drs(i)("出库")
Next
End
If
End
If
End
Select
表事件AfterMoveRow的代码保持不变:
Dim
Key As
Decimal
Dim
Index As
Integer
Dim
Filter As
String
Dim
r As
Row
Index =
Math.Min(e.OldIndex,
e.NewIndex)
Key =
e.Table.Rows(Index)("_SortKey")
r =
e.Table.Rows(e.NewIndex)
Filter =
"[_SortKey] >= " &
Key &
" And [产品] = '" &
r("产品")
&
"'"
e.Table.DataTable.DataCols("入库").RaiseDataColChanged(Filter)
表事件DataRowDeleting的代码保持不变:
e.DataRow("入库")
= 0
e.DataRow("出库")
= 0
表事件AfterLoad的代码有变化:
Dim
drs As
New List(of
DataRow)
For
Each nm
As String
In e.DataTable.GetValues("产品")
'找出每个产品的第一行数据,
添加到集合drs中
drs.Add(e.DataTable.Find("产品
= '"
& nm
& "'",
"[_SortKey]"))
Next
For
Each r
As DataRow
In drs
e.DataTable.DataCols("入库").RaiseDataColChanged(r)
Next
这个代码在数据加载后,重置每个产品第一行的入库列,刷新每个产品的库存,重置某产品的第一行,等于该产品所有行的库存都会刷新,所以没有必要重置所有行的。