vba in access and conditional formatting of forms - ms-access

I have a form that collects different types of data. One of them is the type of document. On accessing the tab the user has to select a type of document, from a pull-down list. If the document is selected as "NDA", then a tab named "type of NDA" must be enabled. The following is the code I am trying to use:
Private Sub doc_AfterUpdate()
If doc.Value="NDA" then
tnda.enabled=true
else
tnda.enabled=false
End if
End Sub
The pgm is simple, yet whenever I run my forms it keeps showing "error:type mismatch" and the code stops working.
Thanks for helping me with the above code.
This time i tried the code on a little more complicated form with more options. I tried to disable all the other options and enable them only according to the options i choose(from "type of document" tab in the form).Given below is the code:
Private Sub doc_BeforeUpdate(cancel as integer)
tnda.enabled=false
tagr.enabled=false
odoc.enabled=false
End Sub
Private Sub doc_AfterUpdate()
Select Case doc.column(1)
Case "NDA"
tnda.enabled=true
Case "Agreement"
tagr.enabled=true
Case "Other"
odoc.enabled=true
Case Else
End Select
End Sub
Private Sub doc_Oncurrent()
Select Case doc.column(1)
Case "NDA"
tnda.enabled=true
Case "Agreement"
tagr.enabled=true
Case "Other"
odoc.enabled=true
Case Else
End Select
End Sub
After running the pgm, all my tabs remain disabled irrespective of the choice of doc. Please provide valuable inputs in this regard.
Note tnda:type of NDA
tagr:type of agreement
odoc:Other document and doc=type of document Find image of my form here

It's because Doc is a combobox, thus probably bound to an ID that is numeric.
So either:
If doc.Value = <insert value of ID of text "NDA"> then
or use the column ID that holds "NDA", most likely the next:
If doc.Column(1) = "NDA" then

Related

VBA to generate permanent Controlls in Microsoft Access

