标签 VBA 下的文章

一、IP归属地查询的广泛应用需求

在现代信息化社会中,IP地址是每个在线设备与互联网连接的唯一标识。通过对IP地址的归属地进行查询,企业和机构可以获取关于用户的地理位置、运营商信息等,帮助他们做出更加精准的决策。无论是在网络安全、数据分析、市场营销,还是反欺诈等领域,IP归属地查询都有着广泛的应用场景。

手动查询IP的归属地往往耗时且易出错,尤其是当需要批量查询时,工作量将成倍增加。借助Excel宏结合本地离线数据库(如CSV文件、JSON文件或MySQL数据库),可以实现自动化的IP查询,显著提升效率和准确性。

二、为什么选择Excel宏进行自动化查询?

Excel是全球最广泛使用的数据处理工具之一。其强大的数据管理和分析功能,使得它不仅限于财务管理,还广泛应用于各行各业。在处理大量数据时,Excel宏(VBA)为用户提供了自动化操作的强大能力。

Excel宏可以与本地离线数据库(如CSV、JSON或MySQL)结合,通过简单的脚本,快速实现IP归属地查询,而无需每次手动查找。相较于在线查询,离线数据库具有快速响应、无需网络依赖等优点,尤其适合需要频繁查询、批量查询的场景。
如何利用Excel宏和离线数据库自动化IP归属地查询?

三、如何编写Excel宏,调用本地数据库进行IP归属地查询?

1. 准备本地数据库

首先,需要准备一个包含IP地址和归属地信息的本地数据库。这里我们使用CSV格式作为示例,数据包括IP地址、归属地和运营商等字段,在正式测试中,我们使用的是IP数据云的IP归属地库。以下是一个简单的CSV文件示例:

IP地址,归属地,运营商
192.168.1.1,北京市,中国联通
180.76.15.18,上海市,中国电信

这个CSV文件可以根据实际需要扩展更多的IP信息

2. 编写Excel宏代码:调用本地CSV文件进行查询

在Excel中,可以使用VBA编程来实现IP查询。以下是一个示例代码,通过VBA宏读取本地CSV文件,并根据输入的IP地址查询归属地信息:

Sub 查询IP归属地()
    Dim ipAddress As String
    Dim csvFilePath As String
    Dim fileContent As String
    Dim lines() As String
    Dim line As String
    Dim data() As String
    Dim found As Boolean
    Dim i As Integer
    
    ' 获取用户输入的IP地址
    ipAddress = InputBox("请输入IP地址:", "IP归属地查询")
    
    ' 设置CSV文件路径
    csvFilePath = "C:\path\to\your\ip_database.csv"
    
    ' 读取CSV文件内容
    Open csvFilePath For Input As #1
    fileContent = Input$(LOF(1), 1)
    Close #1
    
    ' 将CSV内容按行分割
    lines = Split(fileContent, vbCrLf)
    
    ' 查找IP地址对应的归属地
    found = False
    For i = 0 To UBound(lines)
        line = lines(i)
        data = Split(line, ",")
        
        ' 如果找到匹配的IP地址
        If data(0) = ipAddress Then
            MsgBox "IP地址 " & ipAddress & " 的归属地是:" & data(1) & ", 运营商:" & data(2)
            found = True
            Exit For
        End If
    Next i
    
    ' 如果未找到对应IP
    If Not found Then
        MsgBox "未找到IP地址 " & ipAddress & " 的相关信息。"
    End If
End Sub

3. 批量查询IP归属地

为了应对大量的IP地址查询,Excel宏还可以扩展为批量查询的功能。以下是一个批量查询IP归属地的VBA代码,假设IP地址列表存储在Excel工作表的第一列,查询结果将输出到第二列:

