Access VBA event code which modifying copy-paste automaticly - ms-access

Situation : I have an Access form with two coordinate fields to be filled. These numerical fields are called "latitude" and "longitude." A lot of coordinates are being copy-pasted into these fields. Those coordinates contain "," for the decimal which should be ".". An error will occur if not.
Objective : Be able to update my form with copy-pasting without modifying the pasted string manually ("," --> ".").
I looked for a VBA event code for the fields latitude and longitude. I tried =Replace([latitude];",";".") which does not work.
Here is the first try :
Private Sub latitude_OnDirty()
Me!latitude = Replace(Me!latitude, ",", ".")
End Sub
Private Sub longitude_OnDirty()
Me!longitude = Replace(Me!longitude, ",", ".")
End Sub
VBA code is accepted but still have an error message : "The value entered isn't valid for this field." MS Access valid the type of insertion. The field is numeric and any insertion with a "," can't be a number. So, the replace command in VBA code must be before the validation. How to do?

In your Longitude and Latitude fields, go to the event properties and select the "..." on the far-right of the AfterUpdate event.
A window will pop-up asking what type of Builder you want to use. Choose "Code Builder."
The VBE window will pop up and place your cursor below something like Private Sub latitude_AfterUpdate() where latitude will be the name of the textbox on your form. Underneath that line, paste the following code:
Me!Latitude = Replace(Me!Latitude, ",", ".")
Do the same thing for Longitude, but make sure to update the Me!Latitude part with the actual name of the textbox.
EDIT:
Due to the fact that your form controls seem to be bound, I don't think you will be able to dodge the errors thrown when a comma exists in the copy/pasted data. Here is what you can do instead:
Create two new textboxes. I'll call them LonEdit and LatEdit. When you create them, drag them from the ribbon, do not use the "Use Existing Fields." This should make the "Control Source" properties empty, and should display the word "Unbound" in the textbox when the form is in design view.
Next, go to LonEdit's event properties and use the code builder for the AfterUpdate event.
Here is the code you should insert in the LonEdit_AfterUpdate event:
'Test for comma
If InStr(1, Me!LonEdit, ",")>0 Then
'Replace comma with period and copy to old textbox
Me!Longitude = Replace(Me!LonEdit, ",", ".")
Else
'No comma, just copy to old textbox
Me!Longitude = Me!LonEdit
End If
'Clear the new textbox
Me!LonEdit = ""
Do a similar thing for the new Latitude box:
'Test for comma
If InStr(1, Me!LatEdit, ",")>0 Then
'Replace comma with period and copy to old textbox
Me!Latitude = Replace(Me!LatEdit, ",", ".")
Else
'No comma, just copy to old textbox
Me!Latitude = Me!LatEdit
End If
'Clear the new textbox
Me!LatEdit = ""
Once complete, try copy/pasting into the new textboxes, and then tab out of them when finished. This should replace the comma and copy to the old textbox without throwing an error. Not as nice of a solution, but it's the best I can come up with at the moment. Good Luck!

Related

Make fields visible in MS Access form as certain fields are completed

