MS Access Search Form for Large database - ms-access

I'm building a database that will be accommodating a large number of records, so I want a search function that is easiest on the server. I am using the following code but I know it isn't sustainable for a larger database. It's looking at the search box and running a query to narrow the search results:
Private Sub SearchFor_Change()
'Create a string (text) variable
Dim vSearchString As String
'Populate the string variable with the text entered in the Text Box SearchFor
vSearchString = SearchFor.Text
'Pass the value contained in the string variable to the hidden text box SrchText,
'that is used as the sear4ch criteria for the Query QRY_SearchAll
SrchText.Value = vSearchString
'Requery the List Box to show the latest results for the text entered in Text Box
'SearchFor
Me.SearchResults.Requery
'Tests for a trailing space and exits the sub routine at this point
'so as to preserve the trailing space, which would be lost if focus was shifted from
'Text Box SearchFor
If Len(Me.SrchText) <> 0 And InStr(Len(SrchText), SrchText, " ", vbTextCompare) Then
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be feeding
'off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor,
'and restores trailing space lost when focus is shifted to the list box
Me.SearchFor = vSearchString
Me.SearchFor.SetFocus
Me.SearchFor.SelStart = Me.SearchFor.SelLength
Exit Sub
End If
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be
'feeding off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor
Me.SearchFor.SetFocus
If Not IsNull(Len(Me.SearchFor)) Then
Me.SearchFor.SelStart = Len(Me.SearchFor)
End If
Ideally I want a form that has several search fields, and one 'find' button that runs the queries to return the results in a list box.
I'm also not sure how to set it up so that when the user double clicks on a selection from the search results, the selected record is opened in a form in edit mode.
Any help would be much appreciated, thanks!

First off, you've asked two questions in one post. I recommend you take out the second question regarding opening the selection in edit mode on double click.
As best as I can understand, you're concerned about the performance of your current code as well as the lack of features or flexibility it offers.
Regarding performance:
Don't use the change method to perform the filter. If you really do want to use the change method, use it only to set a timer interval to something like 500 (ms) and then perform the filter on the Timer event. This was the filter won't occur until after the user has stopped typing for a half second.
Avoid "fuzzy" searches (use of asterisk/percent in text fields). It doesn't look like you're using them now. While fuzzy searches usually make software more user friendly, they make it less user friendly when they cause a significant hit on the performance.
When working with large amounts of data, most performance gains come from carefully restructuring the way your application works, by upgrading to SQL Server, and by upgrading your server and network to better hardware. You can only improve about so much when using a JET/ACE backend database container. SQL Server with ADO and ODBC linked tables both offer some advantages over DAO with JET/ACE. ODBC linked tables offer lazy loading while ADO offers things like disconnected recordsets which can be filtered without an additional call back to the server (there are limitations to this).
As already mentioned above, you might need to carefully rethink how your application works and how it is designed. It's better to try to limit the amount of complicated queries that are needed and the amount of text-based searching that is allowed/required. Use more lookup/reference tables. Instead of storing thinks like categories as text, consider storing them as a Long Number CategoryID instead. Queries on indexed numeric fields usually perform better than queries on text-based fields, especially if you are using LIKE with asterisks in your query.
As far as the rest of your question (flexibility and features), consider creating a procedure that builds a criteria/where statement for you based on the values of multiple controls. In a situation such as yours my code would look something like this (below). Notice that I did use asterisk (fuzzy search) in my Description search/filter. If it performs poorly you'll need to consider taking that out and allowing the user to put their own asterisks in instead.
Private Sub cmdSearch_Click()
Call SetRowSource
End Sub
Private Sub txtSearch_AfterUpdate()
Call SetRowSource
End Sub
Private Sub cboCategoryID_AfterUpdate()
Call SetRowSource
End Sub
Private Sub txtBrand_AfterUpdate()
Call SetRowSource
End Sub
Private Sub SetRowSource()
Dim sSQL as String
sSQL = "SELECT ItemID, Description, Brand FROM tblItems "
sSQL = sSQL & GetWhere
Me.lstSearchResults.RowSource = sSQL
End Sub
Private Function GetWhere() as String
Dim sWhere as String
If Nz(Me.cboCategoryID, 0) <> 0 Then
sWhere = sWhere & "CategoryID = " & Me.cboCategoryID & " AND "
End If
If Nz(Me.txtSearch, "") <> "" Then
sWhere = sWhere & "Description LIKE '*" & Replace(Me.txtSearch, "'", "''") & "*' AND "
End If
If Nz(Me.txtBrand, "") <> "" Then
sWhere = sWhere & "Brand = '" & Replace(Me.txtBrand, "'", "''") & "' AND "
End If
If sWhere <> "" Then
sWhere = Left(sWhere, Len(sWhere)-5)
GetWhere = "WHERE " & sWhere
End If
End Function
I think I might be a little bit odd in the Access community but I generally do not allow my controls to reference other controls. In your case the RowSource in your listbox references the controls of the form it's located on. For a variety of reasons, I prefer to build my SQL statements in VBA code, particularly when they are subject to change/filtering. Another thing you might consider doing is using a Datasheet form instead of a listbox. You can set the form's RecordSource and just apply your WHERE statement to the form's Filter property then. Datasheet forms are more flexible for the user as they can resize columns and do sorting without any help from you the programmer. You can always lock the controls so they can't do any editing. When I use datasheets this way I think use the DoubleClick event to allow them to open the record, which is arguably less user friendly then using the single click on a listbox.