Sub 批量查询IP归属地()
    Dim ipAddress As String
    Dim csvFilePath As String
    Dim fileContent As String
    Dim lines() As String
    Dim line As String
    Dim data() As String
    Dim i As Integer
    Dim resultRow As Integer
    
    ' 设置CSV文件路径
    csvFilePath = "C:\path\to\your\ip_database.csv"
    
    ' 读取CSV文件内容
    Open csvFilePath For Input As #1
    fileContent = Input$(LOF(1), 1)
    Close #1
    
    ' 将CSV内容按行分割
    lines = Split(fileContent, vbCrLf)
    
    ' 开始处理IP地址列表
    resultRow = 1
    For i = 1 To ActiveSheet.UsedRange.Rows.Count
        ipAddress = Cells(i, 1).Value ' 假设IP地址在第一列
        
        ' 查找IP地址对应的归属地
        For Each line In lines
            data = Split(line, ",")
            If data(0) = ipAddress Then
                Cells(resultRow, 2).Value = data(1) ' 输出归属地
                Cells(resultRow, 3).Value = data(2) ' 输出运营商
                resultRow = resultRow + 1
                Exit For
            End If
        Next line
    Next i
End Sub

该宏将遍历工作表中的IP地址,查找对应的归属地信息并输出结果。

四、如何优化和扩展Excel宏?

Excel宏的强大之处在于其可扩展性。除了查询IP归属地,以下是一些优化和扩展的建议:

增加查询结果的格式化功能:

可以在查询结果中使用条件格式化,突出显示不同的运营商或归属地。例如,可以使用不同的颜色标记中国电信与中国联通的归属地。

支持更多数据库格式:

除了CSV文件外,宏也可以支持JSON或MySQL数据库。对于较大的数据集,使用MySQL数据库可以提高查询效率。

定时查询和自动报告:

结合Excel的定时功能,可以实现自动定期查询并生成报告。对于需要定期更新IP归属地的情况,宏的自动化功能尤为重要。

批量导入IP地址:

在实际应用中,IP地址可能来自多个渠道(如日志文件、数据库等)。Excel宏可以扩展为批量导入不同来源的数据,进一步提升查询效率。当我们在选择数据库时,最好是选择数据维度多且更新频率高的,像IP数据云就是一个不错的选择,结果返回20+维度字段更是支持每日更新。

五、结语

通过利用Excel宏和本地离线数据库,企业和个人可以轻松实现IP归属地查询的自动化,无论是在网络安全、数据分析、市场营销等多个行业中,都会大大提高工作效率,节省时间和精力。无论是单次查询还是批量查询,Excel宏都能提供便捷的解决方案,帮助用户更好地管理和分析IP数据。

Access 连接 SQL Server:直通查询 vs 链接表 vs ADO,如何选择?

摘要:当 Access 前端需要连接 SQL Server 后端时,开发者面临三种主流技术方案:链接表(Linked Tables)直通查询(Pass-Through Queries)ADO 编程。本文从底层原理、性能特征、适用场景三个维度进行深度对比,帮助开发者在实际项目中做出正确的技术选型。


一、技术背景

Access 作为前端开发工具连接 SQL Server 后端,是中小型企业信息化的经典架构。这种"胖客户端"模式相比纯 Web 方案,具有开发效率高、部署简单的优势。

但 Access 与 SQL Server 之间的数据交互存在多种实现路径,不同方案在 网络开销服务器负载代码复杂度 上差异显著。


二、三种方案的底层原理

1. 链接表(Linked Tables)

原理:通过 ODBC 驱动在 Access 中创建指向 SQL Server 表的"快捷方式"。Access 的 ACE/Jet 引擎会将用户操作(筛选、排序、更新)转换为 ODBC 调用。

┌──────────────┐      ODBC       ┌──────────────┐
│   Access     │  ←──────────→   │  SQL Server  │
│  (ACE引擎)   │   链接表驱动     │   (T-SQL)    │
└──────────────┘                 └──────────────┘

技术特点

  • 透明性:开发者可以像操作本地表一样使用 SELECT * FROM tblOrders
  • 引擎介入:ACE 引擎会"尝试"优化查询,但复杂查询可能被拆解为多次网络往返。
  • 事务支持:受限于 ODBC 驱动的事务隔离级别。

创建方式