I am building a form In MS Access for users to input data but there are too many possible fields. Most of the time only about half the fields will be used.
I thus would like to have certain fields appear only depending on what the user inputted on a prior given field.
Ex: user enters project number, title, then he checks a "yes/no" engineering. since he checked it this means engineering is impacted so a dozen fields that the user will have to fill out appear.
Is this possible:
1)without VBA
2)with VBA
Probably not possible without VBA.
With VBA for example:
Ensure your form is in Design view
Right click on your Combo Box, Build Event, Code Builder
This opens the code behind your form. It drops you into the default code for the BeforeUpdate event. We want the Change event instead, so at the top right change the drop down from BeforeUpdate to Change. This will give you a bit of code like this:
Private Sub Field1_Change()
End Sub
Inside here, you want to check the value of the combo box and hide fields as required:
Assuming the name of your combo box is Field1 (yours of course will be different), you add some code so it looks like this to hide Field2:
Private Sub Field1_Change()
If Field1.Value = "Yes" Then
Me.Field2.Visible = False
End If
End Sub
Note you need to know the names of all your fields - this is in the Other tab, Name field in the properties box (F4). You should give all of your fields sensible names so you can understand what is going on in the code.
For a check box, follow exactly the same procedure, but you probably need to use the Click event. Just experiment.
Sample check box code:
Private Sub Check5_Click()
' Note: vbTrue = -1
If Me.Check5 = vbTrue Then
MsgBox ("Ticked")
Else
MsgBox ("Not Ticked")
End If
End Sub
I have a form that will show certain fields after a list box value is selected. I use the AfterUpdate function. It has worked so far. My code is below. ProjectName and ProjectNumber are fields you only want displayed if Engineering is selected. OtherName and OtherNumber are fields you only want to show if it is a "NotEngineering" project.
Insert this code by clicking on the Field that selects the project type, go to the Event tab on the property sheet, and click "After Update" and choose code builder and paste in VBA.
Private Sub ProjectType_AfterUpdate()
If ProjectType.Value = "Engineering" Then
Me.ProjectName.Visible = True
Me.ProjectNumber.Visible = True
Else
Me.ProjectName.Visible = False
Me.ProjectNumber.Visible = False
End If
If ProjectType.Value = "NotEngineering" Then
Me.OtherName.Visible = True
Me.OtherNumber.Visible = True
Else
Me.OtherName.Visible = False
Me.OtherNumber.Visible = False
End If
End Sub
There is a way to do not-quite-this without VBA. I'd definitely recommend VBA though, because you can do a lot more with it.
Rather than hiding, try disabling the unnecessary fields with conditional formatting by expression.
right-click on the control you want disabled.
go down and click on 'Conditional Formatting'
Add a new rule
Select 'Expression is'
example:
[fld1]="yes"
hit the disabled box
click ok
click ok
now the control you've selected will disable if field 1 has "yes" selected.
I have the same problem and I did the following:
Private Sub Field1_Click()
If Field1 = "Yes" Then
Me.Field2.Visible = True
Else: Me.Field2.Visible = False
End If
End Sub
but now I have other problem, when I change record, the field that I choosen to be visible in the last record is now visible on the current record, although I have not choosen any option.
Thank you,

Out of Stack Space & Requery Errors

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.

MS Access TextBox Value is always NULL

