我应该重新使用一个窗体上的子窗体控件还是只创建多个窗体?
在我65人的办公室里,我想为所有员工创建一个“门户”,只需一个.accdb文件。它将允许每位员工从下拉菜单导航到新的“屏幕”。我应该重新使用一个窗体上的子窗体控件还是只创建多个窗体?
我应该使用带有即插即用子窗体控件的单一窗体来集中VBA代码,还是应该只使用不同的窗体?
我想这将是很好,有一个形式与即插即用子窗体控件。当员工选择新的“屏幕”时,VBA只设置每个子窗体控件的SourceObject
属性,然后根据所选“屏幕”的布局重新排列子窗体。例如,我们目前使用一对Access数据库表单来输入和查看我们在工作流系统中发现的错误。因此,在这种情况下,检讨错误,我只想说
SubForm1.SourceObject = "Form.ErrorCriteria"
SubForm2.SourceObject = "Form.ErrorResults"
然后,我只想将它们移动到的地方(这些值将被动态地拉到根据所选择的“屏”):
SubForm1.Move WindowWidth * 0.05, WindowHeight * 0.05, WindowWidth * 0.9, WindowHeight * 0.2
SubForm2.Move WindowWidth * 0.05, WindowHeight * 0.25, WindowWidth * 0.9, WindowHeight * 0.65
因此,这将在窗体上创建一个小标题部分(SubForm1
),我可以选择我想要查看的错误的标准(数据范围,哪个团队提交错误等),然后我可以查看错误标题下面的较大部分(SubForm2
)将结果保存在数据表中。
我可以将事件传播到ErrorCriteria
和ErrorResults
现在绑定到子窗体控件的窗体中。这将帮助我使用描述为here的VBA的基本MVC设计模式。我可以将主窗体视为视图,即使部分视图隐藏在子窗体控件中。控制器只需要知道那个视图。
我的问题出现在用户从下拉菜单中选择一个新的“屏幕”。我认为这将是很好,只是重新利用子窗体控件,就像这样:
SubForm1.SourceObject = "Form.WarehouseCriteria"
SubForm2.SourceObject = "Form.InventoryResults"
然后就是移动/调整这些子窗体到适当的布局为“库存”屏幕。
这种方法似乎让用户界面设计更清晰,因为您基本上只需处理一个充当模板的主窗体,然后将值(SourceObject
属性)插入到该模板中。
但是,每次我们更改“屏幕”时,根据MVC设计模式,我们在幕后都会有一个完全不同的“模型”,并且会有一个新的“视图”。我想知道是否会在幕后干扰MVC VBA代码,或者如果VBA代码本身也可能被模块化(可能使用接口)以使其与用户界面一样适应。
从用户界面的角度和从VBA的角度来看,最简单的方法是什么?使用一个主窗体作为模板,其他窗体可以作为子窗体交换进出,或者只需关闭当前窗体并在用户从下拉菜单中选择新的“屏幕”时打开一个新窗体。
下面简单介绍一种“重新调整用途”或重新格式化多种用途的方法。关于更改VBA代码的问题,一个简单的解决方案是检查标签值或您在控件中设置的某个值,然后调用相应的VBA子例程。
我们有超过100个报告,每个都有自己的选择标准/选项和我们不希望创建的每个报告的唯一过滤器的形式。解决方案是识别报告可用的选择选项,确定这些选项的逻辑顺序,然后创建一个向用户显示选项的表格。
首先,我们创建了表:ctlReportOptions(PK = ID,ReportName,OptionOrder) 字段:ID(Int),ReportName(文本),OptionOrder(Int),ControlName(文本),ControlTop(Int),ControlLeft (Int),SkipLabel(是/否),ControlRecordsourc(文本) 注1:ID不是自动编号。
接下来我们用定义用户将看到的视图的记录填充。 注2:使用零ID,我们为报表上的EVERY字段创建记录,以便我们随时可以为开发人员重新绘制记录。
然后我们创建了窗体并为每个可能的过滤器放置了控件。 我们设置'默认值'属性作为默认值。
一些控件: 组合框来选择报告名称。对于更改事件添加代码如下:
Private Sub cboChooseReport_Change()
Dim strSQL As String
Dim rs As ADODB.recordSet
Dim i As Integer
Dim iTop As Integer
Dim iLeft As Integer
Dim iLblTop As Integer
Dim iLblLeft As Integer
Dim iLblWidth As Integer
Dim iTab As Integer
Dim strLabel As String
On Error GoTo Error_Trap
' Select only optional controls (ID <> 0); skip cotrols always present.
strSQL = "SELECT ctlRptOpt.ControlName, 'lbl' & Mid([ControlName],4,99) AS LabelName, SkipLabel " & _
"From ctlRptOpt WHERE (((ctlRptOpt.ID)<>0)) " & _
"GROUP BY ctlRptOpt.ControlName, 'lbl' & Mid([ControlName],4,99), SkipLabel;"
Set rs = New ADODB.recordSet
rs.Open strSQL, CurrentProject.Connection, adOpenDynamic
Do While Not rs.EOF
Me(rs!ControlName).Visible = False ' Hide control
If rs!skiplabel = False Then ' Hide Label if necessary
Me(rs!LabelName).Visible = False
End If
rs.MoveNext
Loop
rs.Close
iTop = 0
iTab = 0
' Get list of controls used by this report; order by desired sequence.
strSQL = "select * from ctlRptOpt " & _
"where [ID] = " & Me.cboChooseReport.Column(3) & _
" order by OptionOrder;"
Set rs = New ADODB.recordSet
rs.Open strSQL, CurrentProject.Connection, adOpenDynamic
If rs.EOF Then ' No options needed
Me.cmdShowQuery.Visible = True
Me.lblReportCriteria.Visible = False
Me.cmdShowQuery.left = 2000
Me.cmdShowQuery.top = 1500
Me.cmdShowQuery.TabIndex = 1
Me.cmdReset.Visible = False
rs.Close
Set rs = Nothing
GoTo Proc_Exit ' Exit
End If
' Setup the display of controls.
Me.lblReportCriteria.Visible = True
Do While Not rs.EOF
If rs!skiplabel = False Then
strLabel = "lbl" & Mid(rs!ControlName, 4)
iLblWidth = Me.Controls(strLabel).Width
Me(strLabel).top = rs!ControlTop
Me(strLabel).left = rs!ControlLeft - (Me(strLabel).Width + 50)
Me(strLabel).Visible = True
End If
iTab = iTab + 1 ' Set new Tab Order for the controls
Me(rs!ControlName).top = rs!ControlTop
Me(rs!ControlName).left = rs!ControlLeft
Me(rs!ControlName).Visible = True
If left(rs!ControlName, 3) <> "lbl" Then
Me(rs!ControlName).TabIndex = iTab
End If
If Me(rs!ControlName).top >= iTop Then
iTop = rs!ControlTop + Me(rs!ControlName).Height ' Save last one
End If
' If not a label and not a 'cmd', it's a filter! Set a default.
If left(rs!ControlName, 3) <> "lbl" And left(rs!ControlName, 3) <> "cmd" Then
If Me(rs!ControlName).DefaultValue = "=""*""" Then
' Me(rs!ControlName) = "*"
ElseIf left(Me(rs!ControlName).DefaultValue, 2) = "=#" And right(Me(rs!ControlName).DefaultValue, 1) = "#" Then
i = Len(Me(rs!ControlName).DefaultValue)
' Me(rs!ControlName) = Mid(Me(rs!ControlName).DefaultValue, 3, i - 3)
ElseIf Me(rs!ControlName).DefaultValue = "True" Then
' Me(rs!ControlName) = True
ElseIf Me(rs!ControlName).DefaultValue = "False" Then
' Me(rs!ControlName) = False
End If
Else
If Me(rs!ControlName).top + Me(rs!ControlName).Height >= iTop Then
iTop = rs!ControlTop + Me(rs!ControlName).Height ' Save last one
End If
End If
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
If Me.cboChooseReport.Column(1) <> "rptInventoryByDate" Then ' It's special
Me.cmdShowQuery.Visible = True
Me.cmdShowQuery.left = 2000
Me.cmdShowQuery.top = iTop + 300
iTab = iTab + 1
Me.cmdShowQuery.TabIndex = iTab
Else
Me.cmdShowQuery.Visible = False
End If
Me.cmdReset.Visible = True
Me.cmdReset.left = 5000
Me.cmdReset.top = iTop + 300
Me.cmdReset.TabIndex = iTab + 1
Proc_Exit:
Exit Sub
Error_Trap:
Err.Source = "Form_frmReportChooser: cboChooseReport_Change at Line: " & Erl
DocAndShowError ' Save error to database for analysis, then display to user.
Resume Proc_Exit ' Exit code.
Resume Next ' All resumption if debugging.
Resume
End Sub
lblReportCriteria:我们显示的最后一组过滤器,这样当用户抱怨没有显示在报告中,我们要求他们给我们发送丝网印刷。我们还将此文本传递给报告,并在最后一页打印为页脚。
cmdReset:将所有控件重置为其默认值。
cmdShowQuery:执行报告
Private Sub cmdShowQuery_Click()
Dim qdfDelReport101 As ADODB.Command
Dim qdfAppReport101 As ADODB.Command
Dim qdfDelReport102 As ADODB.Command
Dim qdfAppReport102 As ADODB.Command
Dim qryBase As ADODB.Command
Dim strQueryName As String
Dim strAny_Open_Reports As String
Dim strOpen_Report As String
Dim qdfVendorsInfo As ADODB.Command
Dim rsVendorName As ADODB.recordSet
Dim strVendorName As String
Dim rsrpqFormVendorsInfo As ADODB.recordSet
On Error GoTo Error_Trap
If Not IsNull(Me.cboChooseReport.value) And Me.cboChooseReport.value <> " " Then
strAny_Open_Reports = Any_Open_Reports()
If Len(strAny_Open_Reports) = 0 Then
If Me.cboChooseReport.value = "rptAAA" Then
BuildReportCriteria '
If Me.chkBankBal = True Then
DoCmd.OpenReport "rptAAA_Opt1", acViewPreview
Else
DoCmd.OpenReport "rptAAA_Opt2", acViewPreview
End If
ElseIf Me.cboChooseReport.value = "rptBBB" Then
If IsNull(Me.txtFromDate) Or Not IsDate(Me.txtFromDate) Then
MsgBox "You must enter a valid From Date", vbOKOnly, "Invalid Date"
Exit Sub
End If
If IsNull(Me.txtToDate) Or Not IsDate(Me.txtToDate) Then
MsgBox "You must enter a valid To Date", vbOKOnly, "Invalid Date"
Exit Sub
End If
Me.txtStartDate = Me.txtFromDate
Me.txtEndDate = Me.txtToDate
DoCmd.OpenReport Me.cboChooseReport.value, acViewPreview
ElseIf Me.cboChooseReport.value = "rptCCC" Then
If Me.txtVendorName = "*" Then
gvstr_VendorName = "*"
Else
Set rsVendorName = New ADODB.recordSet
rsVendorName.Open "selVendorName", gv_DBS_Local, adOpenDynamic
Set qdfVendorsInfo = New ADODB.Command
qdfVendorsInfo.ActiveConnection = gv_DBS_SQLServer
qdfVendorsInfo.CommandText = ("qryVendorsInfo")
qdfVendorsInfo.CommandType = adCmdStoredProc
strVendorName = rsVendorName("VendorName")
gvstr_VendorName = strVendorName
End If
DoCmd.OpenReport "rptFormVendorReport", acViewPreview
Else
BuildReportCriteria
If Me.cboChooseReport.value = "rptXXXXXX" Then
ElseIf Me.cboChooseReport.value = "rptyyyy" Then
On Error Resume Next ' All resumption if debugging.
DoCmd.DeleteObject acTable, "temp_xxxx"
On Error GoTo Error_Trap
Set qryBase = New ADODB.Command
qryBase.ActiveConnection = gv_DBS_Local
qryBase.CommandText = ("mtseldata...")
qryBase.CommandType = adCmdStoredProc
qryBase.Execute
End If
DoCmd.Hourglass False
DoCmd.OpenReport Me.cboChooseReport.value, acViewPreview
End If
Else
MsgBox "You cannot open this form/report because you already have a form/report(s) open: " & _
vbCrLf & strAny_Open_Reports & _
vbCrLf & "Please close the open form/report(s) before continuing."
strOpen_Report = Open_Report
DoCmd.SelectObject acReport, strOpen_Report
DoCmd.ShowToolbar "tbForPost"
End If
Else
MsgBox "Please Choose Report", vbExclamation, "Choose Report"
End If
Exit Sub
Error_Trap:
Err.Source = "Form_frmReportChooser: cmdShowQuery_Click - Report: " & Nz(Me.cboChooseReport.value) & " at Line: " & Erl
If Err.Number = 2501 Then ' MsgBox "You chose not to open this report.", vbOKOnly, "Report cancelled"
Exit Sub
ElseIf Err.Number = 0 Or Err.Number = 7874 Then
Resume Next ' All resumption if debugging.
ElseIf Err.Number = 3146 Then ' ODBC -- call failed -- can have multiple errors
Dim errLoop As Error
Dim strError As String
Dim Errs1 As Errors
' Enumerate Errors collection and display properties of each Error object.
i = 1
Set Errs1 = gv_DBS_SQLServer.Errors
Err.Description = Err.Description & "; Err.Count = " & gv_DBS_SQLServer.Errors.Count & "; "
For Each errLoop In Errs1
With errLoop
Err.Description = Err.Description & "Error #" & i & ":" & " ADO Error#" & .Number & _
" Description= " & .Description
i = i + 1
End With
Next
End If
DocAndShowError ' Save error to database for analysis, then display to user.
Exit Sub
Resume Next ' All resumption if debugging.
Resume
End Sub
功能运行构建出所有的选择标准的字符串:
Function BuildReportCriteria()
Dim frmMe As Form
Dim ctlEach As Control
Dim strCriteria As String
Dim prp As Property
Dim strSQL As String
Dim rs As ADODB.recordSet
On Error GoTo Error_Trap
strSQL = "select * from ctlRptOpt " & _
"where ID = " & Me.cboChooseReport.Column(3) & _
" order by OptionOrder;"
Set rs = New ADODB.recordSet
rs.Open strSQL, CurrentProject.Connection, adOpenDynamic
If rs.EOF Then
strCriteria = " Report Criteria: None"
Else
strCriteria = " Report Criteria: "
End If
Do While Not rs.EOF
Set ctlEach = Me.Controls(rs!ControlName)
If ctlEach.ControlType = acTextBox Or ctlEach.ControlType = acComboBox Then
If ctlEach.value <> "*" And ctlEach.Name <> "cboChooseReport" And ctlEach.Name <> "cboLocCountry" Then
strCriteria = strCriteria & ctlEach.Tag & " = " & ctlEach.value & " , "
End If
End If
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
If Me.chkOblBal = -1 Then
strCriteria = strCriteria & "Non-zero balances only = Yes"
Else
'return string with all choosen criteria and remove last " , " from the end of string
strCriteria = left$(strCriteria, Len(strCriteria) - 3)
End If
fvstr_ReportCriteria = strCriteria
Set ctlEach = Nothing
Exit Function
Error_Trap:
If Err.Number = 2447 Then
Resume Next ' All resumption if debugging.
End If
Err.Source = "Form_frmReportChooser: BuildReportCriteria at Line: " & Erl
DocAndShowError ' Save error to database for analysis, then display to user.
Exit Function
Resume Next ' All resumption if debugging.
End Function
最后,每个报告有它自己的查询,将过滤器的基础在此窗体上的控件中的值。
希望这会有所帮助。如果你对你看到的任何奇怪的事情感到好奇,请告诉我。 (即我们总是在代码中使用行号(我在发布前删除),使我们能够在那里代码无法确定确切的行)
为什么使用MS Access的ADOB?建议使用DAO,速度要快得多。 ADODB确实有它的位置,但并非总是如此。 – Fionnuala
我们从Access迁移到SQL Server,因此ADO的东西:)我同意DAO并将其作为我的第一选择! –
感谢您的详细解答!我需要一些时间才能解决这个问题,但它解决了我正在努力解决的确切问题。 – BarrettNashville
首先,我当然希望你有一个前端/后端设置,如果你有65人!要在表单界面上给出建议,它取决于您拥有的控件的数量/复杂程度。我们开发了一个“报告界面”,根据用户的角色,他们可以看到从1到20个过滤器(控件)的任何地方,因此使用了一个带有“地图”表的表单。你的表格有多复杂? –
是的,它是一个分割数据库。你如何使用该地图表?听起来不错。 – BarrettNashville
映射表包含字段:ID(PK),ReportName,CtlName,CtlOrder,CtlTop,CtlLeft,SkipLabel(Bool),CtlRecordSource。除非报告使用,否则所有ctls都是隐藏的。当用户从组合框中选择报告时,将从地图表中检索到的字段列表和表单被更改。 –