验证与否?

时间:2012-04-30 10:17:43

标签: excel vba excel-vba

我正在接受用户的输入,我正在给他选择.. 例如,在“导体”栏中,给予他选择“Al或Cu”

同样,在“绝缘体”栏中,他可以选择“XLPE或PVC”

现在上面两个是我给用户的简单版本的列表,但是我的一些列表依赖于以前的输入,为此我使用了这样的数据验证:

=indirect($C5 & "_" & $D5) 

(例如:al_xlpe的命名范围)

假设列C和D引用某些输入(这将导致先前定义2个!命名范围)。

由于上述方法,我被迫使用了很多命名范围(我的一些验证列表选择取决于超过4个或更多输入,它们是这样的:

=indirect("col6" & "_" & col7 & "_" & col8 & "_" & col9) 

(例如:al_xlpe_duct_3的另一个命名范围;可以有4个!)

我面临几个问题:

  1. 由于数据库可以随时扩展,因为包含4个以上输入的验证列表需要4个!命名范围变更。
  2. 数据验证很容易丢失(一个主要问题)
  3. 我无法限制复制和粘贴,因为我的大多数用户都会粘贴其他工作表中的数据(由于列永远不会被修复,因此无法使用导入)
  4. 我无法使用列表框,因为可以输入任意数量的数据行,我需要在每行中选择
  5. 无法使用MS Access进行数据库管理,因为我的工具类型适用于输入数据,而且我的大多数用户都不熟悉访问权限(此外它不允许轻松复制粘贴数据)
  6. 有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

在这个答案中,我提供了一种技术,当我作为一名程序员开始工作45年时非常受欢迎,但是除了我自己多年以来,除了我之外,我还没有看到过常规应用。我们从编译器开发中借用了这种技术,但不太正式地使用它。

在完整的技术中,将有五个步骤:

  1. 设计一种以方便人类的方式编码规范的方法。
  2. 对规范进行编码。
  3. 设计一个或多个表格以便于快速处理方式保存规范。
  4. 设计并实施一个程序,将人格式转换为快速处理格式。
  5. 设计并实施一个程序来解释快速处理格式并执行所需的操作。
  6. 并非每个问题都需要所有五个步骤;有时人类和快速处理格式可能是相同的。这可能听起来很复杂,但它使我们能够轻松有效地解决许多复杂问题。

    在下面的工作表中,我编写了我对您需要的验证类型的理解。

      |         A          |         B          |         C          |
    --+--------------------+--------------------+--------------------+    
     1|Permitted           |Conditions -------------->               |
     2|C=V1|V2|V3|V4       |                    |                    |
     3|D=V5                |C=V1|V2             |                    |
     4|D=V6                |C=V3|V4             |                    |
     5|E=V7|V8             |D=V5                |C=V1                |
     6|E=V9|V10            |D=V5                |C=V2                |
     7|E=V11|V12           |D=V6                |C=V3                |
     8|E=V13|V14           |D=V6                |C=V4                |
    

    在第2行中,我声明C列中的单元格可能取值为V1或V2或V3或V4。

    在第3行中,我声明D列中的单元格可能取值为V5,但仅当同一行的C列值为V1或V2时才会生效。

    在第4行中,我使用自己的一组条件为D列中的单元格声明了一个替代值。

    在第5行中,我声明E列中的单元格可能取值V7或V8,但仅当同一行的D列值为V5且行的C列值为V1时。 / p>

    我对您的要求缺乏足够的了解,无法知道这是否是您的验证要求的最佳或完整表示。但是,如果你喜欢这种技术,我希望你能得到这个想法并且可以方便地表达你的要求。

    接下来我需要定义此规范的快速处理形式。我设计了四个表,并实现了下面的代码,将人工作表格式转换为快速处理格式,然后将这些表的内容输出到即时窗口,准备放在这个答案中:

    Rules per Column table 
    C RR RR                = Column   First rule   Last rule
    3  1  1
    4  2  3
    5  4  7
    

    工作表中有三列有验证规则,即第3(C),4(D)和5(E)列。上表告诉我们,对于第3栏(C),适用规则1至1,对于第5(E)栏,适用第4至7条。

    Rule table              
    I VV VV CC CC   = Index   First value   Last value   First condition   Last condition
    1  1  4  1  0 
    2  5  5  1  1 
    3  8  8  2  2 
    4 11 12  3  4 
    5 15 16  5  6 
    6 19 20  7  8 
    7 23 24  9 10 
    

    对于规则1,条件1到0适用,即没有条件。允许的值是Value表中的条目1到4(V1,V2,V3和V4)。这对应于工作表中的第2行。

    对于规则4,允许值是值表中的条目11和12(V7和V8),条件3到4适用。条件3是第4列(D)必须等于值表中的条目13(V5)。条件4是列3(C)必须等于值表中的条目14(V1)。这对应于工作表中的第5行。

    Condition table
     I C VV VV          = Index   Column   First value   Last value
     1 3  6  7
     2 3  9 10
     3 4 13 13
     4 3 14 14
     5 4 17 17
     6 3 18 18
     7 4 21 21
     8 3 22 22
     9 4 25 25
    10 3 26 26
    
    Value table                 Entries 1 to 26
    E 1=V1  E 2=V2  E 3=V3  E 4=V4  E 5=V5  E 6=V1  E 7=V2  E 8=V6  E 9=V3  E10=V4   
    E11=V7  E12=V8  E13=V5  E14=V1  E15=V9  E16=V10 E17=V5  E18=V2  E19=V11 E20=V12  
    E21=V6  E22=V3  E23=V13 E24=V14 E25=V6  E26=V4
    

    如果您不习惯通过链接表控制代码,可能需要一些时间才能完全理解。我已经按照几个规则的链接。再试几次,你就会明白这个想法。请注意工作表是如何设计为易于人类维护的,而这些表是为计算机快速执行而设计的。

    此编译过程可以在Worksheet Open例程中,也可以预先编译并将表存储在工作簿中。这些表已准备好通过工作表更改例程执行,或者它们可用于计算公式并将公式放置在适当的单元格中。

    我希望我已经足够清楚地解释了这个问题并决定这种技术是否适合您的问题。可以根据需要回答问题,我会扩展解释。

    以下代码将人格式转换为快速处理格式,然后将快速处理格式输出到即时窗口。

    Option Explicit
    
      Type typColRule      ' Definition of entry in Rules per Column table 
        InxRule1 As Long   ' Index of first rule for this column. ) InxRule1 > InxRuleL 
        InxRuleL As Long   ' Index of last rule for this column.  ) if no rules for column
      End Type
      Type typRule         ' Definition of Rule table 
        InxValue1 As Long  ' Index of first permitted value for this rule
        InxValueL As Long  ' Index of last permitted value for this rule
        InxCond1 As Long   ' Index of first condition for this column. ) InxCond1 > InxCondL 
        InxCondL As Long   ' Index of last rule for this column.       ) if no rules for column
      End Type
      Type typCond         ' Definition of Condition table
        Col As Long        ' Column to which this condition applies
        InxValue1 As Long  ' Index of first permitted value for this condition
        InxValueL As Long  ' Index of last permitted value for this condition
      End Type
    
      ' ColRule is sized to (Min to Max) where Min is the lowest column validated
      ' and Max is the highest column validated.  ColRule(N).InxRule1 identifies
      ' the first rule in Rule for column N.  ColRule(N).InsRuleL identifies the
      ' last rule in Rule for column N.
      Dim ColRule() As typColRule
    
      ' There is one entry in Rule per validation row in worksheet "Validate".
      Dim Rule() As typRule
    
      ' There is one entry in ValueCell per value referenced in a permitted or
      ' a condition.
      Dim ValueCell() As String
    
      ' There is one entry in Cond per condition in worksheet "Validate"
      Dim Cond() As typCond
    
    Sub CompileValidation()
    
      Dim ColCodeCrnt As String
      Dim ColNumCrnt As String
      Dim ColValCrnt As Long
      Dim ColValidateCrnt As Long
      Dim ColValMin As Long
      Dim ColValMax As Long
      Dim ConditionCrnt As String
      Dim InxCondCrnt As Long
      Dim InxRuleCrnt As Long
      Dim InxValueCellCrnt As Long
      Dim InxValueListCrnt As Long
      Dim NumCond As Long
      Dim NumValue As Long
      Dim PermittedCrnt As String
      Dim PosEqual As Long
      Dim RowValidateCrnt As Long
      Dim ValueList() As String
    
      With Worksheets("Validate")
    
        ' Determine the size of the arrays to which information will be
        ' compiled.  Find
        '   * The minimum and maximum columns subject to validated
        '   * Number of conditions
        '   * Number of values references
        ' This routine does not allow for blank rows or columns in the
        ' middle of worksheet "Validate".
        ColValMin = -1
        ColValMax = -1
        NumCond = 0
        NumValue = 0
        RowValidateCrnt = 2
        Do While True
          PermittedCrnt = .Cells(RowValidateCrnt, 1).Value
          If PermittedCrnt = "" Then
            Exit Do
          End If
          PosEqual = InStr(1, PermittedCrnt, "=")
          Debug.Assert PosEqual > 1
          ' Determine range of columns validated
          ColCodeCrnt = Mid(PermittedCrnt, 1, PosEqual - 1)
          ColNumCrnt = Range(ColCodeCrnt & "1").Column
          If ColValMin = -1 Then
            ColValMin = ColNumCrnt
          ElseIf ColValMin > ColNumCrnt Then
            ColValMin = ColNumCrnt
          End If
          If ColValMax = -1 Then
            ColValMax = ColNumCrnt
          ElseIf ColValMax < ColNumCrnt Then
            ColValMax = ColNumCrnt
          End If
          ' Determine number of conditions and number of values
          ValueList = Split(Mid(PermittedCrnt, PosEqual + 1), "|")
          NumValue = NumValue + UBound(ValueList) - LBound(ValueList) + 1
          ColValidateCrnt = 2
          Do While True
            ConditionCrnt = .Cells(RowValidateCrnt, ColValidateCrnt).Value
            If ConditionCrnt = "" Then
              Exit Do
            End If
            PosEqual = InStr(1, ConditionCrnt, "=")
            Debug.Assert PosEqual > 1
            ValueList = Split(Mid(ConditionCrnt, PosEqual + 1), "|")
            NumValue = NumValue + UBound(ValueList) - LBound(ValueList) + 1
            ColValidateCrnt = ColValidateCrnt + 1
          Loop
          NumCond = NumCond + ColValidateCrnt - 2
          RowValidateCrnt = RowValidateCrnt + 1
        Loop
    
        ' Size arrays
        ReDim ColRule(ColValMin To ColValMax)
        ReDim Rule(1 To RowValidateCrnt - 2)
        ReDim ValueCell(1 To NumValue)
        ReDim Cond(1 To NumCond)
    
        InxRuleCrnt = 0
        InxValueCellCrnt = 0
        InxCondCrnt = 0
    
        ' Extract rules in column number order
        For ColValCrnt = ColValMin To ColValMax
          ' The first rule for this column, if any, will be the
          ' next entry in the Rule table
          ColRule(ColValCrnt).InxRule1 = InxRuleCrnt + 1
          ' If there are no rules for this column, the last rule index
          ' will be less than the first rule undex
          ColRule(ColValCrnt).InxRuleL = InxRuleCrnt
          RowValidateCrnt = 2
          Do While True
            PermittedCrnt = .Cells(RowValidateCrnt, 1).Value
            If PermittedCrnt = "" Then
              Exit Do
            End If
            PosEqual = InStr(1, PermittedCrnt, "=")
            ColCodeCrnt = Mid(PermittedCrnt, 1, PosEqual - 1)
            ColNumCrnt = Range(ColCodeCrnt & "1").Column
            If ColNumCrnt = ColValCrnt Then
              ' This rule is for the current column
              InxRuleCrnt = InxRuleCrnt + 1
              ' This could be the last rule for this column so
              ' store its index against the column
              ColRule(ColValCrnt).InxRuleL = InxRuleCrnt
              ' The first value for this rule will be the next
              ' entry in the Value table
              Rule(InxRuleCrnt).InxValue1 = InxValueCellCrnt + 1
              ValueList = Split(Mid(PermittedCrnt, PosEqual + 1), "|")
              ' Save each permitted value in the Value table
              For InxValueListCrnt = LBound(ValueList) To UBound(ValueList)
                InxValueCellCrnt = InxValueCellCrnt + 1
                ValueCell(InxValueCellCrnt) = ValueList(InxValueListCrnt)
              Next
              ' Record the index of the last permitted value for this rule
              Rule(InxRuleCrnt).InxValueL = InxValueCellCrnt
              ' The first condition for this rule, if any, will be the next
              ' entry in the Condition table
              Rule(InxRuleCrnt).InxCond1 = InxCondCrnt + 1
              ' If there are no conditions for this rule, the last condition
              ' index will be less than the first condition undex
              Rule(InxRuleCrnt).InxCondL = InxCondCrnt
              ColValidateCrnt = 2
              Do While True
                ConditionCrnt = .Cells(RowValidateCrnt, ColValidateCrnt).Value
                If ConditionCrnt = "" Then
                  Exit Do
                End If
                InxCondCrnt = InxCondCrnt + 1
                PosEqual = InStr(1, ConditionCrnt, "=")
                ColCodeCrnt = Mid(ConditionCrnt, 1, PosEqual - 1)
                ColNumCrnt = Range(ColCodeCrnt & "1").Column
                ' Store the column for this condition
                Cond(InxCondCrnt).Col = ColNumCrnt
                ' The first value for this condition will be the next
                ' entry in the Value table
                Cond(InxCondCrnt).InxValue1 = InxValueCellCrnt + 1
                ValueList = Split(Mid(ConditionCrnt, PosEqual + 1), "|")
                For InxValueListCrnt = LBound(ValueList) To UBound(ValueList)
                  InxValueCellCrnt = InxValueCellCrnt + 1
                  ValueCell(InxValueCellCrnt) = ValueList(InxValueListCrnt)
                Next
                ' Record last value for this condition
                Cond(InxCondCrnt).InxValueL = InxValueCellCrnt
                ColValidateCrnt = ColValidateCrnt + 1
              Loop
              ' Record last condition for this rule
              Rule(InxRuleCrnt).InxCondL = InxCondCrnt
            End If
            RowValidateCrnt = RowValidateCrnt + 1
          Loop
        Next
      End With
    
      Debug.Print "    Rules per Column table"
      Debug.Print "    C RR RR"
      For ColValCrnt = ColValMin To ColValMax
        Debug.Print "    " & ColValCrnt & " " & _
                    Right(" " & ColRule(ColValCrnt).InxRule1, 2) & " " & _
                    Right(" " & ColRule(ColValCrnt).InxRuleL, 2)
      Next
      Debug.Print
      Debug.Print "    Rule table"
      Debug.Print "    I VV VV CC CC"
      For InxRuleCrnt = 1 To UBound(Rule)
        Debug.Print "    " & InxRuleCrnt & " " & _
                             Right(" " & Rule(InxRuleCrnt).InxValue1, 2) & " " & _
                             Right(" " & Rule(InxRuleCrnt).InxValueL, 2) & " " & _
                             Right(" " & Rule(InxRuleCrnt).InxCond1, 2) & " " & _
                             Right(" " & Rule(InxRuleCrnt).InxCondL, 2) & " "
      Next
      Debug.Print
      Debug.Print "    Condition table"
      Debug.Print "     I C VV VV"
      For InxCondCrnt = 1 To UBound(Cond)
        Debug.Print "    " & Right(" " & InxCondCrnt, 2) & " " & _
                             Cond(InxCondCrnt).Col & " " & _
                             Right(" " & Cond(InxCondCrnt).InxValue1, 2) & " " & _
                             Right(" " & Cond(InxCondCrnt).InxValueL, 2)
      Next
      Debug.Print
      Debug.Print "    Value table"
      Debug.Print "    ";
      For InxValueCellCrnt = 1 To UBound(ValueCell)
        Debug.Print "E" & Right(" " & InxValueCellCrnt, 2) & "=" & _
                    Left(ValueCell(InxValueCellCrnt) & "    ", 5);
        If (InxValueCellCrnt Mod 10) = 0 Then
          Debug.Print
          Debug.Print "    ";
        End If
      Next
    

    End Sub

答案 1 :(得分:0)

如果您要坚持命名范围,则应使用INDEX和COUNTA公式使命名范围动态化。这样您就可以将记录添加到列表中,并且命名范围将自动调整。但接下来我会告诉你不要使用命名范围。

这种链接数据验证对于简单的链表很有用。但是你的情况已经非常简单了,我认为你需要远离DV和ActiveX控件,可能是用户形式。

您可以达到一定程度的复杂性。一方面是你现在拥有的。另一方面,所有内容都存在于数据库中,Excel是适当关系数据库的计算引擎/前端。你可能会在这两者的中间结束。

我没有足够的信息给你一个真正相关的,详细的答案,所以我会做出一堆假设,你必须认识到它何时不适合你的情况。我认为您需要创建一个userform来处理数据输入。 userform上的列表框/组合框将通过代码动态更新。将通过功能区,右键单击菜单或“编辑”超链接调用用户窗体。 userform将填充当前选定的行。

然后你将有一个复制和粘贴选项。用户可以将单个项目复制并粘贴到用户表单中,您的代码将验证它们。或者他们可以复制代码将验证的整个信息记录。

不要回避Access作为后端。我的大多数项目都是由Excel控制的Jet数据库。 Excel是计算引擎,输入机制和报告机制。