Close an Access form and use next form in stack - vba

I have a family history database. I occasionally need to add a father or mother to a record "on the fly". I do not want to close the database, add a record, then open it again. Accordingly, I added a button btnQuickAdd which opens a small form (frmQuickAdd) to add in the father/mother's name, date of birth, and gender. The frmQuickAdd has a "Save" button which closes the frmQuickAdd and then requeries the main form using code
DoCmd.Close acForm, "frmQuickAdd", acSaveYes
Me.Requery
However, using me.requery just results in error 2467 - the object is not open or does not exist. The main form clearly is still open. What am I doing wrong please?

As it is, you are trying the requery the frmQuickAdd.
I presume your frmQuickAdd is loaded through a button on the main form. If that's the case, then the .Requery needs to happen on the next line where the form is loaded.
Private Sub btnQuickAdd_Click()
'open form
DoCmd.OpenForm "frmQuickAdd", acNormal, , , acFormPropertySettings, acDialog
'requery
Me.Requery
End Sub
Also, you can avoid the hard-coded form name when closing by using the form's Name property:
DoCmd.Close acForm, Me.Name, acSaveYes
Edit
The above snippet will always requery the main form when the edit-form is loaded. If this behavior is not wanted, you can requery the main form when the save button is clicked using the form's name.
DoCmd.Close acForm, Me.Name, acSaveYes
Forms.MainForm.Requery
Note, MainForm must be loaded or you'll get an error.

Related

Why form object doesn't load RecordSource attribute in VBA for Ms Access

I need to read programmatically the RecordSource attribute of a form, to know what table or query is the form depending on.
After some research, it seems that the form object needs to be actually open in a window (or hidden, but open) so that certain attributes can be accessed. I've put together the following code snippet:
Application.DoCmd.OpenForm "Form1", view:=acDesign, windowMode:=acWindowNormal
Dim frm as Access.Form
Set frm = Forms("Form1").Form
Debug.print frm.Name 'works ok
Debug.print frm.RecordSource 'prints an empty string
Application.DoCmd.Close AcForm, "Form1"
I can see that the form is correctly open, but the RecordSource attribute is empty, eventhough the form is fed from a table.
What could be going on? I'm using .accdb Ms Access database files. And have compacted the database, just in case the db were corrupted, but the issue remains.
Edit: The form is very minimal, and I realize that actually has no RecordSource at all; it only has a subform which, since it is indeed fed with a table, does have a non-blank RecordSource.
So VBA was right returning a blank RecordSource for the parent form. The script posted was correct after all. Thanks for your inputs!!
Any hints or directions would be greatly appreciated. Thank you in advance!!
Most likely, the RecordSource is assigned in code when the form is loaded, either as a table name or as SQL:
Me.RecordSource = "Select * From SomeTable Order By SomeField"
In design view you can't get RecordSource programmatically. You can see from Property window. You have to open it as normal form, may be you want it to open as hidden. You can specify WindowMode to open form hiddenly by DoCmd.OpenForm "Form1", acNormal, , , , acHidden. Try below codes.
Private Sub CmdTest_Click()
Dim frm As Access.Form
DoCmd.OpenForm "Form1", acNormal, , , , acHidden
Set frm = Forms("Form1")
Debug.Print frm.Name
Debug.Print frm.RecordSource
DoCmd.Close acForm, "Form1"
End Sub

Access VBA unable to refresh a form

