I'm building an Access database for one of my University's administrators to track faculty information. There's a section for tracking what faculty is on what committee. This is a many to many relationship, as faculty can be on more than on committee and committees obviously have more than one faculty. Here's a screenshot of my relationship with a Junction Table. I have a demographic table (faculty) with a committees table and a junction table in between them:
What I want to have is a Multi Select List Box populated with committees I can select to populate the Junction Table. This works if I have the List Box set to only have one selection. If I have it set to Multiple selections, I get an error that says "You must enter a value in the jctDemographicsCommitteess.committeeID" field.
Does anyone know how to get this to work?
The List box is in a subform with the Record Source being the junction table. Also, when I set the ListBox up, I set "Store that value in this field:" to CommitteeID.
Let me know if you need more information!
I think that using a set of combo boxes on a continuous subform is the way to go here.
Start by creating a form that is based on the junction table In its properties, change the "Default View" from "Single Form" to "Continuous Forms". Add a combo box to this form, and set the Control Source to be CommitteeID, the RowSource to be based on the Committee table with the CommitteeID being the bound column. Set the Column Count to be 2, and the column widths to be "0;6".
Go back to your form that is based on the Demographics table. On the toolbox there should be an option to insert a subform/subreport. Add it to the detail section of the Demographics form, and when the wizard starts guiding you through, select "Use an existing form", select the sub form that you have just created, and when asked which fields to use to link, select "DEM_ID" to "DemographicID".
You should end up with something like this:
I've added a "Remove" button next to the combo box that simply has the following code:
CurrentDb.Execute "DELETE * FROM Junction WHERE DemographicID=" & Me.DemographicID & " AND CommitteeID=" & Me.CommitteeID
Me.Requery
Regards,
I went and grabbed some code I wrote a long time ago to help. I cleaned it up a little bit but I left things for you to figrue out on your own. Im sorry Im lazy, but you got free code out of it. Its thoroughly documented with inline notes/comments.
Happy coding!
Private Sub SampleListBoxLoop()
On Error GoTo SampleListBoxLoopErr
Dim arr(0 To 4) As String ' you might need to vchange this too
Dim lCol As Long, lRow As Long
Dim vbsql As String
With Me.ListBox
'I wrote this in Access97. Not sure if listbox looping is still backwards or not - find out!
'Looping through multi column list boxes is transposed.
'Which is why we loop through columns first
For lCol = 0 To .ColumnCount + 1 'needs to be plus due to column header
If .Selected(lCol) Then
'You'll need to change the next line based on your scenario.
'Since I gave you free code, you'lll have to figure this one out for yourself :P
For lRow = 0 To 4 'this is to 4 due to Ubound of array and not row count since row count changes with each run
'Assign to the array transposed so that it is filled in normally.
arr(lRow) = Nz(.Column(lRow, lCol))
Next lRow
End If
Next lCol
'needed for deselecting
For lRow = 0 To .ListCount - 1
.Selected(lRow) = False
Next lRow
End With
If arr(0) <> "" Then 'Cannot run this if empty array
'Build your SQL statements herre
CurrentDb.Execute vbsql, dbFailOnError
End If
Me.Requery
Me.ListBox.Requery
Me.Subform.Requery
Exit_Sub:
Exit Sub
SampleListBoxLoopErr:
If DBEngine.Errors.Count > 1 Then
'ODBC Error
For Each errany In DBEngine.Errors
msgbox "ODBCExecute: Err# " & errany.Number & " raised by " _
& errany.Source & ": " & errany.Description, _
vbCritical, "cmdExecuteAttached()"
Next errany
Else 'Access Error
msgbox "ODBCExecute: Err# " & ERR.Number & " raised by " _
& ERR.Source & ": " & ERR.Description, _
vbCritical, "cmdExecuteAttached()"
End If
GoTo Exit_Sub
Resume
End Sub
Related
Five month ago, I got help with this problem:
Auto next number count to specific selection
I needed this auto count for daily reports at constuction sites.
Now my Problem is, the code
Dim NextNumber As Long
NextNumber = Nz(DMax("[raumBTBNR]", "[tbl_RaeumstellenErfassung]", "[raeumKostenstelleIDRef] = " & Me!KostenstelleAuswahl.Value & ""), 0) + 1
Me!Nummer = NextNumber
is placed at SelectingCostCentre_AfterUpdate ( Its working correctly )
The consequence:
If an employee wants to write the daily report:
He opens the program, selects the matching construction site he is at and the daily report number counts +1. So far so good...
If he is now leaving the program, or is clicking at another form without
adding employees at the activity form, Access saved the record in spite of this.
When he now wants to enter the record again, he has to type in his construction site again, but the auto report number isn't correct anymore, because he counts +1 again. This relates to an DoCmd.GoToRecord , , acNewRec at Form_Open
Form looks like this
What i want access to do is:
When opening the form, check if the last record (of the specific constr. site) at table
"Constructionsite entry" has entries at the table "activity log"
If True
DoCmd.GoToRecord , , acNewRec
Else
Open Last Record of the matching constuction site
EDIT:
or something like:
If no data entries at table "activity log" available
Recordset current delete
Apparently my VBA skills are very basic .. if any.
I looked at function DLookup, but don't know how to handle syntax with this type of problem.
EDIT2
Got a first step of a Solution:
If DCount("*", "tbl_RaeumstellEnerfassung", _
"RaeumKostenstelleIDRef=" & Me!KostenstelleAuswahl & " " & _
"AND raeumDatum =" & Format(Me!raeumDatum, _
"\#yyyy-mm-dd\#")) > 1 Then
If MsgBox("Die Räumstelle " & Me![KostenstelleAuswahl].Column(1) & " ist am " & Me![raeumDatum] & " schon eingetragen worden!" & vbCrLf & "Trotzdem fortfahren?", vbQuestion + vbYesNo) = vbNo Then
DoCmd.GoToRecord , , acLast
DoCmd.SetWarnings False
DoCmd.RunCommand acCmdDeleteRecord
DoCmd.SetWarnings True
Else
Cancel = False
End If
End If
He checks if a constructionsitenumber and a date is already entered.
What I would like to do is having a third MsgBox button as Dlookup or DLast, which shows me the last data related to this construction site.
DLookup("[raeumID]", "tbl_RaeumstellenErfassung", "[KostenstellenIDRef] ="Forms![frm_RaeumstellenErfassung]![KostenstellenAuswahl]")
apperently this isnt working .. more precisly I dont get this to work with a 3d button
I have a form control (address) that uses Dlookup to call info from a table "database" but the form is bound to table "Tracker". The dlookup is based on another control on the same form - "Name" I need the form to record this dlookup control, along with other controls that are bound to "tracker" as new recordto the table "tracker."
My failed attempts:
Using the default value property to assign the recalled data from the dlookup to another text box which would be bound to "tracker" This simply does not work for some reason. Perhaps I am missing something that tells this control "Address" to update upon selecting the correct "name?"
Code:
Private Sub SubmitReferral_Click()
On Error GoTo Err_SubmitReferral_Click
DoCmd.GoToRecord , , acNewRec
Exit_SubmitReferral_Click:
Exit Sub
Err_SubmitReferral_Click:
MsgBox Err.Description
Resume Exit_SubmitReferral_Click
End Sub
I also tried this - to assign the data - but the data from the dlookup in control "Address1" is not transferring/copying to control "Address2"
Private Sub Combo276_OnUpdate()
OnUpdate ([Address2].Value = [Address1].Value)
End Sub
Help or suggestions?
PS - I have tried to Edit per request to be as specific as possible, and to follow proper board etiquette.
Still unsure of your field names, etc., but the following is an example you can modify. Change 'tblEmployee' to 'database'.
I must state that if you are just starting out with developing in Access (or VBA) that you should never use names that are reserved words, or that can be misleading. Your table named 'database' is ok if named 'tblDatabase'.
Option Compare Database
option Explicit
Private Sub cmdInsert_Click()
Dim strSQL As String
Dim i As Integer
Debug.Print "cmdInsert; "
i = MsgBox("Do you want to add 1 row for Employee ID: " & Me.EmpID & " to table 'tracker'?", vbYesNo, "Confirm Add")
If i = vbNo Then
Exit Sub
End If
DoCmd.SetWarnings True
strSQL = "INSERT INTO tracker ( FirstName, LastName, Add1, City, St, Zip ) " & _
"SELECT tblEmployee.FirstName, tblEmployee.LastName, tblEmployee.Add1, tblEmployee.City, tblEmployee.St, tblEmployee.Zip " & _
"FROM tblEmployee " & _
"WHERE (((tblEmployee.EmpID)=" & Me.EmpID & "));"
DoCmd.RunSQL strSQL
End Sub
Thanks for the help - I solved my concern by hiding the fields that contain the dlookup, and putting code behind a button that copies the information to fields that are bound and therefore will record to the table "tracker"
I am creating a user interface based off of one internal and two linked (external) Access datasheets in Access 2013.
Two of the fields on my UI are combo boxes that read from the linked datasheets and display the options. This is so that the entries for suppliers and material types are called-out consistently and typos are avoided. However, I would like to add the following functionality:
-If a new value is entered into the combo box the user will be prompted to fill out the necessary information on the new value. This information will subsequently be saved to the appropriate linked datasheet.
How would I go about setting up the prompt from the combo boxes themselves? It would require Access to open a form or sub-form that will, in turn, save to the linked datasheet.
I'd prefer it to be automatic, instead of end-user prompted so that it isn't skipped. It's been years since I played around with VB, so I would like to avoid that if possible and use Access' built-in functions (even if it requires a little more time). Thank you in advance!
Alright, so I was able to do it after researching the "OnNotInList" function and a little VB code.
In the OnNotInList section of the 'Event' properties sheet, I chose 'Code Builder' and entered the following:
Private Sub Supplier_NotInList(NewData As String, Response As Integer)
Dim ctl As Control
Dim dbsCustomerDatabase As Database
On Error GoTo Supplier_NotInList_Err
Dim intAnswer As Integer
Dim strSQL As String
intAnswer = MsgBox("The supplier " & Chr(34) & NewData & _
Chr(34) & " is not currently listed." & vbCrLf & _
"Would you like to add it to the list now?" _
, vbQuestion + vbYesNo, "Spire Manufacturing Solutions")
' Adding the new entry to the list:
If intAnswer = vbYes Then
strSQL = "INSERT INTO CustomerList([CustomerName]) " & _
"VALUES ('" & NewData & "');"
DoCmd.SetWarnings False
DoCmd.RunSQL strSQL
DoCmd.SetWarnings True
MsgBox "The new supplier has been added to the list." _
, vbInformation, "Spire Manufacturing Solutions"
Response = acDataErrAdded
' Opening the Supplier datasheet to add details
' to the new entry:
MsgBox "Opening Supplier database for new entry..."
DoCmd.OpenTable "CustomerList", acViewNormal, acEdit
End If
Supplier_NotInList_Exit:
Exit Sub
Supplier_NotInList_Err:
MsgBox Err.Description, vbCritical, "Error"
Resume Supplier_NotInList_Exit
End Sub
This allowed me to automatically prompt the user to add the details for a new supplier if they enter a new supplier name. Or, cancel the entry if they simply misspelled it. I'd quite forgotten how versatile VB was. Thank you all for your assistance in getting me headed in the right direction!
I'm moving on from multi-value fields due to my conversion to SQL Server for the back end. Unfortunately, I can't figure out how to replace it.
What I've done is created a many to many relationship between my "Opportunities" and "Purpose" tables utilizing a join table (one to many on each). This part was easy.
What I don't understand is how to then create a dropdown listbox (with check boxes to select the options) for the purpose.
I've found resources online pointing to the idea that I will need to use VBA, but have yet to find any actual examples. Is anyone familiar with how to do this?
Thanks in advance.
I had to do this for exactly the same reason; moving to a SQL Server back-end. My solution keeps the checkbox functionality but doesn't have them in a drop-down. You can put the check-boxes in a sub-form (that looks like a drop-down?) if you don't want a bunch of boxes on your main form.
For each checkbox you want to test for whether a record in the related table exists and add it if it doesn't when the box is checked, and the reverse when it's unchecked. The check-boxes must be unbound for this to work.
Here's the add/remove code. It goes in the "After Update" event:
Private Sub cb_1_AfterUpdate()
If Me.cb_1 = True Then
If DCount("Parent_ID", "Checkbox_records", "Parent_ID = " & Me.P_ID & "and Checked_box_num = 1") = 0 Then
strSQL = "INSERT INTO Checkbox_records (Parent_ID, Checked_box_num) VALUES (" & Me.P_ID & "," & "1)"
CurrentDb.Execute strSQL, dbFailOnError
End If
End If
If Me.cb_1 = False Then
If DCount("Parent_ID", "Checkbox_records", "Parent_ID = " & Me.P_ID & "and Checked_box_num = 1") > 0 Then
strSQL = "DELETE FROM Checkbox_records WHERE Parent_ID = " & Me.P_ID & " and Checked_box_num = 1"
CurrentDb.Execute strSQL, dbFailOnError
End If
End If
End Sub
The problem with unbound boxes is that they don't change when you switch records. So you have to set the boxes to reflect the data state when you change records, which you can do in the form's "On Current" event:
Private Sub Form_Current()
If DCount("Parent_ID", "Checkbox_records", "Parent_ID = " & Me.P_ID & "and Checked_box_num = 1") > 0 Then
Me.cb_1 = True
Else
Me.cb_1 = False
End If
End Sub
The thing I don't like about this solution is that you have to duplicate the code for each box, but it runs smoothly so it's livable.
I have a table in Access..
AccessKey
AccessCardID
Distributed (yes/no)
On my form is a combo box with 10 Access Cards. I need help setting up a query where if guest A gets Access Card #1, the user will only be able to choose from Access Cards #2-#10, for guest B, c, and so on, until guest A returns Access Card #1.
so far the the query i have is
SELECT AccessCardID
FROM AccessKey
WHERE Distributed = False;
Here is my new code for After update
Private Sub AccessKeyNo_AfterUpdate()
If MsgBox("Do you want to assign Access Key" & Me.AccessKeyNo & "?", _
vbYesNo) = vbYes Then
Me.GuestAccessKeyID = Me.AccessKeyNo
Me.MyCheckBox = Not IsNull(Me.GuestAccessKeyID)
Me.AccessKeyNo.Requery
End If
End Sub
My new query
SELECT AccessKey.AccessKeyID
FROM AccessKey LEFT JOIN Guest ON AccessKey.AccessKeyID=Guest.GuestAccessKeyID
WHERE (((Guest.GuestAccessKeyID) Is Null));
And the On current for the form
Private Sub Form_Current()
Me.MyCheckBox = Not IsNull(Me.GuestAccessKeyID)
If IsNull(Me![GuestID]) Then
DoCmd.GoToControl "GuestFirstName"
End If
End Sub
Why don't you set Distributed to False in the above query so that you get the list of cards which have not been distributed yet.
If you do not like writing code, you can use a subform and set the record source to your table, AccessKey. Alternatively you can write a little code, say:
Private Sub NameOfComboHere_AfterUpdate()
Dim db As Database
Dim strSQL As String
Set db = CurrentDb
If MsgBox("Do you want to assign " & Me.NameOfComboHere & "?", _
vbYesNo) = vbYes Then
strSQL = "UPDATE AccessKey SET Distributed = True " _
& "WHERE AccessCardID = " & Me.NameOfComboHere
db.Execute strSQL, dbFailOnError
If db.RecordsAffected = 1 Then
MsgBox Me.NameOfComboHere & " has been assigned."
End If
End If
Me.NameOfComboHere.Requery
End Sub
There are a few notes. This assumes that the combobox has a bound column of AccessCardID and that AccessCardID is numeric. It also assumes that you have a reference to Microsoft DAO 3.x Object library. A subform may be the best bet.
EDIT based on Comments
Let us say you have a card id in the guest table, first, add a little code to the Current event for the form:
Me.MyCheckBox = Not IsNull(Me.GuestAccessCard)
This will set the checkbox to false if there is no card id and to true if there is an id.
You will need to change the query for the combobox to:
SELECT AccessCardID
FROM AccessKey
LEFT JOIN Guests
ON AccessKey.AccessCardID = Guest.GuestAccessCard
WHERE Guest.GuestAccessCard Is Null
Then the After Update event would run:
Private Sub NameOfComboHere_AfterUpdate()
If MsgBox("Do you want to assign " & Me.NameOfComboHere & "?", _
vbYesNo) = vbYes Then
Me.GuestAccessCard = Me.NameOfComboHere
Me.MyCheckBox = Not IsNull(Me.GuestAccessCard)
''The data will have to be saved for the
''combo to update with the new data
If Me.Dirty Then Me.Dirty = False
Me.NameOfComboHere.Requery
End If
End Sub
And do not forget to set the GuestAccessCard to Null when the checkbox is unticked and requery the combo.
I may be completely misunderstanding the situation here, but it seems to me that you don't want to have a Boolean field in the table with your cards to indicate if it's checked out -- all you need is a field in your Guests table where you enter the card number. Then you can tell which cards are available with a left join against the card numbers that are in use:
SELECT Cards.CardNumber
FROM Cards LEFT JOIN Guests ON Cards.CardNumber = Guests.CardNumber
WHERE Guests.CardNumber Is Null
This would mean that there's only ever one place where the information is stored (in the Guests table). You could also put a unique index on the CardNumber field in the Guests table (allow Nulls) so you could never assign the same card to two guests.
This may be the way you are doing things, or the way Remou has suggested, but I got too confused by all the convoluted back and forth!