How to launch a Look up form, and return the value from a combo box - vba

Hopefully I can explain what I want to do well enough...here it goes...
I have a data entry form...the user will be entering employeeIDs. Once in normal operation, most people will be entering only their own EmpID, and they should know it, so this won't be a big problem 99% of the time once this DB goes live.
However, I need some temps to enter historical data from paper sheets into the DB. These people will not know anyone else's EmpID. I'd like to set the Student field's OnDblClick event in the subform's datasheet to open a small form with a combo box. The combo box has a list of all Employee Names, and is bound to the EmpID. Once this user enters the name, and selects the person, I have a button they can click to return to the datasheet.
I can use a function to launch the form, no problem there. But how do I return the EmpID to the field in the datasheet that was double clicked?
When user double clicks in the Student field...I want the next form to appear, and then once they type in the name and select the correct person...and then click Found Them!...I need that bound value to return.
I'd love to say I have code to share right now...but the only code I have is to launch the look up form. I'm brain farting on how to pull the value back down.

The way to do this to launch your little dialog form as “acDialog”. This will cause the calling code to WAIT.
And then the “magic” part is when they click on “Found Them” you do NOT close the popup form, but simply set the form’s visible = false. This has the effect of the calling code that popped up this form that halted to continue (the form is kicked out of dialog mode when you do this). So now your calling code continues.
So your code will look like this:
Dim strF As String ' name of popup form
strF = "frmPopUp"
' open form, wait for user selection
DoCmd.OpenForm strF, , , , , acDialog
' if for is NOT open, then assume user hit cancel buttion
' (you should likly have a cancel button on the form - that cancel buttion will
' execute a docmd.close
If CurrentProject.AllForms(strF).IsLoaded = True Then
' grab the value of thee combbo box
strComboBoxValue = Forms(strF)!NameOfComboBox
DoCmd.Close acForm, strF
End If
As noted, the code behind the Found Them button DOES NOT do a close form, but sets forms visible = false (me.Visible = false), and this trick allows the calling code to continue at which point you can examine any value on the form. Remember to then close the form after you grab the value.

It looks like your data table is in a subform so there is a little more work but it does not have to be as complex as the above solution if you don't want it to be. #Andre451 was close but you need the extra step of identifying the form and subform. For the purpose of demonstration let's call the form Attendance and subform Entry then I'll call the second form LookUp. So the code for your double click in the subform field will of course look something like this :
Private Sub Student_DblClick(Cancel As Integer)
DoCmd.OpenForm "LookUp"
End Sub
You really don't need anything else fancy there. For the button on "LookUp" you will put this:
Private Sub Command2_Click()
Forms![Attendance]![Entry].Form![Student] = Forms!Lookup!Student
DoCmd.Close acForm, "LookUp"
End Sub
And that should get you what you want without any overhead or having to leave any ghosts open.

Related

Button to check for missing values in a MainForm and Subforms in MS Access

New to Access (still), have only basic VBA skills.
I've got 3 subforms (subfrm_PackingSteps1 , subfrm_MetalDetection and subfrm_Weights - the first 2 are continuous and the other one is single form) within a main form (frm_daily_packing_record) that users go through and input data. The user should be able to input data in no particular order, and only at the end there would be a button to confirm that the user is ready to save this form.
I'd like to have this button on the main form that checks each control (in main form and subforms) for empty values. I found and adjusted a code to check the recordset of one of the continuous forms (see below), but I can't figure out:
how to include a code that checks each control instead of manually adding all of them (I've used a function before that utilises the Tag property, but can't add it to this)
how to keep the button in the main form while checking the controls/recordsets in the other subforms.
Thanks in advance.
Private Sub ConfirmBtn_Click()
Dim blnSuccess As Boolean
blnSuccess = True
Me.Recordset.MoveFirst
Do While Not Me.Recordset.EOF
If IsNull(Me.pc) Or IsNull(Me.InnerP) Then
blnSuccess = False
Exit Do
End If
Me.Recordset.MoveNext
Loop
If blnSuccess = True Then
MsgBox "You may proceed to save this record"
Else
MsgBox "You still have some empty fields to fill in!", vbCritical + vbOKOnly, "Empty Fields!"
End If
End Sub
I personally don't like this because it is too code-heavy and can easily break when the form is modified.
Instead may I suggest doing it slightly different, for each field have vba code .ondirty or .onupdate and do the validation checking right as the user is actually on that field.
This has 2 benefits, it is creating the validation when you are creating each form field and it STOPS the user right when their first mistake or bad data is entered. The last thing I want is to enter 50 fields, scroll to the bottom, submit fails then scroll back and try to find where the mistake was. If validation is done while the user it doing the actual data entry, when you get to the bottom you should have valid data and the submit should succeed without further testing.
Less code to debug and timely messages to the user if an error is caught!

