I have created a login form named login where the username is typed into the txtEmployee textbox, and I need to display the same in the second page in another form in MS Access.
DoCmd.OpenForm allows you to pass an arbitrary value as the last parameter. This value can be accessed in the new form as Me.OpenArgs:
' Invoked by some Button on the first form '
Sub GoToSecondPage()
DoCmd.OpenForm "MySecondPage", acNormal, , , , , txtEmployee.Value
End Sub
' Second form '
Sub Form_Open(Cancel As Integer)
If Not IsNull(Me.OpenArgs) Then
lblShowEmployeeName.Value = Me.OpenArgs
End If
End Sub
(Code example untested.)
You can pass a delimited string as the OpenArgs parameter:
DoCmd.OpenForm FormName:="miscForm", OpenArgs:=paramstring
Here's a routine for processing a pipe-delimited string passed as the parameter to DoCmd.OpenForm:
Dim Pstring As Variant
If Len(Me.OpenArgs) > 0 Then
Pstring = Split(Me.OpenArgs, "|")
var1 = Pstring(0)
<etc..>
End If
Personally I would pass them through the open arguments when opening the form. For example from form A your would write
DoCmd.OpenForm "frmB", , , , , acDialog,”Badger”
And then in the OnOpen event of form B you can capture what you have sent like this
Me.txtSomething=Me.OpenArgs
You can only pass one thing however What I do a lot is pass a pipe delimited string in the open arguments and then split that out.
A couple ideas...
Use the AfterUpdate event on the login username field to write the name to a global variable, then populate the field on the second page with the OnLoad event.
Or, if you plan on leaving the log in form open at all times you could set the default value of the field directly to =[Forms]![LogInForm]![UserName]
Why not create a public function which stores &/or retrieves a global variable that you store after the first form is executed? This would allow you to not only use it on one or more forms, but also as criteria within a query (for example, to return records which match the user name you've stored).
I agree this is "fake" security, but there also times when it's sufficient for your needs. I've implemented this before, where I match the environment string username to an entry in a user table.
BTW, I refer to this feature as personalization, not security.
Just a thought.
Related
Instead of creating 3 separate identical forms with different record/control sources how can I create a combobox that uses the same form but changes the record source(a table) depending on selection in combobox?
So far I tried this on click event(se below) but it just opens a form with #Name? inserted in textfields.
Private Sub cmbMain_Click()
' combobox main form
Select Case cmbMain
Case "A"
DoCmd.OpenForm "FormX", , , OpenArgs:=Me.RecordSource = "tblA"
Case "B"
DoCmd.OpenForm "FormX", , , OpenArgs:=Me.RecordSource = "tblB"
That's not how you use the OpenArgs argument. OpenArgs is just for passing a string to a form.
Instead, you can use the Forms collection to set the recordsource:
DoCmd.OpenForm "FormX"
Forms!FormX.Recordsource = "tblA"
I've been modifying my Access DB to clean it up and make it more user friendly. As such, I've been changing the names on form labels and controls to include _lbl and _Ctrl instead of the generic names Access assigned them. Previously, I had three separate forms (ListingsForm, ListOffersForm, ListDetailsForm) that I could access by using buttons to call the form. Since the forms were directly related to the primary form (ListingsForm), I changed two of the forms into subforms. Now I keep getting errors when I try to add a record.
On the ListingsForm form, there is a control field for Property Address (Address_Ctrl). This control uses a combo box that is linked to a separate table called Properties. If the property address is not listed in the drop down box, it used to bring up the PropertyForm form to input new properties. After, it requeried to get the new list with the new record. After making the subforms and changing control names, I'm getting an error whenever I try to add a new property address.
Run-time error '2473':
The expression On Not in List you entered as the event property setting produced the following error. Out of stack space.
'Add Address Form Script
Private Sub Address_Ctrl_NotInList(NewData As String, Response As Integer)
DoCmd.OpenForm "PropertyForm", , , , acFormAdd
Call Address_Ctrl_AfterUpdate
End Sub
'Requery Address List Script
Private Sub Address_Ctrl_AfterUpdate()
Forms!MLSListForm.Dirty = False
Me!Address_Ctrl.Requery
End Sub
It highlights the Forms!MLSListForm.Dirty = False line. I've tried Me.Dirty = False but I get the same error. If I remove the line entirely, I get 'Run-time error '2118': You must save the current field before you run the Requery action.'
Having the forms as subforms helps with user-interface so I really do not want to go back to having three separate forms. Any help would be appreciated.
Normally, your comment Address_Ctrl_AfterUpdate() event handler,
And you can try this event handler:
Private Sub Address_Ctrl_NotInList(NewData As String, Response As Integer)
'
' add in Properties tables for NewData:
' By calling SQL Server via ADO:
' INSERT INTO Properties (City) (NewData)...
' Or
'
'
' Now NewData Exists, do this:
'....
' and then set new value and status:
'
Response = acDataErrAdded
'
' here you can then modify new city:
'
DoCmd.OpenForm "PropertyForm", , , "City='" & NewData & "'", acFormPropertySettings
'
End Sub
I've re-debugged in our Access Database VBA, with Response = acDataErrAdded,
Access will Requery automatically Address_Ctrl.Requery() After the Address_Ctrl_NotInList() returns.
So I finally got it working correctly. Turns out I had to set some default values for the controls in the form design. After that, it worked out just fine.
I have an access report which fetches data from an underlying access query. The problem I am having is that I want to be able to allow the user the ability to sort the data based on 3 separate fields. How can I give the user this facility that he can choose if he wants to sort the incoming data based on a field A, B or C?
Kindly help.
Thanks!
The following should help.
First, use openArgs parameter to pass user's selected OrderBy field:
DoCmd.Close acReport, "MyReport"
DoCmd.OpenReport "MyReport", acViewPreview, , , , "OrderByColumnName"
Then, use passed value for ordering on report Load:
For Access 2007+:
Private Sub Report_Load()
Me.Report.OrderByOn = True
Me.Report.OrderBy = Me.Report.OpenArgs
End Sub
For Access 2003:
Private Sub Report_Open(Cancel As Integer)
Me.Report.OrderByOn = True
Me.Report.OrderBy = Me.Report.OpenArgs
End Sub
The outcome is the same: report will be [re]sorted based on the passed parameters.
Hope this helps.
Pivot Charts are the way to go for more complicated visualization of data.
If you really only want them to select on of the 3 fields, you can have a form where the user selects the field (through a listbox i.e.), open the report through the form and set its source attribute accordingly.
I have a form in an MS Access database which lists Orders with an Order Number with one order per page. At the bottom of the form there is a button which opens another form, to add an item for the order.
I am trying to use vb in MS Access to take the order number and automatically put it in a field in the details form for the new item. I have tried different ways but using OpenArgs seems to be recommended. But the detail form won't open and I get run-time errors.
Here are the details of the problem - advice will be much appreciated:
The forms and field concerned are:
Form with orders is frmPedidoAvifiFind
Form with order-lines for one order is frmPedidoAvifi-dtlAdd (a separate form for adding details but not for viewing existing ones).
Field on both forms for Order Number is PedidoAvifiNo. This is a numeric field in both tables which are linked by a one-to-many relation via this field.
Main form: Button bring up detail form, code as follows:
Code on main form button:
Sub AddDetails_Click()
Dim strDocName As String
strDocName = "frmPedidoAvifi-dtlAdd"
' Open frmPedidoAvifi-dtl form in data entry mode and store PedidoAvifiNo in the form's OpenArgs property.
DoCmd.OpenForm strDocName, , , , acFormAdd, , [frmPedidoAvifiFind]![PedidoAvifiNo]
End Sub
Detail form: On Open property
Private Sub Form_Open()
If Me.OpenArgs <> vbNullString Then
Me.PedidoAvifiNo = Me.OpenArgs
End If
End Sub
Test 1: select an order number on main form so that record shows.
Press button to add orderline. - run-time error '2465' can't find the field "|" referred to. Debug highlights the DoCmd line.
Test 2:
Change openform line to:
DoCmd.OpenForm strDocName, , , , acFormAdd, , Me.PedidoAvifiNo
result: - run-time error 2501 the openForm action was canceled.
Thank you,
Mike Gunner
Reus, Spain
The error you are getting means that you have misspelled the name of the control PedidoAvifiNo.
When you type Me. intellisense will give you a list of available fields, see what you have that is similar to PedidoAvifiNo, or check the properties. It can be very easy to switch one letter and not notice.
As for the second part, you should use the Load event, rather than the Open event on frmPedidoAvifi-dtlAdd, because the controls are not yet available in the Open event.
I'm using Access 2007 and have a data model like this...
Passenger - Bookings - Destinations
So 1 Passenger can have Many Bookings, each for 1 Destinations.
My problem...
I can create a form to allow the entry of Passenger details,
but I then want to add a NEXT button to take me to a form to enter the details of the Booking (i.e. just a simple drop list of the Destinations).
I've added the NEXT button and it has the events of
RunCommand SaveRecord
OpenForm Destination_form
BUT, I cant work out how to pass accross to the new form the primary key of the passenger that was just entered (PassengerID).
I'd really like to have just one form, and that allow the entry of the Passenger details and the selection of a Destination, that then creates the entries in the 2 Tables (Passenger & Bookings), but I can't get that to work either.
Can anyone help me out please?
Thanks
Jeff Porter
Actually the best suggestion I can give here is to not actually pass parameters. Simple in your form's on-open event, or even better is to use the later on-load event is to simply to pick up a reference in your code to the PREVIOUS calling form. The beauty of this approach is that if overtime you go from one parameter to 10 parameters then you don't have to modify the parameter parsing code, and you don't even have to modify the calling code. In fact there's no code to modify AT ALL if you decide to examine previous values from the calling form.
So, keep in mind using open args is only a one way affair. You can not use it to return values to the calling form. Furthermore all of the open args params will have to be strings. So, you lose any ability of dating typing such as real integers or even date and time formatting which can be quite problematic to parse out. And as you can see the example here the code to parse out strings can get a little bit messy anyway.
The simple solution is that in each form where you want values from the PREVIOUS from, simply declare a module level variable as follows
Dim frmPrevous as form.
Then in your forms on load an event, simply pick up the name of the previous form as follows:
Set frmPrevious = screen.ActiveForm
That is it. We are done!
We only written one line of code here. (ok two if you include the declaration statement). At this point on words ANY place in your form's current code you can reference the events properties and any field or value in the previous form by doing the following
Msgbox "PK id of previous form = " & frmPrevious.ID
And let's say for some reason that you want the previous form to re-load records as in a continues form. Then we can go:
frmPrevious.Requery
Or, force a record save:
frmPrevious.Dirty = false
So, the above becomes almost as natural and handy as using "ME" in your current code. I find this so simple and easy I think this should have been part of access in the first place.
And as mentioned the uses are endless, I can inspect ANY column or value from the calling form. You can even declare variables and functions as public, and then they can be used.
And, note that this works BOTH WAYS. I can stuff and change values in the calling form. So I can update or change the value of any value/column/control from the calling form.
And there is absolutely no parsing required. Furthermore the above code step even works if the same existing form is called by different forms. In all cases the forms calling ID can be picked up without modifying your code.
And even in the case of that I have many different forms launching and calling this particular form, you can still pull out the ID column. And, in the case that columns could be different from different forms, you can simply declare public variables or public functions in the calling form of the SAME name. So, if I wanted to call a form that needs the DateCreate, but each form did NOT have a consistent column name of DateCreate (maybe invoiceDateCreate and inventory Date Create), then you simply declare a public function in the calling forms with a constant name. We then can go:
Msgbox "Date created of calling form record = " & frmPrevious.DateCreated
So, Date created can be a public variable or public function in the previous form that could be any conceivable column from the database.
So don't pass values between forms, simply pass a reference to the calling form, not only is this spectacularly more flexible than the other methods showing here, it's also an object oriented approach in which you're not limited to passing values.
You can ALSO return values in the calling form by simply setting the value of any control you want (frmPrevous.SomeContorlName).
And as mentioned, you not limited to just passing values, but have complete use of code, properties such as dirty and any other thing that exists in the calling form.
I have as a standard coding practice adopted the above for almost every form. I simply declare and set up the form previous reference. This results in a handy previous form reference as useful as "ME" reference when writing code.
With this coding standard I can also cut and paste the code between different forms with different names and as a general rule my code will continue to run without modification.
And as another example all of my forms have a public function called MyDelete which of course deletes the record in the form, therefore if I want to delete the record in the previous calling form for some reason, then it's a simple matter to do the following
frmPrevious.MyDelete
So I can save data in the previous form. I can requery the previous form, I can run code in the previous form, I can return values to the previous form, I can examine values and ALL columns all for the price of just ONE measly line of code that sets a reference to the calling form.
I do this by defining properties in the form with Property Let and Property Get and passing values to those properties after opening the form, like this:
in the destination form:
Dim strCallingForm As String
Dim lngKey As Long
Public Property Get callingform() As String
callingform = strCallingForm
End Property
Public Property Let callingform(NewValue As String)
strCallingForm = NewValue
End Property
Public Property Let PrimaryKey(NewValue As Long)
lngKey = NewValue
End Property
in the calling form:
Sub btnGo_Click()
Const cform As String = "frmDestinationForm"
DoCmd.OpenForm cform
Forms(cform).callingform = Me.Name
Forms(cform).PrimaryKey = Me.PrimaryKey
Me.Visible = False
End Sub
(end)
I would use the openargs method of the form. That way you can pass one piece of data to the new form from any other form. You can also expand of this by sending a delimited string of arguments and then splitting them out. For example I have a form for editing agent activity that is passed the date, the agents name, agents ID and team in the open args
DoCmd.OpenForm "frmEdit_agent_activity", , , , , acDialog, Item & "|" & Me.txtDate & "|" & Item.ListSubItems(1) & "|" & Item.ListSubItems(2)
The form then uses this to pre populate the controls
Private Sub Form_Load()
If IsMissing(Me.OpenArgs) = True Or IsNull(Me.OpenArgs) = True Then
'no args, exit the form
DoCmd.Close acForm, "frmEdit_agent_activity", acSaveNo
Else
'this form has 4 open args
'1 Staff ID
'2 Date
'3 Team_ID
'4 Staff Name
Me.txtStaff_ID = GetDelimitedField(1, Me.OpenArgs, "|")
Me.txtDate = GetDelimitedField(2, Me.OpenArgs, "|")
Me.txtTeam_ID = GetDelimitedField(3, Me.OpenArgs, "|")
Me.txtStaff_name = GetDelimitedField(4, Me.OpenArgs, "|")
End If
End Sub
Ohh and here is the GetDelimitedField function
Function GetDelimitedField(FieldNum As Integer, DelimitedString As String, Delimiter As String) As String
Dim NewPos As Integer
Dim FieldCounter As Integer
Dim FieldData As String
Dim RightLength As Integer
Dim NextDelimiter As Integer
If (DelimitedString = "") Or (Delimiter = "") Or (FieldNum = 0) Then
GetDelimitedField = ""
Exit Function
End If
NewPos = 1
FieldCounter = 1
While (FieldCounter < FieldNum) And (NewPos <> 0)
NewPos = InStr(NewPos, DelimitedString, Delimiter, vbTextCompare)
If NewPos <> 0 Then
FieldCounter = FieldCounter + 1
NewPos = NewPos + 1
End If
Wend
RightLength = Len(DelimitedString) - NewPos + 1
FieldData = Right$(DelimitedString, RightLength)
NextDelimiter = InStr(1, FieldData, Delimiter, vbTextCompare)
If NextDelimiter <> 0 Then
FieldData = Left$(FieldData, NextDelimiter - 1)
End If
GetDelimitedField = FieldData
End Function
Have you considered subforms? There are ideal for one to many relationships. The Link Child Field(s) will be automatically completed from the Link Master Field(s).
If you need an example of subforms in actions, the Northwind database ships with all versions of Access, or you can download which ever is relevant.
2007
http://office.microsoft.com/en-us/templates/TC012289971033.aspx?CategoryID=CT102115771033
2000
http://www.microsoft.com/downloads/details.aspx?familyid=c6661372-8dbe-422b-8676-c632d66c529c&displaylang=en
You can use OpenArgs.
But I would also suggest that you also consider using a tab control.
That allows you to have different sets of controls on the same "screen real estate", using one single recordset, or using sub forms to show child recordsets.
In that case, you could use the "Next" button to just switch to the next page of your tab control.