高效率的流水账设计

本示例可以参考CaseStudy目录下的文件:高效率流水账.Table
上一节介绍的流水账设计方法是比较低效的,下面介绍一种更为高效的设计方法,希望大家仔细体会。

示例一

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

我们采用和上一节完全不同的方法,不再通过累计出之前的收入和支出,利用二者相减得出余额,而是通过上一行的余额和本行收入和支出,计算出本行的余额,显然这样的计算方式要高效很多。

为此将DataColChanged事件设为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 '如果没有找到上一行,说明本行就是第一行
            e.
DataRow("余额") = e.DataRow("收入") - e.DataRow("支出")
            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

上述代码的原理是:

表事件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)

表事件DataRowDeleting的代码同样保持不变:

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

如果是多人同时编辑数据,还需要在项目事件AfterOpenproject中加入代码:

Dim dr As DataRow
dr
= DataTables("例子一").Find("","[_SortKey]")
If
dr IsNot Nothing Then
    '
模拟第一行的支出发生变化, 刷新已加载行的余额.
    DataTables("
例子一").DataCols("支出").RaiseDataColChanged(dr)
End
If

这样每次打开项目,都会自动重算余额列,确保数据准确,注意我们没有必要重置所有行,只需重置第一行即可

示例二

对于下图所示这种区分产品的流水账:

需要将DataColChanged事件代码改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Select Case e.DataCol.Name
    Case
"产品","入库","出库"
        Dim
dr As DataRow
        Dim
mr As DataRow = e.DataRow
        Dim
drs As List(of DataRow)
        dr
= e.DataTable.Find("[_SortKey] < " & mr("_SortKey") & " And [产品] = '" & mr("产品") & "'", "[_SortKey] Desc"
)
        If
dr Is Nothing Then
            mr
("库存") = mr("入库") - mr("出库"
)
            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
                    dr
("库存") = dr("入库") - dr("出库"
)
                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

上述代码的第一部分(3到14行),用于在修改某行的产品、入库、出库后,重算从此行开始的所有同产品的行的余额,代码原理已经在上面不区分产品的流水账中介绍,不同的只是在条件表达式中加入了产品比较,请大家自己分析。

第二部分代码(15到29行)的原理:

表事件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

如果多用户同时录入数据,可以在项目事件AfterOpenproject中加入代码:

Dim drs As New List(of DataRow)
With
DataTables("例子二")
    For Each nm As String In .GetValues("
产品") '找出每个产品的第一行数据, 添加到集合drs
        drs.Add(.Find("
产品 = '" & nm & "'", "[_SortKey]"))
    Next
    For Each r As DataRow In drs
        .DataCols("
入库").RaiseDataColChanged(r) '重置每个产品的第一行
   
Next
End
With

这样打开项目之后,会自动重置每个产品的第一行,刷新每个产品的库存。

 


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