流水账的设计

本示例可以参考CaseStudy目录下的文件:流水账.Table

流水账对于行的顺序有严格的要求,而且有时免不了要插入行或者调整行位置。
所以对于要设计流水账的表,应该启用插入行功能。
提示:新建的内部表默认已经有插入行的功能
启用插入行功能后,表会自动增加一名为“_SortKey”的高精度小数列,该列的值代表着行在表中的顺序,这是设计流水账的基础。

假定有个简单的流水帐式的表格,输入收入和支出,能够自动计算出余额:

实现的代码不复杂,只需将DataColChanged事件设为:

Select Case e.DataCol.Name
    Case
"收入","支出"
        For Each dr As DataRow In e.DataTable.Select("[_SortKey] >= " & e.DataRow("_SortKey"))
            Dim
Val1 As Double = e.DataTable.Compute("Sum(收入)","[_SortKey] <= " & dr("_SortKey"))
            Dim
Val2 As Double = e.DataTable.Compute("Sum(支出)","[_SortKey] <= " & dr("_SortKey"))
            dr("余额") = Val1 - Val2

        Next
End
Select

该段代码的原理很简单:如果修改了某行的收入或支出,那么针对顺序在该行之后的每一行,计算出截止到此行的收入和支出,二者相减即可得到此行的余额。

现在的问题是,如果上移行或下移行,余额值肯定要自动更新,表事件AfterMoveRow在移动行后执行,为此我们可以在该事件中加入代码:

Dim Key As Decimal
Dim
Index As Integer
Dim
dc As DataCol
Index = Math.Min(e.OldIndex, e.NewIndex)
Key = e.
Table.Rows(Index)("_SortKey")
dc = e.
Table.DataTable.DataCols("收入")
dc.RaiseDataColChanged(
"[_SortKey] >= "
& Key)

上述代码的原理是,取得行原位置和新位置的最小值,然后从此最小位置开始重算余额,例如将8行移到第2行,则从第2行开始重算,将第3行移到第9行,则从第3行开始重算。

此外,如果删除某行,此行之后的所有余额也应该更新,所以还得将DataRowDeleting事件代码设置为:

e.DataRow("收入") = 0
e.
DataRow("支出") = 0

上面的代码在行即将被删除时,将该行的收入和支出设置为0,从而触发DataColChanged事件,重算此行之后的所有其他行的余额 ,由于重算时,此行的收入和支出已经被设置为0,等于其它行在计算余额的时候,不再考虑此行了。

更多的时候,流水账是区分产品的,例如下图的库存表:

此类流水账的设计方法要复杂一些,DataColChanged事件代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Select Case e.DataCol.Name
    Case
"产品","入库",
"出库"
       
Dim drs As List(of DataRow)
        Dim Filter As String
        Filter = "[_SortKey] >= " & e.DataRow("_SortKey") & " And [产品] = '" & e.DataRow("产品") & "'"
        drs = e.DataTable.Select(Filter)
        For Each dr As DataRow In drs
            Filter = "[_SortKey] <= " & dr("_SortKey") & " And [产品] = '" & dr("产品") & "'"
            Dim Val1 As Double = e.DataTable.Compute("Sum(入库)",Filter)
            Dim Val2 As Double = e.DataTable.Compute("Sum(出库)",Filter)
            dr("库存") = Val1 - Val2
        Next
        If e.DataCol.Name = "产品" AndAlso e.OldValue IsNot Nothing AndAlso e.OldValue <> e.NewValue Then
            Filter = "[_SortKey] > " & e.DataRow("_SortKey") & " And [产品] = '" & e.OldValue & "'"
            drs = e.DataTable.Select(Filter)
            For Each dr As DataRow In drs
                Filter = "[_SortKey] <= " & dr("_SortKey") & " And [产品] = '" & dr("产品") & "'"
                Dim Val1 As Double = e.DataTable.Compute("Sum(入库)",Filter)
                Dim Val2 As Double = e.DataTable.Compute("Sum(出库)",Filter)
                dr("库存") = Val1 - Val2
            Next
        End If
End
Select

上述代码的3到12行很好理解,当我们修改产品、入库、出库三列内容后,即从此行开始重算同产品所有行的余额。
假定我们修改的是产品,例如将产品由PD01改为PD02,3到12行会从此行开始重算所有产品为PD02的行的余额;显然此行之后的产品为PD01的行的余额,也是应该重算的,于是就有了13到22行的代码,注意第14行在合成条件的时候,用e.OldValue取得原产品名称。

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


本页地址:http://www.foxtable.com/webhelp/topics/1572.htm