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
Related
I have a rather simple looking problem but it turned out to be more complicated than I thought.
I have a field (column) in my subForm which is a ComboBox.
I have a field (column) in another subForm by which I would like to filter this comboBox.
Basically, the comboBox before filtering has some 600 records, too many to scroll by casual user. I created a simple subForm whose field is linked to a mainForm and this works perfectly (ie. the selected record-field-ID is displayed on mainForm).
Now what I want is that this comboBox is filtered by this record (ie. only showing relevant fields). The problem is that if I simply Requery it with this given filter, the other fields show up blank.
I want this filter to apply only to NEW RECORDS comboBox, not the whole datasheet view.
What I did is:
Private Sub Sekacie_Operacie_GotFocus()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Sekacie_Operacie.RowSource = Replace(SQL, ";", "") & " WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
Me.Sekacie_Operacie.Requery
'works kinda as intended
End Sub
Private Sub Form_AfterInsert()
Me.Sekacie_Operacie.RowSource = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Refresh
End Sub
And when I select the record in my filter subForm:
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
However, this workaround still shows "blank" records sometimes (I have to refresh the form by clicking different record) and I find it strange I had to go all the way to do this. Is there no simpler way of accomplishing this?
A couple of immediate things:
No need to refresh the whole sub form, just refresh the control.
Private Sub Form_AfterInsert()
' Me.Refresh ' replace this line with the following line
Me.Sekacie_Operacie.Refresh ' or is it Requery I can't remember
End Sub
The following code can be improved
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
' by using code like this
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
me.Sekacie_Operacie.RowSource = SQL
' I think the above line will cause the combo to refresh
' anyway (in some version of accees it might not,
' but, if needed add the following line
' me.Sekacie_Operacie.refresh
End Sub
The following line has the potential to produce and error
(perhaps when the main form is a new record)
WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
replace it with
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
Note that adding such code will stop the subform from working independently of the main form and will require it to have a parent form.
You could get around this by using:
if not me.parent is nothing then
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
end if
About your approach.
I find it a bit weird what you are doing. Consider this:
Say the query qrySekacie_Operacie returns 100 rows when run without any WHERE clause. When you are inserting a record into the subform you want to limit the combo by adding a WHERE clause that uses a value in the parent formMe.Parent.Form!txtOP_ID) , this might limit the number of rows displayed to 10.
It is worth noting that if any of the other rows in the subform contain value other than one of these 10 the combo will display a blank value. I think this is what you are experiencing.
My first question was why not use the on current event of the parent form to change the subform combo box SQL. It appears this is not what you need.
It appears that other values can exist in the dataset of the subform and that you only want certain value to be enterable in this subform but you want the subform to display any values that have been entered.
So that gets interesting. Here's what I would do:
Create a hidden combo control (with no control source) that is only visible when a new record is being entered. When this new combo is shown the other one is hidden.
Set up this new combo with the required SQL
use a before insert event to copy the value form the new combo to he old one.
Easy peezy?
Let me know ho you get on.
One other thing
I like to Ensure a subform encapsultes code that operates on it "private" controls.
As I general rule of thumb, where possible (which is 99% of the time), I like to try and keep subforms so that they will work as forms in their own right. They can be though of as an object, so if they work independently, it is an indication that their code has been encapsulated within their own code module.
So if the form has me.form.parent = nothing then the code behaves differently as it is a independent form.
So rather than the parent form code module having code that sets the SQL of the subforms combo, I would write a public sub in the subforms module (called SetComboSQL) that can be called (with the appropriate parameters. ie OPid ) to set the combo SQL.
To call the procedure in the parent form I would add
Private Sub Form_Current()
Me.MySubformControlName.Form.SetComboSQL(OPid:=Me.txtOP_ID)
End Sub
' note that intellisense won't pick up this proeprty BUT it will work. Use debugger to check it out.
I hope you find this tip useful (as it can be a massive headache saver. THE MOST BASIC OO PRINCIPAL is encapsulation. An object should own all the code that effects it's properties (and controls).
I have a quick question. I've developed a booking system that has a sub form containing all the rooms booked on the date displayed (in main form). I've added a combo box to allow users to filter the rooms to only see the ones they select from combo box (re-query the sub-form and then filter).
This works fine apart from the fact my code would sometimes loop through the filtering sub for no apparent reason after reaching the end of the sub?. It displays everything correctly, so this I'm not bothered about (unless it's connected to the real problem).
The real problem I'm having however is when I choose a room that has no bookings for the date displayed. The filter works fine (empty display), but when I then try to choose another room from the combo box the re-query function behind the after update of the combo box does not work!
Has anyone else experienced this before?
The workflow:
Combo box triggers 'after update' event. This re-queries the sub-form, where behind the 'on current' event the filtering of the sub-form happens.
When sub-form is empty I'm unable to perform any further sub-form re-queries.
I do something similar and had a hard time with this but was able to fix it with the following code in my combo_box_afterupdate event.
Dim rs As Object
Me.Form.Filter = ""
Me.Form.Refresh
Set rs = Me.Recordset.Clone
rs.FindFirst "[ValueToFind] = '" & Me![MyComboBoxValue] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
Also be sure to link parent and child as last person mentioned.
I have a particularly difficult problem to deal with.
I am working on a Form for access. This form contains a large amount of cascade data connections.
It serves as a filtering, selection, review, and editing interface for the database.
Below I have included a basic description of the design;
-ComboBox1 populates its data from a reference table, and cascade updates to ComboBox2.
-ComboBox2 populates based on selection of ComboBox1 and then cascades information to ListBox1 which runs a query to generate a list of records.
-Selecting a record in Listbox1 populates the various hidden columns of the list into matching text and comboBox fields for display/Editing. It also on click cascade Populates Listbox2 using a query to pull records from a different table that are related to the selected record from ListBox1.
-I have a number of buttons to handle adding, deleting, and editing the records of the table where ListBox1 Queries its information from.
Everything up to this point works correctly (thanks in part to aid from people answering my other questions)
Where I am stuck now is in trying to create similar buttons that serve the same function with the Table where ListBox2 drawns its records(Add/Edit/Delete)
I have added the Add button successfully already but have become stuck on the edit function.
I have a button that I am trying to build an "OnClick" event for. It needs to pull the selected record from a listbox and have the form it is opening be set to that specific record.
I know I can pull the selected value from the list box with listbox.selected.value but How does that translate into the DoCmd.OpenForm syntax?
The DoCmd.OpenForm parameters you want to adjust are the optional WhereCondition and DataMode. WhereCondition accepts a SQL Where clause without the word where. This restriction is applied to the form's rowsource before the form opens.
DataMode can override the form's saved settings for AllowEdits and DataEntry. It sounds like you want to use acFormEdit in this case.
Private Sub CommandFoo_OnClick()
Dim strWhere As String
'Do yourself a favor and build the WhereCondition in a string variable so that you can see what you've built.
'Surround text values in double quotes.
'Surround dates with # and specify in m/d/yyyy or yyyy-mm-dd
'Make sure you build in spaces in as necessary.
strWhere = "NumberField1=" & ListBox1.Columns(0) & " AND TextField2=" & Chr(34) & ListBox1.Columns(1) & Chr(34)
Debug.Print strWhere
DoCmd.OpenForm FormName:="YourFormName", WhereCondition:=strWhere, DataMode=acFormEdit
End Sub
I have a subform that is part of a larger form in which both contain a PROJECT_ID field. In the main form, the PROJECT_ID field is a key. In the subform, users have the option of a assigning a new representative to the project or making changes to the current project representatives in the subform. A project can have multiple representatives, however only one can be an active primary. My issue is I’m having trouble writing the validation for the primary flag field (ADV_FLAG) because it’s essentially based on a query. I’m a bit of a novice when it comes to VBA, but I think it’s probably the best solution for my issue. Any suggestions or samples of similar code on how to go about solving this issue. Below is currently what I have in the sub_form’s BeforeUpdate Event Procedure.
Private Sub Form_BeforeUpdate(Cancel As Integer)
'Validation for more than one Active Primary on Project
‘Append to the table T_Error_Catch the project_ID and an error_flag of YES
'where a project has more than one active primary.
DoCmd.SetWarnings False
DoCmd.OpenQuery "Q_Append_Errors_MultiplePrimaries", acViewNormal, acEdit
‘Throw an dialog box error to user to indicate this project already has one active primary
If ERR_FLAG = "Yes" And ADV_FLAG.Value <> "Secondary" Then
MsgBox "Project already has an Active Primary.", vbExclamation
ADV_FLAG.SetFocus
Cancel = True
End If
‘Truncate the T_Error_Catch table once the record has been corrected and there is again only one active primary
If ERR_FLAG = "Yes" And ADV_FLAG.Value = "Secondary" Then
DoCmd.SetWarnings False
DoCmd.OpenQuery "Clear T_Error_Catch", acViewNormal, acEdit
End If
End Sub
You're using form before update to validate ADV_FLAG values in existing records. I think you need to also address new records ... to prevent the user from adding a new representative as primary for a PROJECT_ID which already has a primary representative assigned. You could add a procedure for form before insert to deal with that.
However, rather than waiting until the user has completed all the fields before validating ADV_FLAG, do the validating in the after update event of the ADV_FLAG control. The after update procedure would handle both record update and insert.
But perhaps even easier to implement would be a command button on the subform which makes the current representative the sole primary for the current PROJECT_ID. The button's click event could use code such as this:
Dim strSql As String
Dim db As DAO.Database
On Error GoTo ErrorHandler
strSql = "UPDATE YourTable SET ADV_FLAG = 'Secondary' WHERE PROJECT_ID = " & _
Me.txtPROJECT_ID & ";"
Debug.Print strSql
Set db = CurrentDb
db.Execute strSql, dbFailOnError
Me.txtADV_FLAG = "Primary"
ExitHere:
On Error GoTo 0
Debug.Print "RecordsAffected: " & db.RecordsAffected
Set db = Nothing
Exit Sub
ErrorHandler:
'your error handler code here '
I assumed your subform has a text box control named txtPROJECT_ID which is bound to the PROJECT_ID field, and another named txtADV_FLG bound to the ADV_FLAG field. Change those names as required to match your data controls.
With this approach, there's not really a need for the user to directly edit values in txtADV_FLG (since the command button will make any changes needed). So in the property sheet for txtADV_FLG you can set Enabled = No, and optionally Locked = Yes.
The Debug.Print lines are to help you troubleshoot problems. They will print information to the Immediate Window. (You can go to the Immediate Window with the Ctrl+g keyboard shortcut.) After you have the code running correctly, you could disable or remove the Debug.Print statements. Or leave them as is ... you won't suffer any significant performance hit.
Notice I used db.Execute strSql, dbFailOnError instead of DoCmd.SetWarnings False and DoCmd.OpenQuery. I never SetWarnings off. If you do it, you must remember to SetWarnings back on again afterward. Your code didn't include DoCmd.SetWarnings True. So without SetWarnings on, you risk suppressing important information. Don't do that!
My eyes glazed over trying to follow #HansUp's solution, which I'm sure is correct. I'll instead offer an answer that uses the schema design to obviate any need to write much in the way of code.
I've had to do this many times -- you have a N:1 table but you want one of the records to be designated as PRIMARY.
First, you set up your N:1 table.
Then you add a field to the main table (the 1 side) and have that store the PK value of the record in the N table that you want as your main record.
For instance, say you have tblInventory and tblImage, which has an ImageID PK and an InventoryID FK.
To set one of those as the main image, you'd add a MainImageID field to tblInventory, and edit it with a combo box that lists the images from tblImage that are joined to that InventoryID in tblImage. You'd have to requery the combo box in the Inventory form's OnCurrent event, of course.
An example UI is here:
In that implementation, the list of images has a checkbox for TOP, but it's not editable (and that's a bad UI, since it's not clear from looking that it can't be changed there), but users figure it out fairly quickly. It's certainly not necessary that the check be displayed there, or that the same control be used to indicate the TOP item.
I'm pretty new to MS Access. I'm trying to create a simple form that will basically search for a particular record using a textbox, rather than a drop down box. Essentially a user would be able to enter an ID number and retrieve some other related Info. However, I do not want the user to be able to add any new records to the database. I've been able to get the forms to look the way I want them, but I'm not sure where to place the code (do I create a macro, insert the code into the properties of the button?) Any help is greatly appreciated!
I assume that you have bound your form to a table or a query and that you want to be able to enter the ID manually in a textbox, then press ENTER and load that record's data or display an error message if there is no such record.
As dsteele said, make sure that the form's Data property Allow Addtions is set to No to disallow users from adding records.
Then, from the AfterUpdate event of the textbox, add the following code (assuming that your textbox is named txtGoTo):
Private Sub txtGoTo_AfterUpdate()
If (txtGoTo & vbNullString) = vbNullString Then Exit Sub
Dim rs As DAO.RecordSet
Set rs = Me.RecordsetClone
rs.FindFirst "[ID]=" & txtGoTo
If rs.NoMatch Then
MsgBox "Sorry, no such record '" & txtGoTo & "' was found.", _
vbOKOnly + vbInformation
Else
Me.RecordSet.Bookmark = rs.Bookmark
End If
rs.Close
txtGoTo = Null
End Sub
Note that you will have to change the line rs.FindFirst "[ID]=" & txtGoTo to something that is adequate for your data:
"[ID]=" should be replaced by the field you want to search (it could be "[POReference]=" or something else.
if you are searching by a numeric ID, for instance because the field is an autonumber column, then the code is fine.
Otherwise, if the field you are searching on is a string (say PN12-G) then you have to change the code to:
rs.FindFirst "[ID]=""" & txtGoTo & """"
Failing to use the proper quoting (or quoting where not necessary) will result in errors of the kind Data type mismatch....
As a new user, I would recommend that you have a look at the sample NorthWind project database that is either shiped with older versions of Access or available as a template for download from Access 2007.
There a lots of techniques to learn from as a new Access developer, including other ways to implement record navigation.
Set the form property Data/'Allow Additions' to No.
Either in the AfterUpdate event of the textbox, or in the Click event of a button, you can write code or assign a macro to look up and display the record you want.