The problem is that while the front-end allows 4 or more conditions, when I tried to set conditions using VBA, I ran into an error when setting the 4th condition. In other words, if I only tried to set 3 conditions in the code, then the code worked fine.
I am using MS Access 2010. I need to set conditional formatting for two textboxes on a continuous form. I know that older versions of MS Access allowed only 3 conditions on a textbox, but I know that I can get more conditions in Access 2010. My current application has 4 conditions using the user interface. In my research on this question, one person said that later versions of MS Access allow up to 50 conditions. I could not confirm this either way, even when I reviewed the Access 2010 specifications page. But I know I can at least get more than 3 conditions.
Here is the test code that works for up to 3 records:
Function fApplyConditionFormatNow()
Dim objFormatConds As FormatCondition
Dim i As Integer 'index number for specific format conditions
Dim stSQL As String 'query to get list of categories
Dim rs As DAO.Recordset
i = 0
'clear out just in case FormatConditions accidentially got saved
'with the form at some point.
Me.ID.FormatConditions.Delete
'get a recordset containing the formatting information
'(ie, get RGB values for each category type)
stSQL = "SELECT * FROM tblTestConditionalFormatting;"
fRunSQL stSQL, rs 'fRunSQL is custom code that gets runs stSQL and returns the recordset
'loop through recordset to get conditional formatting values
Do Until rs.EOF
'create a condition on textbox named "ID". The condition will be for
'the Category/Type (TypeNm) that's up now in the recordset.
Set objFormatConds = Me.ID.FormatConditions.Add(acExpression, , "[TypeNm] = '" & rs!TypeNm & "'")
'add formatting for the condition just created.
With Me.ID.FormatConditions(i)
.BackColor = RGB(rs!RGB1, rs!RGB2, rs!RGB3)
End With
i = i + 1
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
End Function
When 4 records are included in the category table : ie, tblTestConditionalFormatting, I get the following error:
"Runtime error 7966: The format condition you specified is greater than the number of format conditions."
So, there appears to be a bug in that the front-end can handle more than 3 conditions but the VBA object can only handle 3 conditions? OR maybe I'm doing something wrong. Has anyone else come across this? Do you have an idea for a work around?
Thank you!!
Instead of With Me.ID.FormatConditions(i), use the FormatCondition object that you just created:
Set objFormatConds = Me.ID.FormatConditions.Add(acExpression, , "[TypeNm] = '" & rs!TypeNm & "'")
'add formatting for the condition just created.
With objFormatConds
.BackColor = RGB(rs!RGB1, rs!RGB2, rs!RGB3)
End With
You don't need i anymore.
I have similar code that goes (simplified) like this:
For i = 1 To 6
lColor = DLookup("Color", "someTable", "p_Color_ID = " & i)
Set objFrc = txtFld.FormatConditions.Add(acExpression, , "[ColorGroup] = '" & i & "'")
objFrc.BackColor = lColor
Next i
Edit
Apparently you can edit the FormatConditions >= 3 if you don't touch 0..2:
https://access-programmers.co.uk/forums/showthread.php?t=271679
Maybe I have something like this... :/
Related
I'm building a simple form in MS Access with a listbox in which I want to display data that is stored in a MySQL Server table. I want two columns to display in the listbox.
I am able to display the first column with the additem property but the second column throws the run time error message 424 Object Required.
I have searched for hours in the web an in my books but I can't figure it out.
What is the problem?
Private Sub cmdSuchenVerantwortlich_Click()
Dim rsAuswahl As New ADODB.Recordset
Dim i As Long
If pConnectDB.State = adStateClosed Then
modConnectDB.Connect_To_DB
End If
Me.lstAuswahl.RowSourceType = "Value List"
'Clear Listbox
For i = Me.lstAuswahl.ListCount - 1 To 0 Step -1
Me.lstAuswahl.RemoveItem i
Next i
With rsAuswahl
.ActiveConnection = pConnectDB
.CursorType = adOpenStatic
.CursorLocation = adUseClient
.Open Source:="select MATNR, AUSNAHME from Ausnahmeliste where VERANTWORTLICH = '" & Me.cboVerantwortlich & "' "
Do Until .EOF
Me.lstAuswahl.AddItem .Fields("MATNR").Value
Me.lstAuswahl.Column(1, Me.lstAuswahl.ListCount - 1) = .Fields("AUSNAHME").Value
.MoveNext
Loop
.Close
End With
End Sub
The code Me.lstAuswahl.Column(1, Me.lstAuswahl.ListCount - 1) = .Fields("AUSNAHME").Value throws the error messsage.
The data type in the MySQL table of MATNR and AUSNAHME is varchar.
When I hold the mouse over .Fields("MATNR").Value I can see the excpected value and holding the mouse over .Fields("AUSNAHME").Value I can see the expected value as well. So the data is the but what's wrong ?
Thanks for anybody's help.
To add values using the "AddItem" method of the listbox, you need to concatenate the values, separated by a comma, so a delimited string as below:
Make sure that the columncount of your listbox is set to 2, or however many columns you want displayed.
Me.lstAuswahl.AddItem .Fields("MATNR").Value & "," & .Fields("AUSNAHME").Value
If you are only adding a few records, this is probably okay, but if you are going to display a lot of rows, it maybe best to transfer the data to a local table in access, and then bound your listbox directly to the table/query local to access.
I need to set the default value for a textbox in an ms access 2010 form. The default value needs to be the most recent date in CommunicationTable where the ClientNumber is the same as the ClientNumber associated with the current record in the form. The code below references the correct ClientNumber, but I am not sure how to get the most recent date. I am concerned that DMax might not be the appropriate function for getting the most recent date. How should I change the following to get the most recent date?
=DMax("DateOfCommunication","[CommunicationTable]","[ClientNumber]= "
& [Forms]![Main]![NavigationSubform].[Form]![ClientNumber] & "")
I realize I should also post the larger function in which the above function is nested:
=DLookUp("[Level]","[CommunicationTable]","DateOfCommunication= "
& DMax("DateOfCommunication","[CommunicationTable]","[ClientNumber]= "
& [Forms]![Main]![NavigationSubform].[Form]![ClientNumber] & ""))
Also, the form itself is bound to CommunicationTable. This VBA function is in the DefaultValue dialog box, which I get into via the property sheet for the text box. So I am not sure that creating a sql query will work in this case.
EDIT:
I have uploaded a stripped down copy of the database which reproduces the problem at this file sharing site.
To locate the code:
1.) Open the CommunicationEntryForm and
2.) open the AfterUpdate() event procedure for the ClientNumber field.
Next, to reproduce the problem:
1.) close `CommunicationEntryForm`
2.) In the Main form(which should already be open), click the View tab to open
the most recent CommunicationForm for any Client you want. Note the Level
number for that Communication.
3.) Click on the Communication tab. This will leave the form and show a list
of CommunicationForms for that Client.
4.) Click the Create New Form button. This will open up CommunicationEntryForm.
The value for Level should be the same as the value you noted in step 1 above.
The problem is that this is blank.
Can someone show me how to fix this problem?
#CodeMed - I did download the database but found you have issues other than what you are describing- such as when you 'add' a new communication you simply overwrite an existing record. I managed to get the result you were looking for, but it just changes the 3 records around. Does your non-sample program actually have the ability to create and add new records? As it is, I just changed your existing code to this:
Private Sub cmdNewCommForm_Click()
Dim cNum As Long
Dim strSQL As String
Dim rs As Recordset
Dim db As Database
Set db = CurrentDb
cNum = Forms!Main!NavigationSubform.Form!ClientNumber
strSQL = "SELECT Top 1 Co.Level AS MaxOfLevel " & _
"FROM CommunicationTable co Where Co.ClientNumber = " & cNum
Set rs = db.OpenRecordset(strSQL)
Forms!Main!NavigationSubform.Form!NavigationSubform.SourceObject = "CommunicationEntryForm"
Forms!Main!NavigationSubform.Form!NavigationSubform!ClientNumber = cNum
Forms!Main!NavigationSubform.Form!NavigationSubform!DateOfCommunication = Date
If rs.RecordCount > 0 Then
Forms!Main!NavigationSubform.Form!NavigationSubform!Level = rs!MaxOfLevel
Else
Forms!Main!NavigationSubform.Form!NavigationSubform!Level = 0
End If
Set rs = Nothing
Set db = Nothing
End Sub
What I would do is first grab the date by doing something like:
Dim db as Database
Dim rec as Recordset
Set db = CurrentDB
Set rec = db.OpenRecordset("SELECT Top 1 DateOfCommunication FROM CommunicationTable WHERE ClientNumber= " & [Forms]![Main]![NavigationSubform].[Form]![ClientNumber] & " ORDER BY DateOfCommunication DESC")
This will get the most recent date. Then, in your above VBA, you can just stick rec(0) in where your calculation was:
Me.MyDateField = DLookUp("[Level]","[CommunicationTable]","DateOfCommunication= #" & rec(0) & "#")
Substitute "MyDateField" with whatever that name of your date field actually is.
I'm pretty sure you need the pound signs (or "hashtags" as the kids call them today...) in order for Access to do the calculation on a date value.
First off I'd like to make perfectly clear that my knowledge of Access and VBA is extremely limited at best. I have an employee database system that due to it's age has been prone to small data corruption issues and controls breaking due to differences between 2003/2007 and 2010. While I've managed to hash out the bulk of the problems, one that has me especially concered is the script we're using to manage access to the database. The system is split between two files, a frontend where users can access the database and a backend file that contains all of the tables.
The issue I have is in the frontend form that handles the logon for the users. The way the access system is set up is the user enters their SSN, then the script finds their SSN in the table and if it exists looks if an access checkbox is checked. If they have access, they're directed to the main menu, if not they get a denied message. What I've found though is for some reason or another, if an entry in the personnel table has an incomplete SSN, the script breaks and anyone can gain access to the database.
There's a query that runs in the frontend that looks at the master personnel table and pulls just the first two columns, SSAN and Access.
The form itself has a visible text box, "Text8", and a hidden Combo Box "Combo4". Combo4 uses the previously mentioned query for the row source (SELECT qryAccess.SSAN FROM qryAccess;), while Text8 is where the user enters their SSN.
Here's the code right now:
Option Compare Database
Private Sub Combo4_AfterUpdate()
' Find the record that matches the control.
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
Private Sub Text8_AfterUpdate()
Me![Combo4] = Me![Text8]
' Find the record that matches the control.
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
Like I said before, as long as every entry for the SSNs is a full 9-digits, this system works. However, if for some reason the entry is not the full 9 like I just found in my database (and no, I have no idea what caused that to happen, there is an input mask in place, 000-00-0000;;_), this system breaks. You could type in "abc" for the SSN and gain access to the database.
How can I write a small script that pre-checks the table for SSN entries that don't fit the 9-digit format that is set, and if it finds them, resets them to an unused number, such as 000000000, 000000001, etc?
Also, if you have any suggestions on how to streamline the existing code, I'd be more than happy to take them.
Add this function to you application
Public Function IsValidSSN(ByVal SSN As String) As Boolean
'Determines if SSN is a valid social security number
'requires SSN to be in either "#########" or "###-##-####" format
IsValidSSN = (SSN Like "###-##-####") Or _
SSN Like ("#########")
End Function
Also change your function to this:
Private Sub Combo4_AfterUpdate()
' Find the record that matches the control.
If IsValidSSN(Me![Combo4]) Then
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
Else
DoCmd.OpenForm "frmDenied"
End IF
End Sub
Private Sub Text8_AfterUpdate()
Me![Combo4] = Me![Text8]
If IsValidSSN(Me![Text8]) Then
' Find the record that matches the control.
Dim rs As Object
Set rs = Me.Recordset.Clone
rs.FindFirst "[SSAN] = '" & Me![Combo4] & "'"
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
If Me![Access] = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
EDIT
Also why are you using a combobox to enter a SSN? You can use input mask on text box. Also I would highly suggest that you convert your system to some other identification other than SSN because it is easily passable to get past this code to look at the table containing everyones SSN, by holding down shift when opening the application. As for streamlining your code just remove that combobox altogether. If they are typing it into a textbox there is no need to put it into a hidden combobox.
You have a text field, SSAN, and with that input mask the dashes are not included in the stored values. So valid values would be 9 digit strings.
If that is correct, you can use a query to identify any invalid stored values.
SELECT y.SSAN, Len(SSAN) AS LenghtOfSSAN
FROM YourTable AS y
WHERE Len(SSAN)<>9 OR y.SSAN ALike '%[!0-9]%';
That query will return rows where SSAN includes < or > 9 characters, and any values which include characters other than digits.
Note the ALike keyword tells the db engine to expect ANSI wild card characters. If you prefer Access' * wild card instead, change it to Like '*[!0-9]*'
Once you fix the stored values, add a Validation rule for that SSAN field (Like "#########") to require all values consist of 9 digits.
Since it looks like this became more of a "How do I find the user" than "How do I fix the existing entries", let me throw my hat into the ring.
Unless I completely misunderstand this, the existing (and accepted answer) function is HORRIBLE. You can do this all much more efficiently and with less code. First of all, delete Combo4. No need for it. Then do this:
Private Sub Text8_AfterUpdate()
Dim X as Integer
X = DLookup("Access", "qryAccess", "SSAN = '" & Me!Text8 & "'")
If Nz(X) = True Then
DoCmd.RunMacro "Access"
Else
DoCmd.OpenForm "frmDenied"
End If
End Sub
That's all you need. If the user's SSN was stored incorrectly, he's gonna be denied. 7 digits, 8 digits, doesn't make a difference. Only exact matches get through. That is, assuming 0 = False and 1 = True, which should be the default anyway.
I am working on an Access Database where I need to display records from a table in a form as a datasheet. I believe I have correctly written the code to perform the filtering, but am not sure how to display the records.
I know that I can perform this easier using a query, and then a form based on those results, but wish to limit this process if at all possible, to reduce the overall size of the database. The filter will be sorting a company, and the fiscal dates.
Any help is appreciated.
Here is the code I have thus far...
Option Compare Database
Sub Form_Current()
Dim oTable As DAO.Recordset
Dim oDataNeedsGas
Dim dNextFiscal, dThisFiscal
Dim iGas
'Fiscal Year turnover date, use DateValue(dNextFiscal) comparison.
dNextFiscal = "10/1/" & Year(Date)
dThisFiscal = "10/1/" & Year(Date) - 1
'For Annual training by year comparison.
'Year(DateValue(oTable!randomdate)) >= Year(Date)
Set oTable = Application.CurrentDb.OpenRecordset("tbl_main", dbOpenDynaset)
iGas = 0
Do Until oTable.EOF = True
If (Year(DateValue(oTable![GasDate])) >= Year(Date) And oTable![Platoon] = "Data") Then
`What do I do here?!!?
iGas = iGas + 1
End If
msgbox iGas
oTable.MoveNext
Loop
End Sub
I know the filtering works, because I have it count the matched records, then display in a message box, but I want to be able to display the matched records. How do I go about doing this?
Make the RecordSource on your Datasheet from blank and then have this code run when the form loads:
Option Compare Database
Private Sub Form_Load()
Dim sSQL as String
sSQL = "SELECT * FROM tbl_Main "
sSQL = sSQL & "WHERE Year(DateValue(GasDate)) >= Year(Date) "
sSQL = sSQL & " AND Platoon = 'Data'"
Me.RecordSource = sSQL
MsgBox "RecordCount: " & Me.RecordCount
End Sub
I generally use the Form's RecordSource and the Forms Filter and FilterOn properties. You can always load the form showing all records and then filter down to what you want to see.
I didn't understand this line in your question:
"...but wish to limit this process if at all possible, to reduce the overall size of the database."
Are you trying to increase performance? Are you worried about storing too much data and the tables getting too large? That part of your question just isn't clear.
You can set your Subform's Recordset property to oTable. Make the recordset a property of the main form though, as shown in the following code, so that you can release this reference when the form closes.
Option Compare Database
Private oTable As Object
Private Sub Command2_Click()
Set oTable = Application.CurrentDb.OpenRecordset("tbl_main", dbOpenDynaset)
Set Me.sbfName.Form.Recordset = oTable
End Sub
Private Sub Form_Close()
If Not oTable Is Nothing Then
Set oTable = Nothing
End If
End Sub
For your specific example you would OpenRecordset based on a SQL statement that includes your date-criteria. I haven't tested whether this will be updateable, as it is for a Table. (I am getting the impression that it will not be updateable.)
It is possible to do this but I'm not suggesting it is a recommended approach. It is far easier to use the RecordSource property, filtering its records.
I want to emphasise that I would not use the Recordset of the subform. Use the RecordSource. You can set it to a SQL statement and/or filter records. Using the Recordset property is problematic (and unnecessary).
I am attempting to round-robin names into a "TASK" column depending on which type of job is assigned in the "JOB" column. My table looks like this for example:
my code is as follows:
Sub macro2()
Dim Rst As DAO.Recordset
Set Rst = CurrentDb.OpenRecordset("table1")
Dim employee(2) As String
employee(0) = "empname1"
employee(1) = "empname2"
Dim i As Integer
With Rst
i = 0
Rst.MoveFirst
Do While Not .EOF
If Rst.Fields("JOB") = "LETTER" Then
Rst.Edit
Rst.Fields("Task").value = employee(i)
Rst.Update
End If
.MoveNext
i = i + 1
If i > 2 Then i = 0
Loop
End With
DoCmd.Requery
End Sub
The problem is, sometimes it "misses" an assignment, and I am not sure why.
It should have kept looping those 2 names into the column, but it wont. However, sometimes after running it a couple of times it will do it. Upon opening the DB fresh, it will not, and will appear as above after completing. Any ideas?
This piece of the code allows i to have a value of 2.
i = i + 1
If i > 2 Then i = 0
But UBound(employee) is 1, which means employee(i) should throw a "subscript out of range" error when i is 2. But you didn't report getting that error, so I don't understand what's going on.
Also your first screenshot shows "Letter" and "Change" in the Job column, but the second screenshot shows all as "Letter". That's another puzzler.
Maybe you need to load the recordset with a query rather than the full table.
Set Rst = CurrentDb.OpenRecordset("SELECT Job, Task FROM table1 " & _
"WHERE Job = 'Letter'")
Then as you move through the recordset rows, toggle i between 0 and 1.
i = IIf(i = 1, 0, 1)
It looks to me like those changes might allow your code to work as you intend. However consider an approach which is more flexible and easier to maintain. Store the employee names in a table and open a second recordset, rsEmployees, to hold those employee names. As you move through the first recordset, Rst, also advance the second: rsEmployees.MoveNext If you reach rsEmployees.EOF, do rsEmployees.MoveFirst That should "round robin" the assignments.
That approach would allow you to add/remove employees from the available pool by simply updating the employees table. You wouldn't need to revise your VBA code each time you add/remove employees.