I have form with several sub forms.
When user change combo box value located in main form, I want every subform to refresh using Requery Method. It worked as I want.
Except in one of the subform. I'm adding a listbox and also put a textbox to SUM one of the column in the listbox.
I've create vba code to calculate the column. Put the code in Current event of the subform, and several other events in the subform. Also in listbox AfterUpdate event. But it doesnt seem to work.
Dim i, j As Integer
Dim ctl As Control
Set ctl = Me.lstBox
j = ctl.ListCount - 1
Me.txtTotal = 0
For i = 1 To j
Me.txtTotal = Me.txtTotal + ctl.Column(3, i)
Next i
Me.txtTotal = Format(Me.txtTotal, "#,##")
Add a breakpoint in sub procedure name (such as Form_Current, etc.), but it seems not firing up.
Where should I put this code?
EDITED
I try put the code in ComboBox AfterUpdate event, it worked.
But if there is another approach, I still can give a vote.
Related
I have a Project to be done in MS Access 2016, and stubled across an Issue, that should be easy to resolve, however, I have no clue on how to do it.
The Database I am developing is based on a huge, unfiltered datasheet exported by another database. I have a main form headview in which I placed two subforms listview an detailview. The listviewis sorted by a combobox.
Now to what "should" happen: If you click on an entry of said listview, the detailview shows additional information of the clicked entry.
Both subforms are based on the same datasheet. So I went ahead and tried to match them via primary key entries. However, that didnt work. The detailview subform is still empty. I also tried to write a vba macro for the listview with listview.click() that didnt work either.
Is there a way to connect those two subforms within a main form? If so, how do I do that?
I am greatfull for any response,
Have a nice day
-Ninsa
Ok, finally I got the reason for error 2455, it's a timing problem.
When the procedure Form_Current of the listview form is called the first time, the detail subform is not yet bound to the detail subform control, which causes the error.
Possible solutions
Option1: Ignore error 2455
Either add On Error Resume Next in the top of the Form_Current procedure or rewrite it to handle that specific error 2455:
Private Sub Form_Current()
On Error GoTo Catch
With Me.Parent.DetailSubformControl.Form
.Filter = "[ID] = '" & Me.ID.Value & "'"
.FilterOn = True
End With
Finally:
Exit Sub
Catch:
If Err.Number = 2455 Then Resume Finally
MsgBox Err.Number & "(" & Err.Description & ") occured.", vbExclamation, "Attention"
Resume Finally
End Sub
Option2: Control the source objects of the subform controls
Clear the Source Object property of the subform controls in the head form and set them explicit when loading the head form.
That prevents the unlucky timing at all.
So in the head forms load event procedure add this:
Private Sub Form_Load()
Me.DetailSubformControl.SourceObject = "Table1Detail"
Me.DatasheetSubformControl.SourceObject = "Table1Datasheet"
End Sub
Option3: Use Link Master/Child Fields
You could use the properties Link Master Fields and Link Child Fields of the detail subform control.
Therefor you have to create a textbox control named ID on the head form and for cosmetic aspects hide it by setting its property Visible to False.
This new control will be bound to the detail subform control:
Set the property Link Master Fields and Link Child Fields of the detail subform control on the head form both to ID.
The Form_Current procedure of the listview form then only contains this:
Private Sub Form_Current()
' Set the value of the hidden control 'ID' on the head form,
' which is bound to the detail subform control, to the selected ID.
Me.Parent.ID.Value = Me.ID.Value
End Sub
You should handle filtering of the detailview on the Listview_Current event. That event fires as soon as Listview changes records.
You can either set up an event handler for the Listview_Current event on the listview's form module, or use WithEvents in the parent form to listen to that specific event.
If you choose the latter, note that it's required that Listview has a form module, else the events won't fire.
Given that your ID field of the datasource is ID and the detail subform control is named DetailSubformControl, this example works.
Place this code to the Form_Current event of the listview subform (which is fired on every record you move to):
Private Sub Form_Current()
' Set a reference to the detail subform control
With Me.Parent.DetailSubformControl
' Set the filter of its contained form to the current ID of the listview.
' The "'" are only necessary if it is a text and not a numeric field.
.Form.Filter = "[ID] = '" & Me.ID.Value & "'"
.Form.FilterOn = True
End With
End Sub
I trying to use TabControls for subforms in an Access Form.
I have a subform embedded on a page of a TabControl. I meed to move from the last data entry field in the Main Form to the first data entry field in the Subform.
When I SetFocus to the Control in the subform, it does not allow editing without clicking on the TextBox. Is there a command for immediately enabling editing?
Private Sub ReadDate_AfterUpdate()
Dim myControl As Control
Set myControl = Forms!Data_Input!BOD_Data_Subform!textBoxToEdit
myControl.SetFocus
End Sub
Thanks in advance for any suggestions.
Two steps required to select a control on a subform from a main form
Do the following in order:
Select the subform control.
Select the appropriate control on the
subform.
Example code
Private Sub ReadDate_AfterUpdate()
Dim myControl As Control
' Select the subform control first
Forms!Data_Input!BOD_Data_Subform.SetFocus
Doevents
' Select the control on the subform.
Forms!Data_Input!BOD_Data_Subform.Form!textBoxToEdit.SetFocus
End Sub
* Edits *
I suspect answer would work if there were not TabControls in Main Form. It is a two-part process but in this case the first step is:
Forms!Data_Input!subDataTabControl.Pages(0).SetFocus
Where subDataTabControl is name of the Tab Control and 0 give page index.
I have a rather simple looking problem but it turned out to be more complicated than I thought.
I have a field (column) in my subForm which is a ComboBox.
I have a field (column) in another subForm by which I would like to filter this comboBox.
Basically, the comboBox before filtering has some 600 records, too many to scroll by casual user. I created a simple subForm whose field is linked to a mainForm and this works perfectly (ie. the selected record-field-ID is displayed on mainForm).
Now what I want is that this comboBox is filtered by this record (ie. only showing relevant fields). The problem is that if I simply Requery it with this given filter, the other fields show up blank.
I want this filter to apply only to NEW RECORDS comboBox, not the whole datasheet view.
What I did is:
Private Sub Sekacie_Operacie_GotFocus()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Sekacie_Operacie.RowSource = Replace(SQL, ";", "") & " WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
Me.Sekacie_Operacie.Requery
'works kinda as intended
End Sub
Private Sub Form_AfterInsert()
Me.Sekacie_Operacie.RowSource = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Refresh
End Sub
And when I select the record in my filter subForm:
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
However, this workaround still shows "blank" records sometimes (I have to refresh the form by clicking different record) and I find it strange I had to go all the way to do this. Is there no simpler way of accomplishing this?
A couple of immediate things:
No need to refresh the whole sub form, just refresh the control.
Private Sub Form_AfterInsert()
' Me.Refresh ' replace this line with the following line
Me.Sekacie_Operacie.Refresh ' or is it Requery I can't remember
End Sub
The following code can be improved
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
' by using code like this
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
me.Sekacie_Operacie.RowSource = SQL
' I think the above line will cause the combo to refresh
' anyway (in some version of accees it might not,
' but, if needed add the following line
' me.Sekacie_Operacie.refresh
End Sub
The following line has the potential to produce and error
(perhaps when the main form is a new record)
WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
replace it with
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
Note that adding such code will stop the subform from working independently of the main form and will require it to have a parent form.
You could get around this by using:
if not me.parent is nothing then
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
end if
About your approach.
I find it a bit weird what you are doing. Consider this:
Say the query qrySekacie_Operacie returns 100 rows when run without any WHERE clause. When you are inserting a record into the subform you want to limit the combo by adding a WHERE clause that uses a value in the parent formMe.Parent.Form!txtOP_ID) , this might limit the number of rows displayed to 10.
It is worth noting that if any of the other rows in the subform contain value other than one of these 10 the combo will display a blank value. I think this is what you are experiencing.
My first question was why not use the on current event of the parent form to change the subform combo box SQL. It appears this is not what you need.
It appears that other values can exist in the dataset of the subform and that you only want certain value to be enterable in this subform but you want the subform to display any values that have been entered.
So that gets interesting. Here's what I would do:
Create a hidden combo control (with no control source) that is only visible when a new record is being entered. When this new combo is shown the other one is hidden.
Set up this new combo with the required SQL
use a before insert event to copy the value form the new combo to he old one.
Easy peezy?
Let me know ho you get on.
One other thing
I like to Ensure a subform encapsultes code that operates on it "private" controls.
As I general rule of thumb, where possible (which is 99% of the time), I like to try and keep subforms so that they will work as forms in their own right. They can be though of as an object, so if they work independently, it is an indication that their code has been encapsulated within their own code module.
So if the form has me.form.parent = nothing then the code behaves differently as it is a independent form.
So rather than the parent form code module having code that sets the SQL of the subforms combo, I would write a public sub in the subforms module (called SetComboSQL) that can be called (with the appropriate parameters. ie OPid ) to set the combo SQL.
To call the procedure in the parent form I would add
Private Sub Form_Current()
Me.MySubformControlName.Form.SetComboSQL(OPid:=Me.txtOP_ID)
End Sub
' note that intellisense won't pick up this proeprty BUT it will work. Use debugger to check it out.
I hope you find this tip useful (as it can be a massive headache saver. THE MOST BASIC OO PRINCIPAL is encapsulation. An object should own all the code that effects it's properties (and controls).
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,
I have an Access 2003 form with one subform, set to continuous forms, in a subform control. For one record in the main form, 1 to many records will appear in the sub form. The data displays properly.
The main form is named Widgets and the sub form is named Transactions. There are 5 textbox controls that display the data in the subform. The one in question is ReceiptDate.
What I would like to do is look at the values and determine if there was a receipt for the year 2009, and if so then change the background of that row to yellow so it stands out when the user encounters that condition. Maybe even change the date field's font to boldface..
I tried many ways of referencing the subform's controls. When I have tried Me.Transactions.ReceiptDate I have only received the first record in that subform. I'd like to be able to loop through them and see if the condition is met. I tried Me.Transactions.ReceiptDate(1) and Me.Transactions.ReceiptDate(0) and so forth.
I tried the For Each ctl In Form.Controls route as well. It worked for a few iterations and then I received a run-time error 2455 "You entered an expression that has an invalid reference to the property Form/Report".
I had the subform in "datasheet" mode but thought that was causing me not to be able to read through an array of subform controls. So I changed it to "continuous" mode. I get the same errors for either.
Is there a way to reference specific "rows" in the subform and do something based on a value found? Also, I am performing this in the On Current event as I dont know where else to put the code. The subform loads before the parent form so its possible that these controls arent even fully "there" but then I do get the first row's date when I try it in the Immediate Window.
UPDATE 12.23.2010:
With the gracious help of #Remou I am able to debug.print the ReceiptDate fields from the RecordSet. This is great because now I can evaluate the data and do certain things based on the values.. The #Remou's code helped me put this into the OnCurrent event:
Dim i As Long
Dim frm As Form
Dim rs As DAO.Recordset
' Get the form and its recordset.
Set frm = Me.Form
Set rs = frm.RecordsetClone
' Move to the first record in the recordset.
rs.MoveFirst
' Move to the first selected record.
rs.Move frm.SelTop - 1
' Enumerate the list of selected records presenting the ReceiptDate field
For i = 1 To rs.RecordCount
Debug.Print rs![ReceiptDate]
rs.MoveNext
Next i
So now that I am able to know which row in my subform has a receipt from 2009, I need to be able to highlight the entire row or rows as I come across them in that for loop. How can I reference the actual row? Datasheet view or Continuous Forms view - I have tried both.
Conditional Formatting is great but it only allows me to highlight one particular record and I'd much rather be able to do this via VBA because...... from here I will want to give the use the ability to click on any record in the subform and get the receipt details and potentially print them.
Any ideas?
In this situation, it is best to use conditional formatting.
To refer to a control on a subform, refer to the subform control by name, then the form property to get the form contained, then the control by name:
Me.MySubformControlName.Form.MyControl
See: http://www.mvps.org/access/forms/frm0031.htm
I have finally got it. The frm.SelTop = x will set the selected record and from there I can set the background or font style, etc.. Very cool. A simple test for 2009 and setting the selected record:
Dim i As Long
Dim frm As Form
Dim rs As DAO.Recordset
' Get the form and its recordset.
Set frm = Me.Form
Set rs = frm.RecordsetClone
' Move to the first record in the recordset.
rs.MoveFirst
' Move to the first selected record.
rs.Move 0
' Enumerate the list of selected records presenting
' the CompanyName field in a message box.
For i = 1 To rs.RecordCount
If Year(rs![ReceiptDate]) = 2009 Then
frm.SelTop = i '<-----------------------------
End If
rs.MoveNext
Next i
In order for me to get the email off the bottom of my continuous form, I used this much simpler code (as I avoided the RecordsetClone code)
Me.[email subform].Form.SelTop = Me.[email subform].Form.Count 'selects the last row
str = Me.[email subform].Form.Email 'capture the value of the last row
MsgBox str