MS Access one form to select table values and use to populate multiple fields on other form?

I have a table called GL_Account and IM_Productline
The table IM_Productline has various fields that need to be populated with a value from the field GL_Account.AccountKey (i.e. IM_ProductLine.InventoryAcctkey and IM_ProductLine.CostOfGoodsSoldAcctKey)
To populate the IM_ProductLine table I made a form "Product Line Maintenance" with all the fields. To populate the field IM_ProductLine.InventoryAcctkey I put a (magnifying glass) button behind the field with the following code:
Private Sub CMD_Select_GL_Account_Click()
Me.Refresh
If IsNull(Select_ProductType) Then
'do nothing
Else
Forms![Product Line Maintenance].InventoryAcctkey = Me.SelectGLAccountKey.Column(0)
Forms![Product Line Maintenance].Refresh
End If
DoCmd.Close
End Sub
So the button opens a form Called "Select GL Account" with a combo box that enable to SELECT GL_Account.AccountKey, GL_Account.Account, GL_Account.AccountDesc
FROM GL_Account; and when the OK button is clicked it writes the value from GL_Account.AccountKey to IM_ProductLine.InventoryAcctkey, closes the form "Select GL Account" and then refreshes the form "Product Line Maintenance" so the account number and description become visible for the user.
This all work fine but here's my question:
Now rather than creating a new form for every account field I need to populate (i.e. "Select Inventory GL Account" select "Cost Of Goods Sold GL Account" etc) I'd prefer to use the form "Select GL Account" to select and populate the 11 different account fields. So behind each xxxAcctkeyfield on form "Product Line Maintenance" is a (magnifying glass) button that when clicked pulls up the form "Select GL Account" and when "OK" is clicked it writes the selected AccountKey to the correct field on form "Product Line Maintenance"?
I'd greatly appreciate anyone's efforts to understand what I am trying to explain and point me in the right direction.
Ok, there is the issue that all 11 fields should not require to be "copied" since you have a relational database (you would ONLY store the row PK ID of that selection in the current report. (a so called FK (foreign key) value). That way, say you want to change the choice? Well then you could pop up that form - search + select the one record with all that information, and then upon return ONLY store the one value.
So, I would give some thoughts to the above - you want to leverage the relational database features. And as a result, you don't need to "copy" all that data. This is not much different then say creating a invoice. I can create the new invoice, but all of the address information, and the customer that this ONE invoice belongs to? Well, that is one column with a FK value that points to the customer. Once I select that one customer, then display of the customer name + address can be say a sub form or some such - but no need exists to "copy" that information. It would also means with near zero code, you could move a invoice between customers!!! - (just change the one fk column with to the new/different customer ID (PK) value.
Now, back to the question at a hand?
You can certainly pop up a form, let the user select, enter, pick and do whatever. And THEN you can have the calling code grab + pick out the values from that form.
The way you do this? It involves a not too wide known trick.
The code that calls the form can simply open that form as a dialog form. This will HALT the calling code, the user does whatever, and when done the calling code will THEN continue. Not only does the calling code continue, but it can get/grab/pull/take any values from that pop up form WIHOUT having to use global vars.
The approach is thus thus:
dim strF as string
strF = "frmPopAskInfo"
docmd.OpenForm strF,,,,,,acDialog
' above code waits for user input
if application.AllForms(strF).IsLoaded = true then
' user did not cancel, get values from form
me!AccountNo = forms(strf)!AccountNumber
etc. etc. etc.
docmd.Close acForm,strF
end if
Now the only other issue? Well, the "ok" button on the popup for DOES NOT close the form, what it does is set visible = False. This will kick the form out of dialog mode.
me.Visible = False
So, if the user hits the cancel buttton (close form) or hits the X upprer right (close form), then the form will NOT be loaded when your calling code continues. But, if they hit OK button, then you don't close the form, but ONLY set visbile = false.
This allows the calling code to continue, you are free to get/grab/take values from that form, and then once done, you close the form.
So a form close in that popup = user canceled the form.
So, a popup form, and even a modal form? They do NOT halt the VBA calling code, but a acDialog form does!
You can thus place 2 or 5 little buttons that pops up this form, the user can pick/choose/select/enter values. When they hit ok, your calling code continues, and you are free to pull values from that form. So while all 3-4 buttons might pop up that form, each individual button launch of the form can have code that follows and updates the given control the pop button was placed beside.

Form button intermittently returns runtime error 2101

I have a simple form on which the user selects a value from a list in a combo box, then clicks a button to open another form filtered by the selection. In development and testing it works fine, but in Prod users are occasionally receiving runtime error 2101: 'The setting you entered isn't valid for this property'. If the user restarts their machine the error doesn't persist, at least for a while.
This happens when the user clicks the 'Ok' button, which closes the selection form and opens the main interface form. The code for the button is just:
Private Sub btnOK_Click()
DoCmd.OpenForm "CC_Tracker_from_form", acNormal, , , acFormEdit
DoCmd.CLOSE acForm, "frmCoord_Selector"
End Sub
When the user closes the error popup, the selection form remains visible on top of the main interface, which sort of makes sense if the failure is in the DoCmd.CLOSE line, since Access would have already opened the main form.
Why would error 2101 trigger only some of the time, when the user performs exactly the same action (even the same selection from the combo box)?
I don't think this error has anything to do with the underlying Record Source for the main form, but just in case here's that code:
SELECT
[Bunch of columns],
IIf(dbo_CC_Tracker.RISK_LVL='Low',Null,dbo_CC_Tracker.CHRA+365) AS CHRA_Next,
IIf(dbo_CC_Tracker.RISK_LVL='High',dbo_CC_Tracker.[ICP/Review]+29,
IIf(dbo_CC_Tracker.RISK_LVL='Medium',dbo_CC_Tracker.[ICP/Review]+89,Null)) AS ICP_Next,
IIf((dbo_CC_Tracker.RISK_LVL='Low' Or dbo_CC_Tracker.RISK_LVL='Medium'),Null,dbo_CC_Tracker.F2F+179) AS F2F_Next,
IIf(dbo_CC_Tracker.RISK_LVL='Low',Null,dbo_CC_Tracker.ICTCont+89) AS ICT_Next,
dbo_CC_Tracker.HTR_Letter +30 AS Final_Follow,
dbo_CC_Tracker.ASSIGNED +59 AS Deadline_1,
dbo_CC_Tracker.ASSIGNED +89 AS Deadline_2
FROM dbo_CC_Tracker
WHERE
(((dbo_CC_Tracker.ASSIGNED_CARE_COORDINATOR)=Forms!frmCoord_Selector!cmbCoords)
And dbo_CC_Tracker.[CLOSE] is null)
Or Forms!frmCoord_Selector!cmbCoords Is Null;
It's possible that for some reason the form you are trying to close hasn't been able to save any updates.
I would make sure that if the form has any data changed it is saved, so maybe add
If Me.Dirty Then Me.Dirty = False
To force a save before closing the existing form.
Based on the idea that the first form may be closing before passing the dropdown selection to the second form I've changed from closing the form, DoCmd.CLOSE acForm, "frmCoord_Selector", to hiding the form, Me.Visible = False which should keep the selection active despite the first form no longer displaying.

How to requery form opened by another user

this may seem like an odd question, but I am wanting to requery a form that is opened by another user. Basically I have two different main menus, that pertain to the role of the employee. On these main forms I display a menu with the counts of clients in each status. I then have a main form that holds the clients data. After I update the status field I am requery both forms. Now this requerys the menu that is open on the current user that changed the status, but it doesn't requery the other menu that is opened by the other user unless they close the menu and reopen. Is this possible to requery another subform of another user's form.
Here is my code below:
Private Sub status_ID_AfterUpdate()
On Error GoTo Problems
DoCmd.RunCommand acCmdSaveRecord
Forms!frmNursesMenu!frmStatusCount_Nurses.Form.Requery
Forms!frmNursesMenu!frmStatusCount_Nurses.Form.Repaint
Forms!frmNursesMenu.Requery
Forms!frmNursesMenu.Repaint
Forms!frmAdminMenu!frmStatusCount.Form.Requery
Forms!frmAdminMenu!frmStatusCount.Form.Repaint
Forms!frmAdminMenu.Requery
Forms!frmAdminMenu.Repaint
Exit Sub
Problems:
Err.Clear
Resume Next
End Sub
You will need to use the form's timer event to requery the form every so often or look to see if the form needs a requery.
If your tables include a column with the time of the last edit then you could use that to see if the form needs a requery.

Save record in subform

I have a main form with a tab control containing multiple subforms. I need to be sure that the data in a subform is saved when the user switches tabs. The problem is that DoCmd.RunCommand acCmdSaveRecord seems only applies to the current form so it doesn't save the data in the subform.
I have tried different events on the subform such as deactivate, OnLostFocus etc but they don't fire until another field somewhere else gets the focus.
The ideal solution would seem to be to put something on the OnChange event of the tab control to be sure that all the data is saved. That is my question, how to do I save the record in a subform?
In Access, the default is to save, so unless you have done something pretty complicated to prevent this, moving the focus from a subform will automatically save the record. You can test this by adding a record, moving from the subform, and then checking the table.
You don't have to do anything at all, as the subform is saved as soon as it loses focus (when the tab control changes).
But as an exercise, I've outlined the code you'd write if you needed to.
You can save any form by setting it's .Dirty property to False. For something like this that's going to run a lot, I think I'd write a sub to walk through the subforms, check if any are dirty, and save the dirty ones. Something like this:
Public Sub SaveSubFormsOnTab()
Dim pge As Control
Dim ctl As Control
For Each pge In Me!ctlTab.Pages
Debug.Print pge.Name
For Each ctl In pge.Controls
If ctl.ControlType = acSubform Then
If ctl.Form.Dirty Then
ctl.Form.Dirty = False
End If
Debug.Print ctl.Name
End If
Next ctl
Next pge
Set ctl = Nothing
Set pge = Nothing
End Sub
Now, that's actually quite inefficient in cases where you have lots of controls on your tab control that aren't subforms. If your tab has nothing but subforms, it will be fairly efficient. In either case, it's much more efficient to use a custom collection populated in the form's OnLoad event, and then you'd walk that collection that includes nothing but your tab control's subforms, and save any that are dirty.
Either of these is preferable to using the OnChange event of the tab, because each time you add a tab page with a subform or change the name of a subform control, you'd have to alter the OnChange event.
I was having a similar issue where I needed various code to run in the subform and values in the main form - that were based on values in the subform - to be recalculated. Here is what finally worked:
This is an example with a main form named 'frmCustomers' containing a subform 'sfmTransaction', which in turn has a subform called 'sfmOrderLine'. The forms have a button 'cmdSave' that is only visible when the user clicks the 'cmdEdit' button (the purpose of which is to lock all the controls until the user clicks the edit button, and then to re-lock them when he clicks save):
On the main form ('frmCustomer') go the subform's exit event, and add 'me.recalc' twice, like this:
Private Sub sfmTransaction_Exit(Cancel As Integer)
If (Not Form_sfmTransaction.NewRecord And Form_sfmTransaction.cmdSave.Visible) Or (Not Form_sfmOrderLine.NewRecord And Form_sfmOrderLine.cmdSave.Visible) Then
'To save subforms
Me.Recalc
Me.Recalc
End If
End Sub
In the subform ('sfmTransaction') go the subform's subform's exit event, and add 'me.recalc' twice, like this:
Private Sub sfmOrderLine_Exit(Cancel As Integer)
If Not Form_sfmOrderLine.NewRecord And Form_sfmOrderLine.cmdSave.Visible Then
'To save subform
Me.Recalc
Me.Recalc
End If
End Sub
Hope this helps.
Setting the dirty property to false may force Access to save the data to the database, but it bypasses the before_update event. This means that if you've used this event for validation or other purposes, you can now have bad data in your database.