' VBA 代码创建链接表
DoCmd.TransferDatabase acLink, "ODBC Database", _
    "ODBC;DRIVER={SQL Server};SERVER=192.168.1.100;DATABASE=SalesDB;Trusted_Connection=Yes", _
    acTable, "dbo.Orders", "lnkOrders"

2. 直通查询(Pass-Through Queries)

原理:绕过 ACE 引擎,将 原生 T-SQL 直接发送到 SQL Server 执行,结果集作为只读快照返回。

┌──────────────┐    原生 T-SQL    ┌──────────────┐
│   Access     │  ──────────────→ │  SQL Server  │
│  (仅传递)     │   不经过 ACE     │   (直接执行)  │
└──────────────┘                 └──────────────┘

技术特点

  • 完全控制:可使用 SQL Server 特有语法(TOPWITH (NOLOCK)PIVOT 等)。
  • 只读限制:返回结果集默认不可编辑(除非配合链接表使用)。
  • 存储过程调用:最佳的存储过程执行方式。

创建方式

-- 在查询设计器中设置 "直通" 属性为 "是"
-- 或通过 VBA 创建
SELECT TOP 100 OrderID, CustomerName, OrderDate
FROM dbo.Orders WITH (NOLOCK)
WHERE OrderDate >= '2025-01-01'
ORDER BY OrderDate DESC

VBA 动态执行

Public Sub ExecutePassThrough(strSQL As String)
    Dim qdf As DAO.QueryDef
    
    On Error Resume Next
    CurrentDb.QueryDefs.Delete "qryTemp"
    On Error GoTo 0
    
    Set qdf = CurrentDb.CreateQueryDef("qryTemp")
    With qdf
        .Connect = "ODBC;DRIVER={SQL Server};SERVER=192.168.1.100;DATABASE=SalesDB;Trusted_Connection=Yes"
        .SQL = strSQL
        .ReturnsRecords = True  ' 如果是 INSERT/UPDATE/DELETE,设为 False
    End With
    
    ' 绑定到窗体或报表
    Me.RecordSource = "qryTemp"
End Sub

3. ADO 编程(ActiveX Data Objects)

原理:通过 ADO 对象模型(ADODB.ConnectionADODB.Recordset)直接操作 OLE DB 或 ODBC 数据源,完全脱离 Access 的 DAO/ACE 体系。

┌──────────────┐     OLE DB      ┌──────────────┐
│   Access     │  ←──────────→   │  SQL Server  │
│  (ADO对象)   │   直接连接       │   (T-SQL)    │
└──────────────┘                 └──────────────┘

技术特点

  • 最大灵活性:支持游标类型选择、批量更新、断开式记录集。
  • 可移植性:ADO 代码可直接迁移到 VB6、VBScript、Excel VBA。
  • 代码量大:需要手动管理连接生命周期和错误处理。

典型代码

Public Function GetOrders(strCustomerID As String) As ADODB.Recordset
    Dim conn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim strSQL As String
    
    ' 连接字符串
    conn.ConnectionString = "Provider=SQLOLEDB;Data Source=192.168.1.100;" & _
                            "Initial Catalog=SalesDB;Integrated Security=SSPI;"
    conn.Open
    
    ' 参数化查询防止 SQL 注入
    strSQL = "SELECT * FROM dbo.Orders WHERE CustomerID = ?"
    
    With rs
        .ActiveConnection = conn
        .Source = strSQL
        .CursorLocation = adUseClient  ' 客户端游标,支持断开连接
        .CursorType = adOpenStatic
        .LockType = adLockBatchOptimistic
        .Open , , , , adCmdText
    End With
    
    ' 断开连接,返回独立记录集
    Set rs.ActiveConnection = Nothing
    conn.Close
    
    Set GetOrders = rs
End Function

三、性能对比测试

以下是在 万级数据量 下的典型场景测试结果(仅供参考,实际因网络环境而异):