Situation:
I download Attachments from Outlook and import them into Access tables based on a few criteria.
For example, I download following files:
SenderA_A_Timestamp.csv, SenderA_B_Timestamp.csv, SenderB_C_Timestamp.csv, SenderC_A_Timestamp.csv and loads more.
Now I have made a form where the user can, for example, select all type C CSV-Files since a date or just all types, since a certain date.
Cluster, Hosts and Volume represent the File types. The datetime indicates when it was updated last. Cl_Import, Hosts_Import and Volume_Import are the import specification needed for the different files.
Problem:
I now want to add a new type, for example, under Volume and want to have it stay there when I open the Form the next time.
Software:
-Microsoft Access 2016
-SQL queries
-Outlook 2016
-VBA
Create a table called SwitchboardItems.
Add these fields:
ItemNumber (Numeric, Primary Key)
ItemText (Text)
Command (Numeric)
Argument (Text)
ItemNumber should be sequential (use AutoNum if you want).
ItemText is the text as it will appear on the form.
Command indicate what to do when the button is pressed.
Argument is anything pertinent to that option (such as the file name to be opened).
Create a blank Continuous form.
The Record Source is:
SELECT * FROM SwitchboardItems WHERE [ItemNumber]>0 ORDER BY [ItemNumber];
ItemNumber 0 is the heading for the menu so is ignored in the record source.
Turn Data Entry, Allow Additions, Allow Deletions, Allow Edits and Allow Filters to No on the Data tab in the form properties
Add a command button and a textbox to the Detail section of your form.
Give the button the name of Option1 and the textbox OptionLabel1.
The Control Source for the textbox is ItemText.
Add a label to the Form Header name it Label1.
Add the below code to the Form_Open event. It places the menu heading in the form header:
Private Sub Form_Open(Cancel As Integer)
Me.Label1.Caption = DLookup("ItemText", "SwitchboardItems", "[ItemNumber]=0")
Me.Requery
End Sub
Add this code to the form module:
Private Sub Option_Click()
Select Case Command
Case 1
'Add code to set references to Excel, etc...
'Dim wrkbk As Object
'Set wrkbk = workbooks.Open([Argument])
Case 2
MsgBox [Argument]
Case 3
DoCmd.OpenForm [Argument], acNormal
Case 4
DoCmd.OpenReport [Argument], acViewNormal
Case 5
Case 6
DoCmd.Close
Case 7
DoCmd.OpenQuery [Argument]
Case 8
End Select
End Sub
Each Case statement reflects the Command value in the SwitchboardItems table and you should code what you want each to do (open workbooks, close the database, run SQL, etc).
Finally, add this code to the click events for the Option1 textbox and command button:
Private Sub Option1_Click()
Option_Click
End Sub
Private Sub OptionLabel1_Click()
Option_Click
End Sub
Your finished form will look similar to this (I've clicked Other_Import button which displays a message box as Command is 2):
NB: There's a lot more you could add to this - the last updated time as an extra field in the table for example.

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,

Display only last characters in a textbox

I have an Access 2002 database/application where my clients can enter multiple information about their own clients, including a code which follow some rules.
However, when they view these information after they have been entered, I need to hide every characters in this code except for the 4 last characters. However, the agent needs to be able to edit this code if it needs to be modified.
So basically, I have 3 phases possible:
First time information are filled, empty data. The field must show the characters entered.
At a later date, the code must be hidden in some way to show only the last 4 characters. It can be with * or simply the last 4 characters, but the user must not be able to see what is before these.
The agent edit the code, the code must then be properly modified in the database. The characters must be shown.
I tried to show only the 4 last characters, however my database gets modified... So the code gets cut in the database.
I wrote the following function to obscure sensitive data. Its primary use is to discourage shoulder surfing. I'm not sure if it will meet your particular needs, but it is simple, straightforward and may help others who stumble upon this question.
'Use to hide data in sensitive fields (e.g., BirthDate, PhoneNum, SSN)
'Usage: Ctl OnEnter property: =ObscureInfo(False, Form.ActiveControl)
' Ctl OnExit property: =ObscureInfo(True, Form.ActiveControl)
' Form Open property: =ObscureInfo(True, [BirthDate], [HomePhone], [SSN])
Function ObscureInfo(HideIt As Boolean, ParamArray Ctls() As Variant)
Dim Ctl As Variant
For Each Ctl In Ctls
If HideIt Then
If IsNull(Ctl.Value) Then
Ctl.BackColor = vbWhite
Else
Ctl.BackColor = Ctl.ForeColor
End If
Else
Ctl.BackColor = vbWhite
End If
Next Ctl
End Function
Wow - I'm shocked that this hasn't been answered sufficiently. The best answer is to use an unbound text box in your form instead of a bound one. First you'll need to make your unbound text box populate the actual field. You'll do that in the AfterUpdate event.
Private Sub UnboundTextBox_AfterUpdate()
[MyField] = Me.UnboundTextBox
End Sub
Then you'll need to set an OnCurrent event to populate your unbound text box with the protected view whenever the agents view the record:
Private Sub Form_Current()
Me.UnboundTextBox = String(Len([MyField])-4, "*") & Right([MyField], 4)
End Sub
However, you also want to let your agents edit or view the full code later, if necessary. The best way to do this would be to set the OnEnter event for your unbound text box to pull the whole field value, so the agent can see and edit it - effectively the reverse of your OnUpdate event.
Private Sub UnboundTextBox_Enter()
Me.UnboundTextBox = Nz([Field1]) 'The Nz deals with Null errors
End Sub
I've used this with a field displaying SSN's and it works like a charm.

disable cells based on another cell

In Access 07 on a form: I need to disable two cells based on a dropdown. Another words, if the user picks "new" from the dropdown, two other cells get disabled. I beleive I will need to right-click the dropdown, build code, but I don't know the IF... script I need to use.
Say your form includes a combo box named cboMyField which is bound to a field named MyField in the form's record source, and two text boxes: txtField2 bound to Field2; and txtField3 bound to Field3.
You could check the combo's value and set the Enabled property of the two text boxes appropriately by calling a procedure in your form's code module.
Private Sub SetButtonStatus()
If Me.cboMyField = "new" Then
Me.txtField2.Enabled = False
Me.txtField3.Enabled = False
Else
Me.txtField2.Enabled = True
Me.txtField3.Enabled = True
End If
End Sub
Call that procedure from the form's On Current event so the text box status will be set as you navigate between rows.
Private Sub Form_Current()
SetButtonStatus
End Sub
Do the same for cboMyField's After Update event so the text box status will be updated based on a user change for cboMyField.
Private Sub cboMyField_AfterUpdate()
SetButtonStatus
End Sub
Edit, Trouble-shooting: Since you're using Access 2007. Put the database in a trusted location and run it from there. See Get started with Access 2007 security
Edit2:
Change the SetButtonStatus procedure temporarily to check whether it even runs, and how it sees the combo box value if it does run.
Private Sub SetButtonStatus()
MsgBox "SetButtonStatus"
MsgBox "Value of cboMyField is '" & Me.cboMyField & "'"
If Me.cboMyField = "new" Then
Me.txtField2.Enabled = False
Me.txtField3.Enabled = False
Else
Me.txtField2.Enabled = True
Me.txtField3.Enabled = True
End If
End Sub
Also add Option Explicit to the Declarations section (at the top) of your module. Then select Debug->Compile from the VB editor's main menu. That effort should tell us whether the code includes any names which VBA doesn't recognize.
Edit3: From private communication, the combo's bound field was numeric data type, so could never be equal to "new". Therefore every time the procedure ran, it set text box Enabled property to True.

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.