I am doing some work for someone that insists on using MS Access. I don't usually use it so I am a bit new to the whole control structure and best practices. What I am trying to achieve is to have a filter textbox on a form which, when a value is entered, it will filter the rows in the detail section. That seems like a straightforward use case. I initially tried the following behaviour as the event handler for the On Change event:
Private Sub FilterGrid()
Me.Text32.SetFocus
If Not IsNull(Me.Text32.Text) And Me.Text32.Text <> "" Then
Me.Filter = "JobNumber LIKE '*" & Me.Text32.Text & "*'"
Me.FilterOn = True
End
Else
Me.FilterOn = False
End If
End Sub
This worked perfectly until I typed something that didn't have any rows matching and the whole thing exploded with this error (and was unrecoverable without closing the form):
Run-time error '2185': You can't reference a property or method for a control unless the control has the focus.
I did some reading around and the general opinion was that .Text should not be used and .Value (or simply the Text32 without a property) should be used. That produced some very strange behaviour. The Text32.Value is ALWAYS null. I have a watch window and I can see that for the normal behaviour, Text32.Text has an actual value, but Text32.Value is NULL.
Obviously I am doing something wrong, but I don't have enough experience with Access to know what it is.
Just as an aside, another suggestion was to do Text32.SetFocus right before accessing the Text property. This doesn't resolve the error I mentioned. It still throws the exact same error.
Is anyone able to point me in the right direction here?
As you found out, the textbox's Value is only set after the control loses focus.
Conversely, the Text property is only accessible while the control has focus.
The Value property is defined as the default member for controls; that means Text32 will be implicitly the same as Text32.Value, however, depending on the context,Text32 can sometimes refer to the control itself, not just its value.
All these discrepancies can sometimes be infuriating.
To go back to the matter at hand: you have 2 ways to handle filtering.
if the list to filter is large, it's probably better that the user type their filter, then press ENTER to validate it.
if your list is not too large, you can implement filter as you type.
First case, wait for user input to be validated by ENTER.
Say your filtering textbox is called txtFilter and is located on a form whose subform is showing a datasheet (or continuous form) that you want to filter.
All you need to do is wire the textbox OnKeyDown events as such:
' We will only perform the filter if the user press the ENTER key
Private Sub txtFilter_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case 13, 9
KeyCode = 0
QuickFilter
End Select
End Sub
' Perform the actual filtering on the subform
Private Sub QuickFilter()
Dim sql As String
Dim filter As String
If txtFilter.Text = vbNullString Then
' Reset the filter if the textbox is emtpy
SubForm.Form.FilterOn = False
Else
'Some common substitutions that users may have already inserted as wildchars
filter = Replace(txtFilter.Text, "%", "*")
filter = Replace("*" & filter & "*", "**", "*")
' We construct the filter SQL
sql = "([JobNumber ] LIKE """ & filter & """)"
sql = sql & " OR ([ProjectCode] LIKE """ & filter & """)"
sql = sql & " OR ([SupplierName] LIKE """ & filter & """)"
'... Add as many columns to filter on as you want
' Assign the filter to the subform
SubForm.Form.filter = sql
SubForm.Form.FilterOn = True
End If
End Sub
Second case, filter as you type
Well, it's fairly easy, we just need to add to the above solution a way to track changes as the user is typing.
This is best done through the OnChange event of the texbox.
Private Sub txtFilter_Change()
QuickFilter
End Sub
That's all you need to add.
dont use .text property
instead use .value property
text3.value=text1.value+text2.value
you dont need setfocus like .text property everytime.
it works perfectly....vba ms access

Subform field hyperlinks to open record in another form

I'm trying to replicate a feature in the Access 2007 "Issues" template/example database. When you open up the 'Issues List' form, and click on an ID, it behaves like a hyperlink and opens up that record in another form.
How can I replicate this? I'm not a big fan of using the Access 'create macro' feature and would prefer to use the VBA editor if possible.
Thanks in advance for your help.
I didn't examine that template database, but your description sounds like something you can handle with DoCmd.OpenForm.
Say your form includes a text box named txtID which is bound to a numeric field, ID, in the form's record source. Create a VBA procedure for the text box's click event to pass the current ID value as the OpenForm WhereCondition parameter. (This assumes the record source of the next form also includes that numeric ID field.)
Private Sub txtID_Click()
Const cstrForm As String = "YourNextFormName" ' <-- change this
DoCmd.OpenForm cstrForm, WhereCondition:="[ID]=" & Me.txtID
End Sub
If the data type of the ID field is text rather than numeric, include quotes around the value in the WhereCondition.
WhereCondition:="[ID]='" & Me.txtID & "'"
What you want to do is first set the text field you're interested to a hyperlink. You do this by setting Display As Hyperlink in the Format tab of the Property Sheet or you can do this via vba as following (although it seems to be bugged visually i.e. clickable link but not displayed as such)
myTextBox.DisplayAsHyperlink = acDisplayAsHyperlinkOnScreenOnly
After you do that, create a Click event. In the subroutine you can run the code to open your form filtered with the record number (or appropriate argument)
DoCmd.OpenForm "myFormName", acNormal, , "[ID]=" & Nz([Id], 0)

How do I chain forms in Access? (pass values between them)

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.