场景链接表直通查询ADO
SELECT 1000 条记录1.2s0.3s0.4s
复杂 JOIN(5表关联)8.5s0.8s0.9s
调用存储过程不支持0.2s0.2s
批量 INSERT 1000 条15s0.5s0.6s
单条记录更新0.1s0.1s0.1s

结论

  • 简单 CRUD:三者差异不大。
  • 复杂查询/批量操作:直通查询和 ADO 优势明显。
  • 链接表的性能陷阱:多表 JOIN 时,ACE 引擎可能先拉取全表数据到本地再做关联,造成巨大的网络开销。

四、适用场景决策树

                        ┌─────────────────────────┐
                        │  需要连接 SQL Server?   │
                        └───────────┬─────────────┘
                                    │
                    ┌───────────────┴───────────────┐
                    ▼                               ▼
            需要绑定窗体/报表?               仅需执行命令/获取数据?
                    │                               │
        ┌───────────┴───────────┐                   │
        ▼                       ▼                   ▼
   简单表结构              复杂查询/存储过程      ───→  ADO
   单表或简单JOIN                │                    (最大灵活性)
        │                       │
        ▼                       ▼
    【链接表】              【直通查询】
   (最简单)              (高性能)

场景建议

场景推荐方案理由
数据维护窗体(增删改查)链接表可直接绑定,无需额外代码
报表数据源直通查询只读即可,性能最优
调用存储过程直通查询 / ADO链接表不支持存储过程
复杂多表统计直通查询避免 ACE 拆解查询
需要事务控制ADO可精确控制 BeginTrans/CommitTrans
断开式数据处理ADO支持客户端游标和批量更新
跨数据库查询ADO可同时连接多个数据源

五、混合架构最佳实践

在实际项目中,三种方案往往需要混合使用

┌─────────────────────────────────────────────────────────┐
│                    Access 前端                          │
├─────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │   链接表      │  │   直通查询   │  │    ADO      │  │
│  │ (数据维护)   │  │  (报表/统计) │  │  (存储过程)  │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
              ┌─────────────────────────┐
              │      SQL Server         │
              │   (存储过程/视图/表)     │
              └─────────────────────────┘

架构建议

  1. 基础表:使用链接表,方便窗体绑定。
  2. 复杂视图:在 SQL Server 端创建视图,Access 链接该视图。
  3. 统计报表:使用直通查询,发挥 SQL Server 的聚合能力。
  4. 业务逻辑:封装为存储过程,通过直通查询或 ADO 调用。

六、总结

维度链接表直通查询ADO
学习成本★☆☆★★☆★★★
开发效率★★★★★☆★☆☆
运行性能★☆☆★★★★★★
灵活性★☆☆★★☆★★★
可维护性★★★★★☆★★☆

核心原则

  • 能用链接表解决的,不要过度设计。
  • 性能敏感的场景,优先考虑直通查询。
  • 需要精细控制(事务、游标、多数据源)时,使用 ADO。

「Access开发」 专注于 Microsoft Access 开发与企业级应用,提供以下服务:

📚 技术培训

  • Access VBA 从入门到精通(线上/线下)
  • Access + SQL Server 企业级开发实战
  • Access 系统性能优化与架构设计

💼 定制开发

  • 企业 ERP/CRM/进销存系统开发
  • 旧系统升级与性能优化
  • Access 迁移至 Web/Power Platform 咨询

🔧 技术支持

  • 代码审查与重构建议
  • 疑难问题远程诊断
  • 一对一技术辅导

技术改变业务,专注创造价值。

hi,大家好!

为什么还不春节,最近又不知道在忙些啥,又半个月过去了,答应大家的框架,又又又跳票了!既然这样的话,今天那就再给大家分享点干货!今天的代码量比较大,大家给个一键三连吧,谢谢大家啦啦啦!
平时,我们在开发的过程中,遇到需要验证的文本框,是不是还在用IF ……Then MsgBox…… 这样的方式输出?那也太Low了,那今天就给大家分享一个完整的验证方案!来吧,让我们Hi起来!

