写一个过滤表单请求的过滤器
级联表单过滤
第1章-目录表(包括附加的数据库) 第2章-简介 第3章-表布局 第4章-表单布局 第5章-表单模块 第6章-代码讨论(过滤器控件) 第7章-代码讨论(REST)-------------------------------------------------- ------------------------------
第2章-简介关于此练习,我需要解释的第一件事是,尽管我尝试使用具有一定意义的数据,但从根本上讲,这是一个练习,它说明了在各种情况下尤其需要过滤和级联过滤的各种可用概念。
这是一个相当基本的示例,并且可以与其他绑定形式的页眉节中的一些未绑定选择控件一起使用。 创建此练习时,没有使用或滥用SubForm。
在此示例中,我将一个名为frmAccount的表单绑定到表tblAccount。 表单具有显示为(绑定)控件的所有表字段(主要是TextBox-名称以'txt'开头,还有ComboBox-cboAccountType)。 它还具有四个额外的(未绑定)控件,使操作员可以通过AccountName(或其一部分),CreationDate和SalesThisYear选择项目。 选择这些字段仅出于说明目的,不应准确反映任何现实情况。
为避免混淆,最好选择具有含义的名称,以反映其内容,同时避免使用特殊字符(包括空格)和已经用于其他用途的名称。 后者的一个很好的例子是“日期”。 在等待中将其用作字段或控件名称是一个问题。
-------------------------------------------------- ------------------------------
第3章-表布局 表名= [ tblAccount ]AccountID; Autonumber; PK
AccountCode; String
AccountName; String
CreationDate; Date/Time
AccountType; Numeric; 0==>Control (Internal Usage); 1==>Supplier; 2==>Customer
SalesThisYear; Numeric
-------------------------------------------------- ------------------------------ 第4章- 表单 布局 表单名称= [ frmAccount ]
cboByAccountType; ComboBox; Unbound
txtByAccountName; TextBox; Unbound
txtBySalesLastYear; TextBox; Unbound
cboFromCreationDate; ComboBox; Unbound
cboToCreationDate; ComboBox; Unbound
txtAccountID; TextBox; Bound to [AccountID]
txtAccountCode; TextBox; Bound to [AccountCode]
txtAccountName; TextBox; Bound to [AccountName]
txtCreationDate; TextBox; Bound to [Creation]
cboAccountType; ComboBox; Bound to [AccountType]
txtSalesThisYear; TextBox; Bound to [SalesThisYear]
-------------------------------------------------- ------------------------------ 第5章-表格模块
Option Compare Database
Option Explicit
Private Const conDateSource As String = "SELECT DISTINCT [CreationDate] " & _
"FROM tblAccount " & _
"ORDER BY [CreationDate]"
Private Sub cboByAccountType_AfterUpdate()
Me.cboAccountType.DefaultValue = Nz(Me.cboByAccountType, "")
Call Cascade
Call ApplyFilter
End Sub
Private Sub txtByAccountName_AfterUpdate()
Call Cascade
Call ApplyFilter
End Sub
Private Sub txtBySalesThisYear_AfterUpdate()
Me.txtSalesThisYear.DefaultValue = Nz(Me.txtBySalesThisYear, "")
Call Cascade
Call ApplyFilter
End Sub
Private Sub cboFromCreationDate_AfterUpdate()
Dim strDefault As String
With Me
If IsNull(.cboFromCreationDate) Then
strDefault = ""
Else
strDefault = "#" & Format(CDate(.cboFromCreationDate), _
"m/d/yyyy") & "#"
End If
.txtCreationDate.DefaultValue = strDefault
End With
Call ApplyFilter
End Sub
Private Sub cboToCreationDate_AfterUpdate()
Call ApplyFilter
End Sub
'Cascade filters the two unbound filter ComboBoxes cboFromCreationDate &
'cboToCreationDate by the values already entered/selected from the top level
'unbound controls in the Header Section (cboByAccountType; txtByAccountName &
'txtBySalesThisYear. As cboFromCreationDate is always reset in here, the
'DelfaultValue for txtCreationDate must be also.
Private Sub Cascade()
Dim strSQL As String, strFilter As String
With Me
.cboFromCreationDate = Null
.cboToCreationDate = Null
.txtCreationDate.DefaultValue = ""
strSQL = conDateSource
strFilter = GetFilter()
If strFilter > "" Then _
strSQL = Replace(strSQL, "ORDER", "WHERE " & strFilter & " ORDER")
.cboFromCreationDate.RowSource = strSQL
.cboToCreationDate.RowSource = strSQL & " DESC"
End With
End Sub
'ApplyFilter applies new Filter from GetFilter().
Private Sub ApplyFilter()
Dim strFilter As String, strOldFilter As String
With Me
strOldFilter = .Filter
strFilter = GetFilter
If strFilter <> strOldFilter Then
.Filter = strFilter
.FilterOn = (strFilter > "")
End If
End With
End Sub
'GetFilter returns the new filter depending on the values currently in
'the unbound controls in the Header Section (cboByAccountType; txtByAccountName;
'txtBySalesThisYear; cboFromCreationDate & cboToCreationDate.
Private Function GetFilter() As String
Dim strFilter As String
With Me
'cboByAccountType - Numeric - Exact match
If Not IsNull(.cboByAccountType) Then _
strFilter = strFilter & " AND " & _
"([AccountType]=" & _
.cboByAccountType & ")"
'txtByAccountName - Text - Like / Wildcards
If Not IsNull(.txtByAccountName) Then _
strFilter = strFilter & " AND " & _
"([AccountName] Like '*" & _
.txtByAccountName & "*')"
'txtBySalesThisYear - Numeric - Greater or Equal
If Not IsNull(.txtBySalesThisYear) Then _
strFilter = strFilter & " AND " & _
"([SalesThisYear]>=" & _
.txtBySalesThisYear & ")"
'cboFromCreationDate - Date - Between or Equal
If Not IsNull(.cboFromCreationDate) Then
strFilter = strFilter & " AND ([CreationDate]"
If IsNull(.cboToCreationDate) Then
strFilter = strFilter & "=#%F#)"
Else
strFilter = strFilter & " Between #%F# And #%T#)"
strFilter = Replace(strFilter, _
"%T", Format(CDate(.cboToCreationDate), _
"m/d/yyyy"))
End If
strFilter = Replace(strFilter, _
"%F", Format(CDate(.cboFromCreationDate), _
"m/d/yyyy"))
End If
'Debug.Print ".Filter = '" & strOldFilter & "' - ";
'Debug.Print "strFilter = '" & strFilter & " '"
'Tidy up results and apply IF NECESSARY
If strFilter > "" Then strFilter = Mid(strFilter, 6)
GetFilter = strFilter
End With
End Function
-------------------------------------------------- ------------------------------ 第6章-代码讨论(过滤器控件)
每个控件的过滤器部件代码均以其开头为基础,以检查其是否为空。 当其中没有数据时,任何控件都将为Null。
每个都以“ AND”开头,但是稍后将其从找到的第一个中剥离,然后再应用。
构建的每个检查都用括号()包围,以确保逻辑与其他检查不混淆。
每个未绑定的过滤器控件都有一个
与之关联的AfterUpdate事件过程。 从根本上讲,每一项都需要执行三项任务,尽管并非所有这些控件都需要全部三项:- 设置相关的.DefaultValue属性。
只需cboByAccountType , txtBySalesThisYear和cboFromCreationDate即可执行此操作。 - 级联所选值以反映在cboFromCreationDate和cboToCreationDate ComboBox列表中。
只需cboByAccountType , txtByAccountName和txtBySalesThisYear即可执行此操作。 - 过滤表单以反映所有过滤器控件。
所有控件都这样做。
当所有控件都向正在进行的过滤器字符串中添加任何内容时,所有控件始终以字符串“ AND”开头。 必须在过滤器字符串的每个元素之间进行分隔,以确保将所有元素都考虑在内。 当然,这将在一开始就留下一个无关紧要的东西。 可以很容易地安排它在末尾有多余的一个,但是从字符串开头剥离它的代码比从末尾剥离它的代码更直接,因为这需要首先确定字符串的长度。 。
为了说明,我在下面的两行代码中包括以下内容:
If strFilter > "" Then strFilter = Mid(strFilter, 6)
If strFilter > "" Then strFilter = Left(strFilter, Len(strFilter) - 5)
显然,第1行比第2行短,涉及的函数调用和算法更少。
第2条线可能更接近人类的思维方式,因此您可能会看到许多示例。
数
数字文字不需要分隔符。
我们有两个数字控件。 一个处理完全匹配,另一个处理大于或等于匹配。
完全匹配-cboByAccountType我们从字符串“ AND([AccountType] =”然后从cboByAccountType控件添加(附加字符串))值开始,然后(最后)添加字符串“)”。 使用cboByAccountType = 1,结果为:
AND ([AccountType]=1)
这将查找[AccountType]为1(客户)的所有记录。
大于或等于匹配-txtBySalesThisYear
我们从字符串“ AND([SalesThisYear]> =“开始,然后添加(附加字符串)”来自txtBySalesThisYear控件的值,然后(最后)添加字符串“)”。 使用txtBySalesThisYear = 1,000,结果为:
AND ([SalesThisYear]>=1000)
这将查找SalesThisYear> = 1,000的所有记录。
文本
文本文字的分隔符是(')-请参见
引号(')和双引号(“)-在何处以及何时使用它们以获取更多信息。文本过滤是作为“类似”构造完成的,因为帐户名称可能是唯一的(在这种情况下,完全匹配是毫无意义的)。 我们从字符串“
AND([[AccountName]像'* “,然后从txtByAccountName控件添加(附加字符串)值,然后(最后)添加字符串” *') “。使用txtByAccountName =” pub“,结果为: AND ([AccountName] Like '*pub*')
这将找到[AccountName]包含“ pub”的任何记录。
日期
日期文字的分隔符是(#)-请参见
有关此内容的更多信息,请参见文字日期时间及其定界符(#) 。首先,为了描述cboFromCreationDate控件返回一个版本的操作,我们可以有用地传递给SQL。 在代码中说:
Format(CDate(.cboFromCreationDate), "m/d/yyyy"))
操作员可以以他们希望的任何格式输入数据。
只要是有效日期,我们就不会在意。
也就是说,我们确实需要由我们的代码正确解释数据。
CDate()调用可确保首先将数据转换为有效的日期值。
这将使用本地设置来解释什么本质上是字符串数据。
接下来, Format()调用将该日期转换为适合SQL的格式化字符串。
特别是m / d / y格式。
他们可以输入“ 2007年2月1日”,也可以输入英格兰的“ 1/2/2007”或美国的“ 2/1/2007”。 无论输入什么
CDate()将使用本地设置将其转换为有效的日期/时间值。 SQL要求任何日期文字都必须明确,但坚持要求使用美国短日期格式(m / d / y),而不要使用本地日期(无论哪种格式)。 有关更多详细信息,请参见上面的链接文章。由于我们还可以选择输入To Date,因此此处的筛选必须确定操作员是否输入了To值。 如果有,它将使用
在#X#和#Y#构造之间。 否则,它仅使用=#X# 。因此,我们以字符串“
AND([[CreationDate] “,然后我们确定是否有截止日期,然后从那里去。如果有截止日期,我们添加(附加字符串)字符串“
在#%F#到#%T#)之间 。否则,我们只需添加“ =#%F#)即可 。接下来,我们将用此字符串中的%F和%T替换控件中的实际值。使用
Replace()函数,我们去除了这些可替换标记(%F和%T),并将其替换为控件中的值。 如果在字符串中未找到要替换的令牌,则不会对字符串进行任何更改(该函数的结果与传递的主字符串相同)。因此,如果我们只是在cboFromCreationDate中输入“ 2007年2月1日”,那么生成的过滤器元素将是:
AND ([CreationDate]=#2/1/2007#)
但是,如果我们随后在cboToCreationDate中添加“ 2007年2月28日”,则生成的过滤器元素将为:
AND ([CreationDate] Between #2/1/2007# And #2/28/2007#)
无论哪种方式,我们都能得到我们所期望的。
如果对这一切如何工作有任何疑问或困惑,我强烈建议您在靠近底部的“调试”行之前删除注释标记。
GetFilter()函数过程,并查看打印到VBA IDE窗口的“即时”窗格中的内容。-------------------------------------------------- ------------------------------
第7章- 更新事件后的 代码讨论(REST)这俩
Cascade()和ApplyFilter()子例程使用GetFilter()函数。 Cascade()只是确保在继续之前重置两个日期过滤器控件(层叠到 )。 然后,它调用GetFilter()确定整个筛选器字符串的值,然后将其应用于两个Date控件的.RowSource属性。 它使用Replace()函数将可选的WHERE子句插入conDateSQL中找到的初始SQL中。 ApplyFilter()同样非常简单。 它从GetFilter()检查建议的过滤器,并将其与现有过滤器进行比较。 仅当它们不同时,才应用新的。 有关执行此操作的详细信息,请参阅下面的“ 应用过滤器” 。 GetFilter()主要与上面第6章中的详细描述( 代码讨论(过滤器控件) )相同。我注意到许多人在区分VBA处理的内容和传递给SQL引擎(解释器)的内容方面遇到问题。 它可以帮助您了解是否取消注释
从“查找”控件中选择一个或多个值后, “调试。打印行并查看结果。 这介于VBA处理和SQL之间,因此在该阶段可能会有所帮助。 应用过滤器首先,我们检查过滤器是否确实与当前过滤器不同。 如果是的话,我们需要设置过滤器,并根据strFilter是否为空来确定是否应使用过滤器(.FilterOn)。
-------------------------------------------------- ------------------------------
翻译自: https://bytes.com/topic/access/insights/893657-cascaded-form-filtering
写一个过滤表单请求的过滤器