跨表引用

假定产品表和订单表已经通过品名建立了关联,关联的名称为“产品_订单”。
产品表和订单表都有单价列,其中订单表中的单价数据来自于产品表。
通常我们会在订单表新建一个名为“单价”的表达式列,将其表达式设为:

Parent(产品_订单).单价。

这样在订单表输入品名后,单价列就会自动引用产品表中对应产品的单价。

但是采用表达式列会有以下棘手的问题:

1、如果产品表没有某产品的资料,需要在订单表中手工输入单价的时候,你会发现你是没有办法在订单表的单价列输入数据的。
2、如果这是一个特殊的订单,需要一个例外的价格,同样你会发现,订单表的单价数据是无法修改的。
3、如果从某一天开始,需要按照新的价格销售产品,于是你修改产品表的单价数据,但是修改后你会发现订单表原有订单的单价数据会全部自动更新为新的单价,显然这不是我们所希望的。

上述问题不是表达式列所能解决的,更甚的是,我们可能因为某些特殊原因,并没有在产品表和订单表之间建立关联,此时采用表达式列来引用产品单价根本就无从谈起。
所以我们有必要介绍另一种跨表引用数据的方法,这个方法不使用表达式,而且不管这两个表之间是否建立了关联,此方法应该同样有效。

以上面介绍的订单表引用产品表的单价为例,首先要确保订单表中的单价列不是表达式列,而是标准的数据列,然后在订单表的DataColChanged事件中设置如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
If e.DataCol.Name = "品名" Then '如果内容发生变动的是品名列
    If e.NewValue Is Nothing Then '如果新值是空白,也就是品名列的内容为空
        e.
DataRow("单价") = Nothing '那么清空此行单价列的内容
   
Else
        Dim
dr As DataRow
       
'否则在产品表查找同名的产品行,将找到的行赋值给变量dr
        dr =
DataTables("产品").Find("[品名] = '" & e.NewValue & "'")
        If
dr IsNot Nothing Then '如果找到了同名的产品行,也就是dr不是Nothing
            e.
DataRow("单价") = dr("单价")
        End
If
    End
If
End
If

为便于讲述,我给上面的代码加上了行号,代码的原理为:

第1行代码判断发生变动的是否是品名,如果是品名,第2行代码判断品名是否为空,如果品名为空,第3行代码清除此订单的单价,如果品名不为空,则执行第7行代码从产品表找出此产品所在的行, 并赋值给变量dr,如果在产品表找到此产品(也就是变量dr不为Nothing),就执行第9行代码,将订单的单价设置为此产品的单价。

注意第7行代码,其Find方法的条件表达式是动态合成的:

dr = DataTables("产品").Find("[品名] = '" & e.NewValue & "'")

条件表达式分为三部分,用运算符&连接起来;如果输入的品名为"PD01",那么三部分组合起来的结果是:

[品名]  = 'PD01'

也就说第7行代码等效于:

dr = DataTables("产品").Find("[品名]  = 'PD01'")

这是我们第二次介绍动态合成表达式,这是基本功,后面还会有专门的一节进行介绍,不掌握是不行的。

上述代码实现了:

1、在订单表中的某行,首次输入品名,或者输入一个不同的品名,那么会自动从产品表找出对应产品的单价,填入单价列中;
2、如果删除订单表某行品名列的内容,单价列的数据会自动清空;
3、订单表的单价列数据可以手工修改;
4、如果修改产品表的单价数据,并不会
影响订单表的现有数据,但是新增行会自动获得新的单价;
5、如果某一订单要重新获得新的单价,在品名单元格执行一次剪切粘贴操作即可,原因是什么,请您自己思考;
6、不管是否在产品表和订单表之间建立了关联,都能正常计算,这样可以避免因为关联太多而影响系统速度。

如果产品表和订单表是通过品名、型号、规格这三列联系起来的(不管是否建立了关联),在订单表输入品名、型号、规格后,再从产品表找出对应产品的单价,填入单价列中,那么订单表的DataColChanged事件可如下设置代码:

Select Case e.DataCol.Name
    Case
"品名","型号",
"规格"
       
Dim dr As DataRow = e.DataRow
       
Dim pr As DataRow
       
If dr.IsNull("品名") OrElse dr.IsNull("型号") OrElse dr.IsNull("规格") Then
            dr(
"单价") = Nothing
       
Else
           
Dim filter As String
            filter =
"品名 = '" & dr("品名") & "' And 型号 = '" & dr("型号") & "' And 规格 = '" & dr("规格") & "'"
            pr =
DataTables("产品").Find(filter)
            If
pr IsNot Nothing
Then
                dr(
"单价") = pr("单价")
            End
If
       
End If
End
Select

如果订单表有多列数据来自于产品表,一样可以采用类似的设计。
例如,假定产品表和订单表通过产品编号联系起来(不管是否建立了关联),订单表输入产品编号后,品名、型号、规格、单价四列内容从产品表自动继承输入。
为实现此目的,订单表
的DataColChanged事件代码应设置为:

If e.DataCol.Name = "产品编号" Then
    If e.NewValue Is Nothing Then
        e.
DataRow("品名") = Nothing
        e.
DataRow("型号") = Nothing
        e.
DataRow("规格") = Nothing
        e.
DataRow("单价") = Nothing
   
Else
       
Dim dr As DataRow
        dr =
DataTables("产品").Find("[产品编号] = '" & e.NewValue & "'")
        If
dr IsNot Nothing

            e.
DataRow("品名") = dr("品名")
            e.
DataRow("型号") = dr("型号")
            e.
DataRow("规格") = dr("规格")
            e.
DataRow("单价") = dr("单价")
        End
If
   
End If
End
If

如果有十几列甚至更多列的数据需要继承,上面代码可能会显得过于繁琐,为此我们可以考虑将代码改写为:

If e.DataCol.Name = "产品编号" Then
    Dim
nms() As String = {"品名","型号","规格","单价"}
    If
e.NewValue Is Nothing Then

        For
Each nm As String In nms

            e
.DataRow(nm) = Nothing

        Next

    Else

        Dim
dr As DataRow

        dr
= DataTables("产品").Find("[产品编号] = '" & e.NewValue &
"'")
        If
dr IsNot Nothing
            For Each
nm As String In nms
               
e.DataRow(nm) = dr(nm)
            Next
        End If
    End If
End If

这样不管有多少列,我们都只需修改下面这样代码,增加和删除列名即可:

Dim nms() As String = {"品名","型号","规格","单价"}

这种编码方式在处理大量重复任务的时候很实用,应该仔细体会掌握。


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