Related

MS Access Tab Control Name with Number of Records

I am developing an Access database (using Office 2016) and have several tab controls which I want to display the number of records in the subform/subreport.
After a lot of searching etc I have it working for the subforms using a function which I call in the main forms current event (but in a seperate function so I can also call via a macro when I change the main forms record with a combo box, as it wasn't updating otherwise). The code I am using is:
Function ClientTotals()
Dim i As Integer
i = Form_sbfrm_ClientContacts.Recordset.RecordCount
Form_frm_Clients.ClientTabs.Pages("Contacts").Caption = "Contacts (" & i & ")"
End Function
This works perfectly for me and my tab name becomes "Contacts (No. of records)" but I can't get the syntax right to change this to work for a report, is it possible?
I have tried:
Function ClientTotals()
Dim i As Integer
i = Form_sbfrm_ClientContacts.Recordset.RecordCount
Form_frm_Clients.ClientTabs.Pages("Contacts").Caption = "Contacts (" & i & ")"
Dim j As Integer
j = Report_rpt_CurrentProjects.Recordset.RecordCount ' this line is highlighted with the debugger
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
End Function
As well as:
Dim j As Integer
j = rpt_CurrentProjects.Report.Recordset.RecordCount ' this line is highlighted with the debugger
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
and various others.
Another question I have is why is the syntax for the form "Form_sbfrm" etc and not using a "!". If I change to "!" it bugs out.
Thanks for your help, KAL
Thanks Delecron,
I think I will stick with the tabs for now as they are giving me exactly what I want, but remember what you have said for when I make future improvements if its a better way of doing it.
EDIT
Using what you have said I changed my VBA to a DCOUNT method:
Dim j As Integer
j = DCount("*", "qry_CurrentProjects", "FK_Project_Client_ID = Forms!Navigation!Navigationsubform.form!Client_ID")
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
This means my report tabs are now also working just how I wanted
I was getting in a muddle with the criteria/filter, hense the edit.
If Recordset is an old method I am assuming it would be best to replace my other code with the Dcount method?
Thanks again, KAL
Further EDIT
After doing this I could see that everytime the form was changed there was a slight flicker. Not bad but you could see there was a lot of calculation going on. Therefore I have changed my method to the following, and posted here for anyone looking at this in the future.
In the form footer a textbox with COUNT([Project_ID])
In my function
Dim j As Integer
j = Form_frm_Clients!rpt_CurrentProjects.Report!txt_CurrentProjectsCount.Value
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
Now I can see it is working quicker with no flicker.
Recordset if you need to return complex data, if you need one value, a total or a sum, Domain functions are the way to go. Don't overdue them though, having too many on a form or a report can start to bog down usability.
Glad I can help.
Recordset.Recordcount is a legacy feature that only worked in ADP files (Access front end's to a SQL database). Those are no longer supported.
If the report is based on 1 client only and there is no grouping then you can do this:
Click on the detail section and in Events create an event for On Paint. In there set
(Name of Page).Caption = DCount("*", "NAME OF QUERY/TABLE") or
(Name of Page).Caption = DCount("*", "NAME OF QUERY/TABLE", "Filter Expression") (Filter expression optional).
If the report is grouped where it will show a different page per client or date range or any other grouping this will not work since the Caption field is not data bound. You would have to add logic to the Dcount statement above to field by the current filter condition.
For example, say you have a database of 200 clients and you're running one big report on all of them, each page will get its own tab control per client, the syntax would be
(Name of Page).Caption = DCount("*", "ClientContacts, "ClientID = " & ClientID)
The better way to do it especially if you are grouping is get rid of the tab control and use databound controls. You could create a box around the information that would be in the tab page and put a textbox where the tab would go. Create a group header for however you are grouping the data. Create another invisible textbox in the group header and set the controlsource = Count([fieldname]) where fieldname is whatever you are grouping the data by (the inside brackets count).
Then in the textbox you created to simulate the tab, set the controlsource to the name of the invisible textbox.
Let me know if this helps.

MS Access TextBox Value is always NULL

I am doing some work for someone that insists on using MS Access. I don't usually use it so I am a bit new to the whole control structure and best practices. What I am trying to achieve is to have a filter textbox on a form which, when a value is entered, it will filter the rows in the detail section. That seems like a straightforward use case. I initially tried the following behaviour as the event handler for the On Change event:
Private Sub FilterGrid()
Me.Text32.SetFocus
If Not IsNull(Me.Text32.Text) And Me.Text32.Text <> "" Then
Me.Filter = "JobNumber LIKE '*" & Me.Text32.Text & "*'"
Me.FilterOn = True
End
Else
Me.FilterOn = False
End If
End Sub
This worked perfectly until I typed something that didn't have any rows matching and the whole thing exploded with this error (and was unrecoverable without closing the form):
Run-time error '2185': You can't reference a property or method for a control unless the control has the focus.
I did some reading around and the general opinion was that .Text should not be used and .Value (or simply the Text32 without a property) should be used. That produced some very strange behaviour. The Text32.Value is ALWAYS null. I have a watch window and I can see that for the normal behaviour, Text32.Text has an actual value, but Text32.Value is NULL.
Obviously I am doing something wrong, but I don't have enough experience with Access to know what it is.
Just as an aside, another suggestion was to do Text32.SetFocus right before accessing the Text property. This doesn't resolve the error I mentioned. It still throws the exact same error.
Is anyone able to point me in the right direction here?
As you found out, the textbox's Value is only set after the control loses focus.
Conversely, the Text property is only accessible while the control has focus.
The Value property is defined as the default member for controls; that means Text32 will be implicitly the same as Text32.Value, however, depending on the context,Text32 can sometimes refer to the control itself, not just its value.
All these discrepancies can sometimes be infuriating.
To go back to the matter at hand: you have 2 ways to handle filtering.
if the list to filter is large, it's probably better that the user type their filter, then press ENTER to validate it.
if your list is not too large, you can implement filter as you type.
First case, wait for user input to be validated by ENTER.
Say your filtering textbox is called txtFilter and is located on a form whose subform is showing a datasheet (or continuous form) that you want to filter.
All you need to do is wire the textbox OnKeyDown events as such:
' We will only perform the filter if the user press the ENTER key
Private Sub txtFilter_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case 13, 9
KeyCode = 0
QuickFilter
End Select
End Sub
' Perform the actual filtering on the subform
Private Sub QuickFilter()
Dim sql As String
Dim filter As String
If txtFilter.Text = vbNullString Then
' Reset the filter if the textbox is emtpy
SubForm.Form.FilterOn = False
Else
'Some common substitutions that users may have already inserted as wildchars
filter = Replace(txtFilter.Text, "%", "*")
filter = Replace("*" & filter & "*", "**", "*")
' We construct the filter SQL
sql = "([JobNumber ] LIKE """ & filter & """)"
sql = sql & " OR ([ProjectCode] LIKE """ & filter & """)"
sql = sql & " OR ([SupplierName] LIKE """ & filter & """)"
'... Add as many columns to filter on as you want
' Assign the filter to the subform
SubForm.Form.filter = sql
SubForm.Form.FilterOn = True
End If
End Sub
Second case, filter as you type
Well, it's fairly easy, we just need to add to the above solution a way to track changes as the user is typing.
This is best done through the OnChange event of the texbox.
Private Sub txtFilter_Change()
QuickFilter
End Sub
That's all you need to add.
dont use .text property
instead use .value property
text3.value=text1.value+text2.value
you dont need setfocus like .text property everytime.
it works perfectly....vba ms access

MS Access: Filter/Search records in a form using main form and subform criteria from user input

I'm currently working on a database which is rather complex, at least for my ability level. Essentially it is a database of projects, structures and contacts. Within these structures there are sub structures each with unique attributes.
Projects, structures and contacts are joined together in one main control form with a tabbed pane. And within these panes are the forms which have multiple subforms. I am trying to write a search function which I can place on each form that will allow the user to select multiple criteria to filter results. However, some of the criteria or on the forms and other criteria are drawn from multiple subforms and to top it off some fields in the subforms have multiple entries. I understand that this may not sound like very sound database design, but I've been told to adhere to a specific structure and layout which I am not skilled enough to work through.
I've been looking through allen browne's website regarding search criteria, but that is only one a single form. I've attempted to piece together the Subquery tutorial page as well, but to no avail. Essentially, I'm wondering if it is at all possible to filter information say.. the type of structure which is on the main form and then for example.. a range of numbers such as length which exists in a subform and an object proximity which exists in another subform, and have it so that the main form only displays records which fulfill that combination of criteria from the subforms and main form.
My apologies if this is vague, and if it'll help I can post up the general framework I have for my database thus far. Thank you for any assistance you can provide.
EDIT--- Added screenshots for more information
I can't post images since my rep is below 10 but hopefully I can include links
http://img829.imageshack.us/img829/1594/99258898.png
http://img40.imageshack.us/img40/2186/27578829.png
It's difficult to see here, but within the combo boxes some are multi-valued lists. And the combo box on the first image to the right, is switched based on the selection of Type from the upper left combo box, which has different attributes. Ideally I'd like to be able to search through primary forms and subforms. and result in records which only fulfill all the user inputted data. Right now I've tried using filters, one for each criteria, but all that does is blank out information in the subforms which doesn't correspond to the filter criteria.
Do you have any screenshots you could possibly post?
This sounds to me like a larger design issue, perhaps mostly in your UI but possibly in your table structure as well. One my own principles I try to follow is to limit how many things a single screen/form does or shows. What your describing sounds quite confusing to me and I think the average user could easily be overwhelmed by it.
Normally subforms are the result of a main form. That is, they contain child records and are linked to records in the main form via the child records foreign key and the main forms primary key. It isn't that there aren't other ways to design your forms but this is considered "normal" or "standard" design. Your description indicates that filtering the main form is determine by filters set on the subforms. I guess I can't think of any problem that could possibly need solving through something as confusing and clumsy as this. I leave the possibility open that I haven't encountered something as difficult or complex as you're dealing with.
I think you may have some difficulty getting better help here as your question is really better suited to a discussion forum such as UtterAccess.com. You may consider posting a copy of your database on such a site so the users can review your design and possibly recommend a more solid and standard approach.
Edit1:
1) Based on your screenshots, I think you're trying to do too many things in one form. I would normally take each of your top tabs (Projects, Structures, Companies, Contacts) and make each of those their own form. Access 2007 and 2010 allow you to use a tabbed interface natively and I can't think of any real benefit to using the tabbed container control to develop your own tabbed interface.
2) It's my opinion that searches should normally be performed in a single main form that has a single listbox, datasheet view subform, or continuous forms subform. Whichever of these three you choose is not overly important. After performing the search, the user should be able to click in the resulting records in the listbox or subform to select that record and move to it on a details view form similar to what you've already built here. It might take several different search screens to allow your users to search all of the different ways you'd like them to. For example, you might build a screen to search projects, one to search structures, another to search contacts, and a fourth one to search companies. It should be possible to combine some of these in a query and allow the user to search by more than one criteria but it can get confusing if you try to allow them to search too many fields at once, especially if the fields they are searching are in separate tables. For example, if you build a query that shows all projects, structures, and companies together and use this as the recordsource for a datasheet subform, this could allow users to search by project, structure, or company. However, if a project has 10 structures that project will be listed 10 times in the query/form which could confuse users if they are thinking about searching by project. If they are instead searching by structure, it will make perfect sense to them that there are 10 entries for the same project since they'll see 10 different structures.
My basic recommendation is, be careful about trying to build too many functions into a single form. There are some types of tasks or sets of data that do require complex forms with multiple subforms and lots of controls, but in general you want to try to avoid this. It's better to build each form with a very focused task and just simply build more forms to accomplish all of the necessary tasks. Of course, you can take this approach too far as well but you do really need to try to minimize complexity to save yourself from headaches in the future.
As far as filtering based on multiple criteria, I'll see if I can find any good examples or maybe put one together and make it available. It isn't so difficult but it is a multi-step process to build and experience has led me to a general design that I'm happy with and that works almost flawlessly in every possible situation.
Edit2:
Here's how you can create a multi-field filter/search.
1) Build your query. Include all tables and fields which need to viewed by the user or searched on by the user. You can include fields here that will be searched on but not viewable by the user, although that could be confusing to the user. Save your query and give it an appropriate name. Be sure to include your id/primary key fields since those will be needed in step number 4.
2) Now Highlight your query in the navigation pane, go to the top menu and choose Create > More Forms > Datasheet (I'm using Access 2007). Allow access to create and show you a datasheet form which we will use as a subform. Save this and give it an appropriate name like subformProjectSearch of fsubProjectSearch.
3) Get the design view for the new form you just created. Change the properties to disallow additions, deletions, and edits unless you'd really like to allow any of these operations. In general, I don't allow any data entry in these kinds of search forms unless I have it well planned out and tested.
4) Go to the code for this form and add code to popup relevant detail forms on double click. For example, in the projectid or project name field, allow your user to double click to bring up the project details form. Add double click routines to any other textbox where the user might want to pop up a structure, company or contact, assuming you've chosen to include one or more of those in your query. You're code will look something like DoCmd.OpenForm "ProjectDetails", , , "ProjectID = " & Me!ProjectID Now save your form again and close it.
5) Now create a new blank form and add your first form as a subform. Leave room at the top to add controls for filtering. This form does not need to have any recordsource set.
Diversion: I usually take one of two different approaches to filtering. Number one is to give the user one textbox that they can type into. I think do fuzzy searches in a whole bunch of different fields using the data they've typed in. It's a perfectly awful approach performance wise, especially once the database grows past about 20,000 records. It can also lead to some confusion since too many results will likely come up for a short generic search. If they only type in the letter s and search for it they will likely get almost all records.
The second approach I use is probably more common. Give them a different textbox for every field they are likely to want to search by. You an also make it a checkbox or combobox if that fits the field they are searching on. These will not be bound controls. This allows the user to get fairly specific and it also is most likely to return the exact results they are looking for. As far as the code goes, I don't think that one approach is really much simpler than the other. With either approach that I've listed here you really need to check the type of data they've entered before you put it in your filter statement for certain fields. For example, you wouldn't want to try to filter a date field using their non-date, text input. On this second approach you can possibly eliminate more of this problem by using validation rules, input masks, or by setting the textboxes format property. Combos also help to prohibit incorrect data from being entered.
Yet a third option is somewhat of a hybrid approach. You can create one text box that searches on more than one field but not necessarily on all fields. For example, you might create one textbox that will be used to search on Company, FirstName, or LastName. You can create as many of these textboxes as you need. How the input in these textboxes gets used will be totally determined by you in your VBA code.
6) Now that you've decided which approach you want to take, you can go ahead and add your controls. Give them appropriate names and labels.
7) Now it's time to create the function that will build the filtering statement. Use one of the two ideas I listed below. Make sure you run these when the user presses your search button or on the controls AfterUpdate event.
Private Sub txtSearch_AfterUpadate()
Call FilterSubformOption1
End Sub
Private Sub FilterSubformOption1()
Dim strFilter as String
Dim sSearch as String
sSearch = Replace(Nz(Me.txtSearch, ""), "'", "''")
If sSearch <> "" Then
If IsDate(sSearch) = False Then
strFilter = "(ProjectName LIKE '*" & sSearch & "*' OR "
strFilter = strFilter & "StructureName LIKE '*" & sSearch & "*' OR "
strFilter = strFilter & "CompanyName LIKE '*" & sSearch & "*')"
Else
strFilter = "ProjectDate = #" & CDate(sSearch) & "#"
End If
End If
If strFilter <> "" Then
Me.fsubProjectSearch.Form.Filter = strFilter
Me.fsubProjectSearch.Form.FilterOn = True
Else
Me.fsubProjectSearch.Form.Filter = ""
Me.fsubProjectSearch.Form.FilterOn = False
End If
End Sub
Private Sub FilterSubformOption2()
Dim strFilter as String
If Nz(Me.txtProjectName, "") <> "" Then
strFilter = "ProjectName LIKE '*" & Replace(Me.txtProjectName, "'", "''") & "*' AND "
End If
If Nz(Me.txtStructureName, "") <> "" Then
strFilter = strFilter & "StructureName LIKE '*" & Replace(Me.txtStructureName, "'", "''") & "*' AND "
End If
If Nz(Me.txtCompanyName, "") <> "" Then 'Search on multiple fields from one textbox
strFilter = strFilter & "CompanyName LIKE '*" & Replace(Me.txtCompanyName, "'", "''") & "*' AND "
End If
If IsNull(Me.txtProjectDate) = False Then
If IsDate(Me.txtProjectDate) = True Then
strFilter = strFilter & "ProjectDate = #" & Me.txtProjectDate & "#"
End If
End If
If Right(strFilter, 5) = " AND " THEN strFilter = Left(strFilter, Len(strFilter) - 5)
If strFilter <> "" Then
Me.fsubProjectSearch.Form.Filter = strFilter
Me.fsubProjectSearch.Form.FilterOn = True
Else
Me.fsubProjectSearch.Form.Filter = ""
Me.fsubProjectSearch.Form.FilterOn = False
End If
End Sub
Private Sub FilterSubformOption3()
Dim strFilter as String
If Nz(Me.txtProjectName, "") <> "" Then
strFilter = "ProjectName LIKE '*" & Replace(Me.txtProjectName, "'", "''") & "*' AND "
End If
If Nz(Me.txtStructureName, "") <> "" Then
strFilter = strFilter & "StructureName LIKE '*" & Replace(Me.txtStructureName, "'", "''") & "*' AND "
End If
If Nz(Me.txtName, "") <> "" Then 'Search on multiple fields from one textbox, the "hybrid" solution
strFilter = strFilter & "(CompanyName LIKE '*" & Replace(Me.txtName, "'", "''") & "*' OR "
strFilter = strFilter & "FirstName LIKE '*" & Replace(Me.txtName, "'", "''") & "*' OR "
strFilter = strFitler & "LastName LIKE '*" & Replace(Me.txtName, "'", "''") & "*') AND "
End If
If IsNull(Me.txtProjectDate) = False Then
If IsDate(Me.txtProjectDate) = True Then
strFilter = strFilter & "ProjectDate = #" & Me.txtProjectDate & "#"
End If
End If
If Right(strFilter, 5) = " AND " THEN strFilter = Left(strFilter, Len(strFilter) - 5)
If strFilter <> "" Then
Me.fsubProjectSearch.Form.Filter = strFilter
Me.fsubProjectSearch.Form.FilterOn = True
Else
Me.fsubProjectSearch.Form.Filter = ""
Me.fsubProjectSearch.Form.FilterOn = False
End If
End Sub
And that pretty much completes the multiple criteria filtering solution I typically use.
I create complex query-by-form interfaces for nearly all my Access apps. Why? Because my users want them! There are some screenshots of some older examples on my website, but none really is very clear. The point is that I'm writing SQL on-the-fly, and choosing whether to query particular tables based on whether there are criteria sought for that table.
I can say a lot more about, but it quickly shades into proprietary information. If these look like the direction you want to go, why don't you create a new question with enough information to suggest how it could be done based on these principles.

Access VBA - How would I have a loop in VBA that allows me to loop through control names

I have about 10 text boxes on a form that are actually used for display not entry. They are are named txt_001_Name, txt_002_Title, etc..what kind of loop is used for this.
What kind of VBA should I use to actually loop through the names of the text boxes? So if I was to debug.print it would look like:
txt_001_Title
txt_002_Title
txt_003_Title
This is probably pretty simple to do - all the more reason that I should learn how!
EDIT: Sorry, I should have been more descriptive about this.
Because of the above naming convention, I am looking to iterate through these text boxes so that I can perform something with each. What each of these 10 text boxes actually represent is numeric values, each having a SQL statement behind them in the form's onload event. I also have another set of ten that hold numeric values that are much more static, and finally another ten that use an expression to simply divide each of the first ten, against the relative "second" ten, and the value ends up in the relative 3. So basically it ends up looking like a dashboard table.
'first ten' 'second ten' 'resulting ten'
---------------------------------------------------
txt_001_value txt_001_calc txt_001_result
txt_002_value txt_002_calc txt_002_result
etc.
So I actually want to use this for the 'resulting' text boxes. I want to loop through the first ten and perform this easy calculation:
me.txt_001_result = me.txt_001_value / me.txt_001_calc
All the naming conventions "match up", so I can manually type out the 10 lines of the above for this, but I am sure there is a better way (loop through this), and I should probably learn it.
You can list the names of textbox controls with a simple procedure like this:
Public Sub TextBoxNames(ByRef pfrm As Form)
Dim ctl As Control
For Each ctl In pfrm.Controls
If ctl.ControlType = acTextBox Then
Debug.Print ctl.Name
End If
Next ctl
Set ctl = Nothing
End Sub
You could call it from the form's Load event:
Private Sub Form_Load()
TextBoxNames Me
End Sub
However, I don't understand what you're trying to accomplish. I realize you want to do something with ctl.Name other than Debug.Print, but I don't know what that is.
Rather than computing a result for me.txt_001_result and then assigning that value to the text box, consider setting the control source for txt_001_result to txt_001_value / txt_001_calc and let Access put the proper value into txt_001_result for you.
In response to your comments, I'll suggest this procedure as a starting point for you to build upon:
Public Sub MyTextBoxValues()
Const cintLastTextBoxNum As Integer = 10
Dim i As Integer
Dim strValueControl As String
Dim strCalcControl As String
Dim strResultControl As String
Dim strPrefix As String
For i = 1 To cintLastTextBoxNum
strPrefix = "txt_" & Format(i, "000")
'txt_001_value txt_001_calc txt_001_result '
strValueControl = strPrefix & "_value"
strCalcControl = strPrefix & "_calc"
strResultControl = strPrefix & "_result"
'me.txt_001_result = me.txt_001_value / me.txt_001_calc '
'Debug.Print strResultControl, strValueControl, strCalcControl '
Me.Controls(strResultControl) = Me.Controls(strValueControl) / _
Me.Controls(strCalcControl)
Next i
End Sub
I prefer to use a FOR EACH to iterate through the controls collection of whatever the textboxes are on (either the form itself or a panel control)
dim myBox as Textbox
For each myBox in myForm
myBox.Text = "hello"
Next
Also means you can make custom groups (by putting them all on the same container).
Note that if you have other controls, you might need a typecheck in there (IF TYPEOF(myBox) = "TextBox" THEN ...)
You could also do it like:
dim i as integer
For i = 1 to 10
myForm.Controls("txt_00" & i & "_Title").Text = "hello"
Next i
I definitely prefer the For Each, though.
I can't entirely understand why you need to do what you're doing, but I've had forms like that where I had an unbound form that I wanted to display an arbitrary number of fields, so I can see it. If you're walking the collection of controls only in the form's OnOpen event, that's fine. But if you're doing it in the OnCurrent of a bound form, or multiple times in an unbound form, you might consider a long post of mine on using custom collections to manage groups of controls.

Rebind Access combo box

I have an Access 2007 form that is searchable by a combobox. When I add a new record, I need to update the combobox to include the newly added item.
I assume that something needs to be done in AfterInsert event of the form but I can't figure out what.
How can I rebind the combobox after inserting so that the new item appears in the list?
The easiest way is to guarantee that the combobox is always up-to-date is to just requery the combobox once it gets the focus. Even if the recordset is then updated somewhere else, your combobox is always up-to-date. A simple TheCombobox.Requery in the OnFocus event should be enough.
There are two possible answers here that are efficient:
use the Form's AfterInsert event to Requery the combo box (as well as the OnDeleteConfirm event). This will be sufficient if the combo box does not display data that the user can update and that needs to be updated if the underlying record is updated.
if updates to the data need to be reflected in the combo box, then it would make sense to add a requery in the AfterUpdate events of the controls that are used to edit the data displayed in the combo box.
For example, if your combo box lists the names of the people in the table, you'll want to use method #2, and in the AfterUpdate event of Me!txtFirstName and Me!txtLastName, requery the combo box. Since you're doing the same operation in four places, you'll want to write a subroutine to do the requery. So, the sub would look something like this:
Private Sub RequerySearchCombo()
If Me.Dirty Then Me.Dirty = False
Me!MyCombo.Requery
End Sub
The reason to make sure you requery only when there is actually an update to the data displayed in the combo box is because if you're populating the combo box with the list of the whole table, the requery can take a very long time if you have 10s of 1,000s of records.
Another alternative that saves all the requeries would be to have a blank rowsource for the combo box, and populate it only after 1 or 2 characters have been typed, and filter the results that the combo displays based on the typed characters. For that, you'd use the combo box's OnChange event:
Private Sub MyCombo_Change()
Dim strSQL As String
If Len(Me!MyCombo.Text) = 2 Then
strSQL = "SELECT MyID, LastName & ', ' & FirstName FROM MyTable "
strSQL = strSQL & "WHERE LastName LIKE " & Chr(34) & Me!MyCombo.Text & Chr(34) & "*"
Me!MyCombo.Rowsource = strSQL
End If
End Sub
The code above assumes that you're searching for a person's name in a combo box that displays "LastName, FirstName".
There's another important caveat: if you're searching a form bound to a full table (or a SQL statement that returns all the records in the table) and using Bookmark navigation to locate the records, this method will not scale very well, as it requires pulling the entire index for the searched fields across the wire. In the case of my imaginary combo box above, you'd be using FindFirst to navigate to the record with the corresponding MyID value, so it's the index for MyID that would have to be pulled (though only as many index pages as necessary to satisfy the search would actually get pulled). This is not an issue for tables with a few thousand records, but beyond about 15-20K, it can be a network bottleneck.
In that case, instead of navigating via bookmark, you'd use your combo box to filter the result set down to the single record. This is, of course, extremely efficient, regardless of whether you're using a Jet back end or a server back end. It's highly desirable to start incorporating these kinds of efficiencies into your application as soon as possible. If you do so, it makes it much easier to upsize to a server back end, or makes it pretty painless if you should hit that tipping point with a mass of new data that makes the old method too inefficient to be user-friendly.
I assume your combobox is a control on a form, not a combobox control in a commandBar. This combobox has a property called rowsource, that can be either a value list (husband;wife;son;girl) or a SQL SELECT instruction (SELECT relationDescription FROM Table_relationType).
I assume also that your form recordset has something to do with your combobox recordset. What you'll have to do is, once your form recordset is properly updated (afterUpdate event I think), to reinitialise the rowsource property of the combobox control
if the recordsource is an SQL instruction:
myComboBoxControl.recordsource = _
"SELECT relationDescription FROM Table_relationType"
or if it is a value list
myComboBoxControl.recordsource = myComboBoxControl.recordsource & ";nephew"
But over all I find your request very strange. Do you have a reflexive (parent-child) relationship on your table?
I would normally use the NotInList event to add data to a combo with
Response = acDataErrAdded
To update the combo.
The Access 2007 Developers Reference has all the details, including sample code:
http://msdn.microsoft.com/en-us/library/bb214329.aspx
Requery the combo box in the form's after update event and the delete event. Your combo box will be up to date whenever the user makes changes to the recordset, whether it's a new record, and change, or a deletion.
Unless users must have everybody else's changes as soon as they're made, don't requery the combo box every time it gets the focus because not only will the user have to wait (which is noticable with large recordsets), it's unnecessary if the recordset hasn't changed. But if that's the case, the whole form needs to be requeried as soon as anybody else makes a change, not just the combo box. This would be a highly unusual scenario.
After update:
Private Sub Form_AfterUpdate()
On Error GoTo Proc_Err
Me.cboSearch.Requery
Exit Sub
Proc_Err:
MsgBox Err.Number & vbCrLf & vbCrLf & Err.Description
Err.Clear
End Sub
After delete:
Private Sub Form_Delete(Cancel As Integer)
On Error GoTo Proc_Err
Me.cboSearch.Requery
Exit Sub
Proc_Err:
MsgBox Err.Number & vbCrLf & vbCrLf & Err.Description
Err.Clear
End Sub