现代 Web 开发(如 Bootstrap、Vue 等框架)通过 DOM 操作实现了“所见即所得”的验证反馈(红框、图标、气泡提示)。本文旨在通过 VBA 模拟这一机制。

1。创建类模块

首先,我们先创建一个类模块:ClsFieldValidator,你没有看错,我们上来就要创建一个类模块,这里写了几个常用的验证,必填、邮箱、手机号、身份证号、纯数字、长度限制、数值范围、自定义正则、日期格式。

' 类模块: ClsFieldValidator
Option Compare Database
Option Explicit

' 验证结果枚举
Public Enum ValidationResult
    vrValid = 0
    vrInvalid = 1
    vrEmpty = 2
End Enum

' 验证类型枚举
Public Enum validationType
    vtRequired = 1          ' 必填
    vtEmail = 2             ' 邮箱
    vtMobile = 3            ' 手机号
    vtIDCard = 4            ' 身份证号
    vtNumeric = 5           ' 纯数字
    vtLength = 6            ' 长度限制
    vtRange = 7             ' 数值范围
    vtCustomRegex = 8       ' 自定义正则
    vtDate = 9              ' 日期格式
End Enum

Private m_MinLength As Long
Private m_MaxLength As Long
Private m_MinValue As Double
Private m_MaxValue As Double
Private m_CustomPattern As String
Private m_ErrorMessage As String

' ========== 属性 ==========
Public Property Get errorMessage() As String
    errorMessage = m_ErrorMessage
End Property

Public Property Let MinLength(value As Long)
    m_MinLength = value
End Property

Public Property Let MaxLength(value As Long)
    m_MaxLength = value
End Property

Public Property Let MinValue(value As Double)
    m_MinValue = value
End Property

Public Property Let MaxValue(value As Double)
    m_MaxValue = value
End Property

Public Property Let CustomPattern(value As String)
    m_CustomPattern = value
End Property

' ========== 核心验证方法 ==========
Public Function Validate(ByVal inputValue As Variant, ByVal validationType As validationType) As ValidationResult
    Dim strValue As String
    strValue = Nz(inputValue, "")
    
    ' 清空上次错误信息
    m_ErrorMessage = ""
    
    Select Case validationType
        Case vtRequired
            Validate = ValidateRequired(strValue)
            
        Case vtEmail
            Validate = ValidateEmail(strValue)
            
        Case vtMobile
            Validate = ValidateMobile(strValue)
            
        Case vtIDCard
            Validate = ValidateIDCard(strValue)
            
        Case vtNumeric
            Validate = ValidateNumeric(strValue)
            
        Case vtLength
            Validate = ValidateLength(strValue)
            
        Case vtRange
            Validate = ValidateRange(strValue)
            
        Case vtCustomRegex
            Validate = ValidateRegex(strValue)
            
        Case vtDate
            Validate = ValidateDate(strValue)
            
        Case Else
            Validate = vrValid
    End Select
End Function

' ========== 具体验证规则 ==========

