i have my regular form with a continous form attached to it. Everything works fine untill the subform has no data.
Here is the subform with data
image1
IF there are no data in the subform, the form becomes like this.
image 2
I am using the following code in my on current event on my main form:
Function RefreshCOunt()
If Me.frmVerifyEquipmentSub.Form.Recordset.RecordCount = 0 Then
Me.frmVerifyEquipmentSub.Visible = False
Me.txtTotal = 0
Me.Refresh
Else
Me.frmVerifyEquipmentSub.Visible = True
Me.frmVerifyEquipmentSub.Form.Requery
Forms![test3].txtTotal = Me.frmVerifyEquipmentSub.Form.txtSum
Me.Refresh
Forms![test3].Refresh
End If
End Function
Private Sub Form_Current()
Call RefreshCOunt
End Sub
Private Sub Form_GotFocus()
Call RefreshCOunt
End Sub
Here is the code on my subform, THis function is run when my user clicks the save button.
Function RefreshCOunt()
If Me.txtCount = 0 Then
Forms![test3].SetFocus
Else
Me.Requery
Forms![test3].txtTotal = Me.txtSum
Me.Refresh
Forms![test3].Refresh
End If
End Function
I have a text behind the subform which says "NO Available Data to be shown"
2 problems in this situation are:
Empty form view as shown on image 2 (resolved as i had linked the record source of the main form to the table also used in subform)
Hide the subform once the user has verified all the equipment.
-currently my code only works the first time the form is loaded.
-the subform does not hide once the user has verifed all the equipment
I know how to do this in report with the HAsData function but i am not sure how i can do so in Forms.
It sounds like your query for the main form has an inner join to the table (or a query with this table) for the subform.
Remove this join or change it to an outer join.
Related
I have a command button on a form which opens a separate form to only a new record that matches the Contact ID of the current form. This second form exists so I can establish a relationship between my contact and a new group, which is a one-to-many relationship as each contact can be in more than one groups, but some contacts aren't in any group.
Some users will be assigning existing contacts to a new group, and some users will be adding new contacts to an existing group, so I want the form establishing relationships between contacts and groups to exist independently from either the contacts form or the groups form. This is why I didn't simply create a subform on the contacts form. I don't know which direction my users will be coming from when editing relationships, and ideally I'd like to set up a system where the form the user is coming from will create a new record with one of the two fields (contact and group) auto-populated depending on the "parent" form. That is, if my user is accessing the relationships form from a contact form, I will see a new record with the ContactID filled in and I can select a group. Similarly, if a user is accessing the relationships form from a group form, that group will be filled in and the user will select a contact.
I have the following subroutine On Click of my command button on the contacts form:
Private Sub CmdAssignContactGroup_Click()
DoCmd.OpenForm "frmContacts_Groups", , , , acFormPropertySettings
[Forms]![frmContacts_Groups]![ContactID] = [Forms]![frmContacts]![ContactID]
End Sub
This works for any one contact, but when I move to a different contact without closing the contact form, the filter criteria remain the same and the command button brings me to the record in the contacts/groups form attached to the previous contact.
How can I tell the command button to reset after I change records within the contact form?
This answer uses the DefaultValue and the Locked property of a Control to pin the ContactID (if form is opened by frmContacts) or the GroupID (if form is opened by frmGroups) to the opening form values.
I assume two ComboBoxes in frmContacts_Groups that are bound to their IDs, but display the names of Client/Group, named cboContactID and cboGroupID.
Code in frmContacts_Groups:
Private Sub Form_Load()
Dim OpeningID As Long
Select Case Me.Openargs
Case "frmContacts"
OpeningID = Nz(Forms!frmContacts!ContactID,0) ' Nz to prevent errors on empty opening form
Me.Filter = "ContactID = " & OpeningID
Me.cboContactID.DefaultValue = OpeningID
Me.cboContactID.Locked = True
Me.cboGroupID.DefaultValue = vbNullString
Me.cboGroupID.Locked = False
Case "frmGroups"
OpeningID = Nz(Forms!frmGroups!GroupID,0)
Me.Filter = "GroupID = " & OpeningID
Me.cboContactID.DefaultValue = vbNullString
Me.cboContactID.Locked = False
Me.cboGroupID.DefaultValue = OpeningID
Me.cboGroupID.Locked =True
Case Else
Me.cboContactID.DefaultValue = vbNullString
Me.cboContactID.Locked = False
Me.cboGroupID.DefaultValue = vbNullString
Me.cboGroupID.Locked =False
Me.Filter = vbNullString
End Select
Me.FilterOn = Len(Me.Filter) > 0
End Sub
Code for opening in frmContacts:
Private Sub CmdAssignContactGroup_Click()
If Not CurrentProject.AllForms("frmContacts_Groups").IsLoaded then
DoCmd.OpenForm FormName:="frmContacts_Groups", OpenArgs:="frmContacts"
Else
Msgbox "frmContacts_Groups is allready opened! What should we do?"
End If
End Sub
Your where-condition does not relate to the form to be opened but to the currently open form, as you reference ContactID with [Forms]![frmContacts_Groups]![ContactID]. I.e. you pass the result of the comparison of the currently open forms (True or False) instead of passing a condition that will be evaluated by the opening form.
You must pass it a condition as text like "ContactID = 10".
Dim condition As String
condition = "ContactID = " & Me!ContactID
DoCmd.OpenForm "frmContacts_Groups", WhereCondition:=condition
Since ContactID is on the same Form, you can access it with Me!ContactID. It also looks like you are erroneously passing the condition as the WindowMode parameter.
Also make sure the properties of the frmContacts_Groups Form are
AllowEdits = Yes
AllowDeletions = No
AllowAdditions = No
and most important!
DataEntry = No
If DataEntry is Yes, then the form will jump to the new record!
Before opening the form, you could also call
DoCmd.Close acForm, "frmContacts_Groups"
to make sure the previous edits of a possibly still open form do not interfere with the new edits.
I would like to have a button to open a form and then run VBA code on that form. So either using Form_Load or and intermediate module. Does anyone know how to do this?
Thanks
Use OpenArgs.
First form:
DoCmd.OpenForm "SecondForm", OpenArgs:=Me.Name
Second form:
Private Sub Form_Load()
If Me.OpenArgs = "FirstForm" Then
' Stuff
End If
End Sub
Declare a module level variable in the second form:
Dim Prev As Form
In the On Load-event of the second form (this sets a reference to the first form):
Set Prev = Screen.ActiveForm
And in the On Close-event:
Set Prev = nothing
Now you can check the name of the previous form with:
If Prev.Name = "..." Then
... your actions
End If
Furthermore you can check any property or field from the first/previous form this way. Prev is acting like Me now.
Lets say the new form be opened from a number of forms in your application;
Can more than one of the calling forms be open at any one time?
If not, use this:
Private Sub Form_Load()
If isLoaded("Form1") then
Form1_InstructionSet
ElseIf isLoaded("Form2") then
Form2_InstructionSet
...
End If
End Sub
Private Sub Form1_InstructionSet
...
End Sub
etc.
If more than one of the calling forms can be open simultaneously, you should parameterise the new form, as per #Andre's answer above.
I have a form with a subform. The subform is a continuous form so I can use conditional formatting. Using controls in a Tab Control, the values of the currently selected record on the subform are changed. So, I requery the subform to update the subform continuous form to show the updated data.
I can get the correct record re-selected in the subform, but the position of that record in the list jumps to the top of the subform's list instead of maintaining the position it was in prior to the update.
I have tried playing with the CurrentSectionTop values of the subform, but I am not able to correctly maintain the user's selection position within the subform after requerying the subform.
Is there some way to get the current position of the subform's continuous form's scrollbar position and then set that position in code after the continuous form is requeried? (Stephen Lebans' code for doing this (see: http://www.lebans.com/SelectRow.htm) does not work for me because I'm using Access 2013 and his code won't convert to Access 2013).
Here's a sample of what the subform continuous form display might look like to begin with while Record 7 is the current record selected:
{start of continuous form view}
[ ] Record 3 in continuous form view
[ ] Record 4 in continuous form view
[ ] Record 5 in continuous form view
[ ] Record 6 in continuous form view
[>] Record 7 in continuous form view
[ ] Record 8 in continuous form view
[ ] Record 9 in continuous form view
{end of continuous form view}
{tab control displays down here below the continuous form subform}
After the subform is requeried, here is what the subform continuous form display looks like, but I want the display to look the same as above; the display should not put Record 7 as the top record in the continuous form view since it was the 5th record down in the view originally so I want it to be the 5th record down after the requery:
{start of continuous form view}
[>] Record 7 in continuous form view
[ ] Record 8 in continuous form view
[ ] Record 9 in continuous form view
[ ] Record 10 in continuous form view
[ ] Record 11 in continuous form view
[ ] Record 12 in continuous form view
[ ] Record 13 in continuous form view
{end of continuous form view}
{tab control displays down here below the continuous form subform}
I couldn't get Wayne G Dunn's solution working, but I came up with this alternative. It's not wonderful, but it worked for me.
The basic idea is that each record in the continuous form has a position (ie top record showing on the screen is in position 1, regardless which actual record it is). You have a table that relates those positions, to the Form.currentSectionTop property of each record in the form, so you can figure out what position the current record is in. Then it's relatively straightforward to return to that position after the requery.
Create the table with the positions - this needs to run on startup or somewhere - might need to be more frequent if the user can resize or anything might change the number of records that could be shown in the continuous form.
Public Sub Setup_Positions()
Dim sql As String
Dim Position As Long
Dim currentSectionTop As Long
Dim lastSectionTop As Long
sql = "DELETE FROM tblRecordPosition"
currentdb.execute sql
DoCmd.GoToRecord , , acFirst
Position = 1
Call Set_NoUpdate
With Forms("frmMain").Controls("frmContinuousSubForm").Form
currentSectionTop = .currentSectionTop
Do While currentSectionTop <> lastSectionTop
'record previous sectiontop
lastSectionTop = currentSectionTop
'write it into the table
sql = "INSERT INTO tblRecordPosition (Position, CurrentSectionTop) " & _
"SELECT " & Position & ", " & _
currentSectionTop
CurrentDb.Execute sql
'update to next position and record the 'last' one, move to next record. When we've run out of visible ones, the last and current will be the same.
Position = Position + 1
DoCmd.GoToRecord , , acNext
'get new current sectiontop
currentSectionTop = .currentSectionTop
Loop
End With
Call Set_NoUpdateOff
End Sub
Set up global variables and a couple of functions to maintain them. The 'NoUpdateRequired' variable is optional - I use it to prevent unnecessary stuff running all the time.
Public NoUpdateRequired As Boolean
Public Position As Long
Public Sub Set_NoUpdate()
NoUpdateRequired = True
End Sub
Public Sub Set_NoUpdateOff()
NoUpdateRequired = False
End Sub
Create this function to convert between the property you can measure, and the actual position:
Public Function Get_Position(Optional InputCurrentSectionTop As Long) As Long
Dim currentSectionTop As Long
Dim Position As Long
If InputCurrentSectionTop > 0 Then
currentSectionTop = InputCurrentSectionTop
Else
currentSectionTop = Forms("frmMain").Controls("frmContinuousSubForm").Form.currentSectionTop
End If
Position = Nz(ELookup("Position", "tblRecordPosition", "CurrentSectionTop = " & currentSectionTop), 0)
Get_Position = Position
End Function
In the current event of the continuous form, you need this:
Private Sub Form_Current()
If NoUpdateRequired = False Then
Position = Get_Position
End If
End Sub
And finally, in the bit where you want your refresh to happen, you need this:
Public Sub Refresh_ContinuousSubForm()
'All this problem goes away if you can use Refresh instead of Requery, but if you have a few things editting the underlying table, you must use requery to avoid 'another user has changed the data' errors.
'However, this then causes the form to jump
'back to the first record instead of keeping the current record selected. To get around this, the following has been employed:
'the .seltop property allows you to select the top selected record (in most cases, only one record is selected). This is recorded before the refresh, and
'the form set back to that after the refresh. However, this puts the selected record at the top of the screen - confusing when you're working down a list.
'The .currentSectionTop property measures the number of twips from the selected record to the top of the screen - and correlates to which position in the list
'of 25 records in the bottom pane. tblRecordPosition converts between the twips to the actual position (recorded when the database is opened).
'The key to all this is that going back to the right record using .seltop only puts the record at the top of the screen IF the record wasn't already visible on the screen.
'But GoToRecord, if used when you're already at the top of the screen, will push the records down the screen as you move backward (upward) through them.
'So we go to the right record, and it will probably be at the top of the screen because of the requery. Then we push them down the screen back to the original position
'using GoToRecord, but now we're on the wrong record. Then we return to the right record using .seltop, and because it's already on the screen, it won't move position.
Dim startSeltop As Long
Dim newSectionTop As Long
Dim newPosition As Long
Dim startPosition As Long
Dim recordsToMove As Long
'Also global variable Position (long) which is recorded in the form's current event
Call Set_NoUpdate
startPosition = Position
With Forms("frmMain").Controls("frmContinuousSubForm").Form
.Painting = False 'stops the screen flickering between
startSeltop = .SelTop 'records which record we're on. Position represents where that was showing on the screen.
.Requery 'does the requery
.SelTop = startSeltop 'sets us back to the correct record
newSectionTop = .currentSectionTop 'measures in twips which position it's in (usually 1)
newPosition = Get_Position(newSectionTop) 'converts that to the position
recordsToMove = startPosition - newPosition 'calculates how many records to move - moving records using GoToRecord moves the position as well
If recordsToMove > 0 Then
DoCmd.GoToRecord , , acPrevious, recordsToMove 'moves back enough records to push our record to the right place on the screen
End If
.SelTop = startSeltop 'now sets back to the correct record
.Painting = True 'turns the screen painting back on
End With
Call Set_NoUpdateOff
End Sub
The following code is a subset of the code found on Stephen Lebans' website: http://www.lebans.com/SelectRow.htm . That link has a link to a zipped version of an Access database with all the code to handle multiple scenarios, however the database is an older version and needs to be converted. Mr Leban's code does far more than what is included here, but I am only using this code to solve one specific issue.
(A) Create a Class Module named 'clsSetRow' and paste in the following code:
Option Compare Database
Option Explicit
Private mSelTop As Long
Private mCurrentSectionTop As Long
Public Property Get SelTop() As Long
SelTop = mSelTop
End Property
Public Property Let SelTop(x As Long)
mSelTop = x
End Property
Public Property Get CurrentSectionTop() As Long
CurrentSectionTop = mCurrentSectionTop
End Property
Public Property Let CurrentSectionTop(x As Long)
mCurrentSectionTop = x
End Property
(B) In your module for your form, include the following at the top:
Private SR As clsSetRow
Dim lCurRec As Long
(C) Add the following Event Handlers and code:
Private Sub Form_Load()
Set SR = New clsSetRow
End Sub
Private Sub Form_Current()
' This event can be called during the Form Load event prior to the init of
' our class so we must test for this.
If Not SR Is Nothing Then
SR.SelTop = Me.SelTop
SR.CurrentSectionTop = Me.CurrentSectionTop
End If
End Sub
Private Sub Form_AfterInsert() ' OR JUST USE THE BEFOREINSERT
lCurRec = Me.CurrentRecord
'Debug.Print "After Insert, Current: " & Me.CurrentRecord
End Sub
Private Sub Form_BeforeInsert(Cancel As Integer) ' OR JUST USE THE AFTERINSERT
lCurRec = Me.CurrentRecord
'Debug.Print "Before Insert, Current: " & Me.CurrentRecord
End Sub
(D) Wherever you want to reposition (i.e. after a REQUERY), add the following line of code:
DoCmd.GoToRecord acDataForm, Me.Name, acGoTo, lCurRec
(E) To test this, just add a command button that will 'Requery and then GoToRecord'.
NOTE: Simply scrolling up or down using the scrollbar will NOT save the row of where you are! You need to establish a 'current record' for this to reposition.
Good Luck! And thank you Stephen Lebans for the code!
I have a Main Form with 3 fields, where the rep would enter the Property, Room Type, and the requested Check-in Date - however on the same form i have a subform displaying Property, Room Type and Check-in Dates NOT AVAILABLE. If a rep enters data in those 3 fields and ALL 3 match what is in the NOT AVAILABLE subform - what code (either VBA or On Lost Focus etc) can i use to look up those values in the subform (so that if it matches what's in the subform it will not allow for submission) and also popup an ERROR message that the "Property, Room Type and Check-In Date you selected are not available" ?
(Main Form data entered will go into a table let's say RoomRequestTable and Not Available subform displays data from another table RoomNotAvailableTable)
Below is some sample code utilizing the BeforeUpdate event. I'm assuming you want to trigger this from any of the fields, so I wrote a sample function that returns false if all of the fields match. We access the subform by calling Me.[SubFromName].Form.
Private function validate() As Boolean
Dim sbfrm As Form
Set sbfrm = Me.SubFormName.Form
If Me.FieldName = sbfrm.FieldName And Me.FieldName2 = sbfrm.FieldName2 And Me.FieldName3 = sbfrm.FieldName3 Then
MsgBox "you can't do that", vbExclamation, "Sorry kid"
'return false
validate = false
Else
'return true
validate = true
End If
End Function
Private Sub ControlName1_BeforeUpdate(Cancel As Integer)
Cancel = Not validate
If Cancel then
Me.ControlName1.Undo
End If
End Sub
Private Sub ControlName2_BeforeUpdate(Cancel As Integer)
Cancel = Not validate
If Cancel then
Me.ControlName2.Undo
End If
End Sub
Private Sub ControlName3_BeforeUpdate(Cancel As Integer)
Cancel = Not validate
If Cancel then
Me.ControlName3.Undo
End If
End Sub
I'm trying to modify existing code to add a popup box. This popup box is another form, and when a user clicks on a button on this form, I want a textbox on the base form to be populated. After that the popup disappears and then I need to access that value from the textbox on the base form.
The sequence should be:
Base form button click calls modal popup
Click in button on popup saves value to base form's textbox, then returns control.
Base form then uses this value to do something.
Base Form
Sub base()
DoCmd.OpenForm "PaperType", , , , , acDialog
MsgBox Me.TheAnswer 'This line gives a null error
End Sub
Popup Form
Private Sub btnRolls_Click()
'Me.Tag = 1
Forms!ReceiptDetail_sfrm!TheAnswer = 1
Me.Visible = False
End Sub
Private Sub btnSheets_Click()
'Me.Tag = 4
Forms!("ReceiptDetail_sfrm").TheAnswer = 4
Me.Visible = False
End Sub
You can do it that way, you probably need something like:
Private Sub btnRolls_Click()
Forms!ReceiptDetail_sfrm!TheAnswer = 1
Forms!ReceiptDetail_sfrm.Refresh
Me.Visible = False
End Sub
I've done what you're doing many times. I'm not at work to check my code right now for an example, but it's doable.
What I tend to do is hide the popup, then read from it:
Main Form:
Private Sub base()
DoCmd.OpenForm "PaperType", , , , , acDialog
'code waits for modal hide here
Me!TheAnswer = Forms("PaperType").SomethingOnThatFormThatStoresTheValue
DoCmd.Close acForm, "PaperType", acSaveNo
MsgBox Me.TheAnswer
End Sub
Popup
Private Sub btnRolls_Click
'may be hidden control
Me.SomethingOnThatFormThatStoresTheValue = TheValueToRead
Me.Visible = False
End Sub
Make sure that either A) Your popup can't be closed from the form itself, or B) Your calling code will handle the error of trying to read something that's no longer loaded (or both).
Apparently the correct answer is: "Don't do it this way!". Instead of trying to pass data between the forms, which is a pain, I made a table in Access which only has 1 field in 1 record. Then I store the value in there using DoCmd.RunSQL, and retrieve it from the other form using DLOOKUP().