I have a form (frmDropDownEdit) that has a filtered table as the data. A "New" button is created that opens another form (frmDropDownNew) and the user can enter new data. When complete the new form is closed and the user is back to the original form. The code for frmDropDownNew correctly add the info to the table, then the code refreshes the frmDropDownEdit form but it does not refresh. If I click the refresh button in the ribbon, it also does not refresh. But refresh all does work.
How can I have my code refresh the data in frmDropDownEdit. I also put code me.refresh on the OnGotFocus event but that does not even run.
Here is my source code
Private Sub Command5_Click()
'Add Button
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("tblDropDown")
rst.AddNew
rst!DdCategory = Me.txtCategory.Value
rst!DdDescription = UCase(Me.txtDescription.Value)
rst.Update
rst.Close
DoCmd.Close
Forms!frmDropDownEdit.Refresh
End Sub
On my MS Access 2010 ×64 single user PC, Forms![AnyForm] .Refresh never worked in VBA, independently where it is placed in any database's code. Form_Current() doesn't run either as it should after data are modified (verified by putting a Stop therein). Moreover, records with modified data are neither marked dirty nor refreshed before the vba code has finished. Procedures which should run without delay when data are modified don't run, even when placed into the modified fields' events.
Thus, one has to use a work-around. Many people recommend to use .Requery instead of .Refresh and then to return by vba code to the desired record, but this requires a field with a primary key.
My solution for tables without primary key is the following:
'…
' ESSENTIAL: this code must be run from ANOTHER module !
' (it runs without error in MyForm's own code [eg. in Form_Activate(),
' but then MyForm is NOT immediately refreshed as desired,)
' one still has to repeat these steps by hand afterwards…
DoCmd.GoToRecord acForm, "MyForm", acNext
DoCmd.GoToRecord acForm, "MyForm", acPrevious
' NB: Forms![MyForm].Refresh doesn't work
' at this place in "MyOtherModule" either.
'…
As mentioned in above code comments: this code must be run from another module ("MyOtherModule") - in my case, a form-independent procedure called upon closing a pop-up form opened from the first form, which interactively modifies data. These data should be updated/refreshed immediately when closing the pop-up form, reflecting all changes and their consequences (for example, automatic filling-in/deleting of other data and/or en/disabling controls or making them [in]visible, depending on the modified fields' values).

Unable to refer form in another form's procedure

How do I refer a form when writing a procedure that is related to another form?
When I try to refer a form(not the current form) it returns null.in the image, the highlighted script in the project database window is where the code is written in.The form Forms("All Patient Info") is Null.
Image
This code is working. But the userform you are refering to must be opened, otherwise it won't be found. So add some routine like this:
If Not CurrentProject.AllForms("All Patient Info").IsLoaded Then
DoCmd.OpenForm "All Patient Info", acNormal
End If
Forms("All Patient Info").txtTest.Caption = "LEL"
If you don't want the form to be visible while you are doing something, add
Application.Echo False
before you open the form. When you are done modifying the form, close it with
DoCmd.Close acForm, "All Patient Info"
And Enable Echo again:
Application.Echo True

User level security?

I have a table that has usernames, passwords, and a yes/no column for isadmin.
How do I make it so if they login with an account that has a check mark under "isadmin" they get access to design view, the ribbon, etc? Though if they log in with an account that doesn't have a check mark under the isadmin box they only can view the forms, not edit them, and the ribbon is inaccessible?
I just don't know where to start, as I had assumed there was a way to save the database as a seperate copy that only users can view forms in, and if the admin runs his copy he gets all the changes to the tables (via the forms) the users made. So when the admin edits a form, and saves it it doesn't remove all the user's data as when it was saved, it was saved to the admin's copy too. I'm really confused.
I am using Access 2013
This is a simple solution for user level security being removed in newer releases of Access; using a lot of VBA.
STEP 1: Creating The Table
First, create a table. I will name mine LogininfoT. Now, for the columns inside of the table, name them EmployeeID, LoginID, LoginPassword, EmployeeName, and lastly IsAdmin. Make EmployeeID your key, and IsAdmin a YES/NO field.
For testing, add two users to this table. With this information:
EmployeeID LoginID LoginPassword EmployeeName IsAdmin
1 1111 1234 Bob [x]
2 2222 1234 Stewert [ ]
STEP 2: Creating The Forms
Now that we have the table made, let's design the form to use this set of data.
I will name my form LoginF. Go into design view, and slap down a text box, a combo box, and a button. For the combo box rename the text to say something like Login ID (you can change this to whatever fits your need) and for the text box, put the text as Password (once again, change this to whatever you want it doesn't effect the outcome). The text in the button can be whatever you want, I will be putting Login on it.
Click the combo box and rename it. I will be naming it LoginCmBx. Next, click the text box and rename it, I will be naming it PasswordTxt. Lastly, click the button and rename it, I will be naming it LoginBtn.
Click the combo box again and under the event tab, go into the After Update scripting. Use code and type this in:
Private Sub LoginCmBx_AfterUpdate()
Me.PasswordTxt.SetFocus
End Sub
This makes it so after you select a username, it automatically puts the focus onto the password text box so you can start typing right away without using TAB on your keyboard, or using your mouse.
Next, go to the button and under the event tab, go into the On Click scripting. Use code and type this in:
Private Sub LoginBtn_Click()
If IsNull(Me.LoginCmBx) Or Me.LoginCmBx = "" Then
MsgBox "You must enter a User Name.", vbOKOnly, "Required Data"
Me.LoginCmBx.SetFocus
Exit Sub
End If
If IsNull(Me.PasswordTxt) Or Me.PasswordTxt = "" Then
MsgBox "You must enter a Password.", vbOKOnly, "Required Data"
Me.PasswordTxt.SetFocus
Exit Sub
End If
If Me.PasswordTxt.Value = DLookup("LoginPassword", "LoginInfoT", _
"[EmployeeID]=" & Me.LoginCmBx.Value) Then
EmployeeID = Me.LoginCmBx.Value
On Error Resume Next
DoCmd.DeleteObject acQuery, "IsAdminQ"
On Error GoTo Err_LoginBtn_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("IsAdminQ", _
"SELECT IsAdmin " & _
"FROM LoginInfoT " & _
"WHERE EmployeeID = " & LoginCmBx.Value)
Exit_LoginBtn_Click:
DoCmd.Close acForm, "LoginF", acSaveNo
DoCmd.OpenForm "MenuF"
Exit Sub
Err_LoginBtn_Click:
MsgBox Err.Description
Resume Exit_LoginBtn_Click
Else
MsgBox "Password Invalid. Please Try Again", vbOKOnly, _
"Invalid Entry!"
Me.PasswordTxt.SetFocus
End If
End Sub
What this does is check if you selected a username, if not it spits out an error telling the user to select one. If you did, it checks if you entered a password. If they didn't, it spits out another error saying they didn't enter a password. If they selected both, and the password doesn't match the one in the table for the username you selected it spits out an error saying you got the password wrong. If you got the password right to the username you selected, it logs you in. It will then close the current form you are on, and open up a new one named "MenuF" it will also create a query with that little bit of information under the username you selected, either if it's an admin or not.. We haven't created MenuF yet, so lets quickly do that. We aren't done with LoginF just quite yet though, so be prepared to come back to that later!
Create the form, and put down a button. Here is your menu form, you can create as many buttons as you want going to other forms or even just put a subform on here and have your entire database. Taht button you put down, you can name the text to whatever you want. I put mine as Log out. Name the button MenuLogOutBtn. Go into the event tab, and under the On Click scripting click code and type this in:
Private Sub MenuLogOutBtn_Click()
DoCmd.DeleteObject acQuery, "IsAdminQ"
DoCmd.OpenForm "LoginF"
DoCmd.Close acForm, "MenuF", acSaveNo
End Sub
What this does is delete the query the login button created, opens the login form again, and closes the menu. Simple!
Now I need you to throw down a checkbox, and name it MyCheckbox. This box requires no special coding, or control sources. Though I do suggest changing visible as no, and deleting the text that comes along with it.
Now, go to the form's event properties and under the Open scripting go to code and type this in:
Private Sub Form_Open(Cancel As Integer)
Me.MyCheckbox.Value = GetLoginStateIsAdmin()
If GetLoginStateIsAdmin = True Then
Me.ShortcutMenu = True
DoCmd.ShowToolbar "Ribbon", acToolbarYes
DoCmd.ShowToolbar "Menu Bar", acToolbarYes
Application.SetOption "ShowWindowsinTaskbar", True
DoCmd.SelectObject acTable, , True
Else
Me.ShortcutMenu = False
DoCmd.ShowToolbar "Ribbon", acToolbarNo
DoCmd.ShowToolbar "Menu Bar", acToolbarNo
Application.SetOption "ShowWindowsinTaskbar", False
DoCmd.NavigateTo "acNavigationCategoryObjectType"
DoCmd.RunCommand acCmdWindowHide
End If
End Sub
What this does is checkbox's information which is attached to query's IsAdmin column and give GetLoginStateIsAdmin that boolean variable. After it does that, it starts a simple If statement that turns off menu bars and disabled right click if you aren't an admin; if you are, it allows you do right click and all menu bars are visible.
Though if you didn't notice yet, our checkbox doesn't get the information from the query yet! Oh no!
STEP 3: Creating The Public Modules
If you were on your toes, you would notice even the login code wouldn't work at this point. First, we need some public modules. Go to the Create tab in the ribbon, and create a module. Type this in:
Public EmployeeID As Long
Save this module as LoginModule.
Create another module, and type this in:
Function GetLoginStateIsAdmin()
'
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("IsAdminQ")
GetLoginStateIsAdmin = Nz(rst(0), False)
Set rst = Nothing
'
End Function
Save this one as GetAdmin.
Lets create one more module; so the user opening the database can't by bass stuff by using the shift key to launch it.
Type this in it:
Function ap_DisableShift()
'This function disable the shift at startup. This action causes
'the Autoexec macro and Startup properties to always be executed.
On Error GoTo errDisableShift
Dim db As DAO.Database
Dim prop As DAO.Property
Const conPropNotFound = 3270
Set db = CurrentDb()
'This next line disables the shift key on startup.
db.Properties("AllowByPassKey") = False
'The function is successful.
Exit Function
errDisableShift:
'The first part of this error routine creates the "AllowByPassKey
'property if it does not exist.
If Err = conPropNotFound Then
Set prop = db.CreateProperty("AllowByPassKey", _
dbBoolean, False)
db.Properties.Append prop
Resume Next
Else
MsgBox "Function 'ap_DisableShift' did not complete successfully."
Exit Function
End If
End Function
Save that as ShiftModule.
We are done the modules! Lets go back to the LoginF now.
STEP 4: Finishing Up LoginF
Go to the form's event tab, and click the on load scripting. Click code, then type this in:
Private Sub Form_Load()
On Error Resume Next
DoCmd.DeleteObject acQuery, "CustomerMoreInfoQ"
End Sub
What this does is make sure that the query the login button creates is deleted when this form starts up, just in case the user closes the database without logging out. So if you click login, it won't cause errors because the query isn't still there.
STEP 5: Testing It Out.
Run the form LoginF in form view, and select Bob as the username. Type in the password 1234 into the password text box, and click login. It should open up the MenuF and you see all menus and you can right click. Good. Now, log out and login with Stewert, using the same password. Now you see all the menus remove themselves, and you can't right click! Yay!
For extra security, in the LoginF's Other tab, make sure Shortcut Menu is set to No. This will set right click to be disabled always; as you aren't logged in as a user at this point. It doesn't know if you are an admin or not.
STEP 6: Disabling The Toolbars On Start Up & launching LoginF On Start Up.
Go to File > Options > Current Database.
Under Display Form, select FormF.
Under the Navigation section, unclick Display Navigation Pane.
Click okay, then go back to LoginF; go into the On Load code and add this just before the End Sub:
DoCmd.ShowToolbar "Ribbon", acToolbarNo
You are done! Save your database, then close it and open it again. It should load the LoginF form where you can't right click, there are no menus etc. The only way to get the menus to edit things is to log into an admin account!
Step 7: Expanding
This doesn't automatically expand the more you add forms though. You need to add that checkbox named MyCheckbox (I suggest copy + pasting it) to each form you add, and add this code to each form you add:
Private Sub Form_Open(Cancel As Integer)
Me.MyCheckbox.Value = GetLoginStateIsAdmin()
If GetLoginStateIsAdmin = True Then
Me.ShortcutMenu = True
Else
Me.ShortcutMenu = False
End If
End Sub
Though once you do that to every form, the security works and you need to log in to an admin account to change anything. If you are just a user, you can use the form normally (click buttons, edit data on subforms, etc) You can't edit the form it self though.

OpenForm in acHidden, then Save does not work

I'm trying to open form, change some properties, save, then close the form; all hidden from user.
For some reason, when I open form with acHidden mode, it throws me Error 29068 cannot complete this operation. You must stop the code and try again.
Here is the code :
DoCmd.OpenForm "frmProsContractorList", acDesign, , , , acHidden
DoCmd.Save acForm, "frmProsContractorList"
DoCmd.Close acForm, "frmProsContractorList"
If I use acWindowNormal instead of acHidden, no errors.
If I take out Save method, no errors.
It seems like OpenForm with acHidden and Save method does not work.
Can someone verify if above code works, and how to resolve?
WORK-AROUND : below works
DoCmd.OpenForm "frmProsContractorList", acDesign, , , , acHidden
DoCmd.Close acForm, "frmProsContractorList", acSaveYes
http://msdn.microsoft.com/en-us/library/ff192860(v=office.14).aspx
You can not edit a form's properties and then save the form. You can, however, edit some of a form's properties without opening the form. If you explain in full detail what you're trying to do, I can edit this answer to tell you how it's achieved. Until then, the answer is, "It can't be done the way you're doing it."
EDIT: You can simply set the field's Enabled property to True or False on the form's Load event.
i.e.
MyField.Enabled = False