' 必填验证
Private Function ValidateRequired(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        m_ErrorMessage = "此字段为必填项"
        ValidateRequired = vrEmpty
    Else
        ValidateRequired = vrValid
    End If
End Function

' 邮箱验证
Private Function ValidateEmail(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        ValidateEmail = vrEmpty
        Exit Function
    End If
    
    ' 使用 VBScript.RegExp 进行正则验证
    Dim regex As Object
    Set regex = CreateObject("VBScript.RegExp")
    
    With regex
        .Global = True
        .IgnoreCase = True
        .Pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    End With
    
    If regex.test(strValue) Then
        ValidateEmail = vrValid
    Else
        m_ErrorMessage = "请输入有效的邮箱地址"
        ValidateEmail = vrInvalid
    End If
    
    Set regex = Nothing
End Function

' 手机号验证 (中国大陆11位手机号)
Private Function ValidateMobile(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        ValidateMobile = vrEmpty
        Exit Function
    End If
    
    Dim regex As Object
    Set regex = CreateObject("VBScript.RegExp")
    
    With regex
        .Global = True
        .Pattern = "^1[3-9]\d{9}$"
    End With
    
    If regex.test(strValue) Then
        ValidateMobile = vrValid
    Else
        m_ErrorMessage = "请输入有效的11位手机号"
        ValidateMobile = vrInvalid
    End If
    
    Set regex = Nothing
End Function

' 身份证号验证 (18位)
Private Function ValidateIDCard(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        ValidateIDCard = vrEmpty
        Exit Function
    End If
    
    Dim regex As Object
    Set regex = CreateObject("VBScript.RegExp")
    
    With regex
        .Global = True
        .IgnoreCase = True
        ' 18位身份证:6位地区码 + 8位生日 + 3位顺序码 + 1位校验码
        .Pattern = "^\d{6}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$"
    End With
    
    If regex.test(strValue) Then
        ' 进一步验证校验码
        If ValidateIDCardChecksum(strValue) Then
            ValidateIDCard = vrValid
        Else
            m_ErrorMessage = "身份证号校验码错误"
            ValidateIDCard = vrInvalid
        End If
    Else
        m_ErrorMessage = "请输入有效的18位身份证号"
        ValidateIDCard = vrInvalid
    End If
    
    Set regex = Nothing
End Function

' 身份证校验码算法
Private Function ValidateIDCardChecksum(strValue As String) As Boolean
    Dim weights As Variant
    Dim checkCodes As String
    Dim total As Long
    Dim i As Long
    
    weights = Array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
    checkCodes = "10X98765432"
    
    total = 0
    For i = 1 To 17
        total = total + CInt(Mid(strValue, i, 1)) * weights(i - 1)
    Next i
    
    Dim checkChar As String
    checkChar = Mid(checkCodes, (total Mod 11) + 1, 1)
    
    ValidateIDCardChecksum = (UCase(Mid(strValue, 18, 1)) = checkChar)
End Function

' 纯数字验证
Private Function ValidateNumeric(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        ValidateNumeric = vrEmpty
        Exit Function
    End If
    
    If IsNumeric(strValue) Then
        ValidateNumeric = vrValid
    Else
        m_ErrorMessage = "请输入有效的数字"
        ValidateNumeric = vrInvalid
    End If
End Function

' 长度验证
Private Function ValidateLength(strValue As String) As ValidationResult
    Dim strLen As Long
    strLen = Len(strValue)
    
    If strLen = 0 Then
        ValidateLength = vrEmpty
        Exit Function
    End If
    
    If m_MinLength > 0 And strLen < m_MinLength Then
        m_ErrorMessage = "长度不能少于 " & m_MinLength & " 个字符"
        ValidateLength = vrInvalid
    ElseIf m_MaxLength > 0 And strLen > m_MaxLength Then
        m_ErrorMessage = "长度不能超过 " & m_MaxLength & " 个字符"
        ValidateLength = vrInvalid
    Else
        ValidateLength = vrValid
    End If
End Function

' 数值范围验证
Private Function ValidateRange(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        ValidateRange = vrEmpty
        Exit Function
    End If
    
    If Not IsNumeric(strValue) Then
        m_ErrorMessage = "请输入有效的数字"
        ValidateRange = vrInvalid
        Exit Function
    End If
    
    Dim numValue As Double
    numValue = CDbl(strValue)
    
    If numValue < m_MinValue Then
        m_ErrorMessage = "数值不能小于 " & m_MinValue
        ValidateRange = vrInvalid
    ElseIf numValue > m_MaxValue Then
        m_ErrorMessage = "数值不能大于 " & m_MaxValue
        ValidateRange = vrInvalid
    Else
        ValidateRange = vrValid
    End If
End Function

' 自定义正则验证
Private Function ValidateRegex(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        ValidateRegex = vrEmpty
        Exit Function
    End If
    
    If Len(m_CustomPattern) = 0 Then
        ValidateRegex = vrValid
        Exit Function
    End If
    
    Dim regex As Object
    Set regex = CreateObject("VBScript.RegExp")
    
    With regex
        .Global = True
        .IgnoreCase = True
        .Pattern = m_CustomPattern
    End With
    
    If regex.test(strValue) Then
        ValidateRegex = vrValid
    Else
        m_ErrorMessage = "输入格式不正确"
        ValidateRegex = vrInvalid
    End If
    
    Set regex = Nothing
End Function

' 日期格式验证
Private Function ValidateDate(strValue As String) As ValidationResult
    If Len(Trim(strValue)) = 0 Then
        ValidateDate = vrEmpty
        Exit Function
    End If
    
    If IsDate(strValue) Then
        ValidateDate = vrValid
    Else
        m_ErrorMessage = "请输入有效的日期"
        ValidateDate = vrInvalid
    End If
End Function

2。添加一个通用模块

接着,我们要再创建一个通用模块。模块名:M_ValidationUI


' 标准模块: M_ValidationUI
Option Compare Database
Option Explicit

' 验证状态图标 (使用 Unicode 字符)
Public Const ICON_VALID As String = "验证正确"
Public Const ICON_INVALID As String = "验证错误"
Public Const ICON_EMPTY As String = ""

' 颜色常量
Public Const COLOR_VALID As Long = 32768       ' 绿色 RGB(0, 128, 0)
Public Const COLOR_INVALID As Long = 255       ' 红色 RGB(255, 0, 0)
Public Const COLOR_WARNING As Long = 33023     ' 橙色 RGB(255, 128, 0)
Public Const COLOR_DEFAULT As Long = 0         ' 黑色

' 更新验证状态显示
Public Sub UpdateValidationStatus( _
    ByVal lblStatus As Access.Label, _
    ByVal result As ValidationResult, _
    Optional ByVal errorMessage As String = "")
    
    Select Case result
        Case vrValid
            With lblStatus
                .Caption = ICON_VALID
                .ForeColor = COLOR_VALID
                .ControlTipText = "验证通过"
            End With
            
        Case vrInvalid
            With lblStatus
                .Caption = ICON_INVALID
                .ForeColor = COLOR_INVALID
                .ControlTipText = IIf(Len(errorMessage) > 0, errorMessage, "验证失败")
            End With
            
        Case vrEmpty
            With lblStatus
                .Caption = ICON_EMPTY
                .ForeColor = COLOR_DEFAULT
                .ControlTipText = ""
            End With
    End Select
End Sub

' 高亮文本框边框 (模拟 Web 效果)
Public Sub HighlightTextBox( _
    ByVal txtControl As Access.TextBox, _
    ByVal result As ValidationResult)
    
    Select Case result
        Case vrValid
            txtControl.BorderColor = COLOR_VALID
            
        Case vrInvalid
            txtControl.BorderColor = COLOR_INVALID
            
        Case vrEmpty
            txtControl.BorderColor = COLOR_DEFAULT
    End Select
End Sub

' 显示错误提示气泡 (使用标签模拟 Tooltip)
Public Sub ShowErrorTooltip( _
    ByVal lblTooltip As Access.Label, _
    ByVal message As String, _
    ByVal show As Boolean)
    
    If show And Len(message) > 0 Then
        With lblTooltip
            .Caption = message
            .Visible = True
            .BackColor = RGB(255, 240, 240)  ' 浅红色背景
            .ForeColor = COLOR_INVALID
            .BorderColor = COLOR_INVALID
            .BorderStyle = 1  ' 实线边框
        End With
    Else
        lblTooltip.Visible = False
    End If
End Sub

' 验证整个表单,返回是否全部通过
Public Function ValidateForm(frm As Access.Form, ParamArray validations() As Variant) As Boolean
    Dim i As Long
    Dim allValid As Boolean
    Dim result As ValidationResult
    Dim validator As ClsFieldValidator
    
    allValid = True
    Set validator = New ClsFieldValidator
    
    ' validations 参数格式: txtControl, lblStatus, ValidationType, [可选参数...]
    ' 示例调用: ValidateForm(Me, Me.txtEmail, Me.lblEmailStatus, vtEmail, ...)
    
    For i = LBound(validations) To UBound(validations) Step 3
        Dim txtCtrl As Access.TextBox
        Dim lblCtrl As Access.Label
        Dim vType As validationType
        
        Set txtCtrl = validations(i)
        Set lblCtrl = validations(i + 1)
        vType = validations(i + 2)
        
        result = validator.Validate(txtCtrl.value, vType)
        UpdateValidationStatus lblCtrl, result, validator.errorMessage
        HighlightTextBox txtCtrl, result
        
        If result = vrInvalid Then allValid = False
        ' 必填字段为空也算失败
        If vType = vtRequired And result = vrEmpty Then allValid = False
    Next i
    
    ValidateForm = allValid
    Set validator = Nothing
End Function

3。创建窗体

类与通用的模块都有了,接下来就教大家来调用了,创建一个窗体,具体的如下图,一个文本框(txtEmail),2个标签(lblEmailStatus,lblMobileError),一个按钮。
这里我们只用一个邮件验证来举例!

4。窗体代码

控件有了,就可以来添加相应的调用代码了,具体的代码里注释都添加好了,大家自己查看添加。

Option Compare Database

Private m_Validator As ClsFieldValidator
' ========== 提交按钮验证 ==========
Private Sub Command4_Click()
 Dim r3 As ValidationResult
    r3 = m_Validator.Validate(Me.txtEmail, vtEmail)
    UpdateValidationStatus Me.lblEmailStatus, r3, m_Validator.errorMessage
    HighlightTextBox Me.txtEmail, r3
    
        ' 判断是否全部通过
    allValid = (r3 = vrValid Or r3 = vrEmpty)
    If allValid Then
        MsgBox "验证通过,正在提交...", vbInformation, "成功"
    Else
        MsgBox "请检查输入内容,修正标红的字段。", vbExclamation, "验证失败"
    End If
End Sub

Private Sub Form_Load()
Set m_Validator = New ClsFieldValidator
InitStatusLabels
End Sub
' 初始化状态标签
Private Sub InitStatusLabels()
    Dim lbls As Variant
    Dim i As Long
    
    lbls = Array(Me.lblEmailStatus)
    
    For i = LBound(lbls) To UBound(lbls)
        With lbls(i)
            .Caption = ""
            .FontSize = 14
            .FontBold = True
            .TextAlign = 2  ' 居中
        End With
    Next i
    
    ' 隐藏错误提示标签
    Me.lblMobileError.Visible = False
End Sub

 
' 通用验证方法
Private Function ValidateField( _
    txtCtrl As Access.TextBox, _
    lblStatus As Access.Label, _
    vType As validationType) As ValidationResult
    
    Dim result As ValidationResult
    result = m_Validator.Validate(txtCtrl.value, vType)
    
    ' 更新 UI
    UpdateValidationStatus lblStatus, result, m_Validator.errorMessage
    HighlightTextBox txtCtrl, result
    
    ValidateField = result
End Function
' ========== 可选:失去焦点时验证 ==========
Private Sub txtEmail_LostFocus()
 
    Dim result As ValidationResult
    If Len(Nz(Me.txtEmail, "")) > 0 Then
        result = ValidateField(Me.txtEmail, Me.lblEmailStatus, vtEmail)
        ShowErrorTooltip Me.lblMobileError, m_Validator.errorMessage, (result = vrInvalid)
    End If
End Sub

5。运行测试

最后,就是运行测试了,我们来看一下效果。

这里的样式觉得不满意的,也可以自行调整。

设计思路

  • 采用面向对象(OOP) 的设计思路,将验证规则与 UI 渲染分离。
  • ClsFieldValidator (类模块):核心逻辑层。负责封装正则表达式、处理数值比较、日期校验,不包含任何 UI 代码。
  • M_ValidationUI (标准模块):UI 渲染层。负责操作 Access 控件的边框颜色、标签内容。
  • Form_xxx (窗体):调用层。在控件事件中实例化验证类并接收返回结果。

喜欢这篇文章?点个“在看”,分享给更多 Access 开发者!