Access cascading combobox autofill When left with one option - vba

I have ~50 cascading comboboxes on a form that I want to autofill if only one option is left. I found some code that worked for cboTwo (second combobox), but the other comboboxes aren't filling in automatically. I still have to use the drop down menu to make a selection. Is there any way that I can make all of my comboboxes autofill if there is only one option left in the drop down? I'd prefer some sort of macro help because that's what I've been using until now, but I'll use VBA if necessary. Thank you for all of your help!
Private Sub cboOne_AfterUpdate()
Me.cboTwo.Requery
If Me.cboTwo.ListCount = 1 Then
With Me.cboTwo
cboTwo.SetFocus
cboTwo.Value = cboTwo.ItemData(0)
End With
End If
End Sub
Private Sub cboTwo_AfterUpdate()
Me.cboThree.Requery
If Me.cboThree.ListCount = 1 Then
With Me.cboThree
cboThree.SetFocus
cboThree.Value = cboThree.ItemData(0)
End With
End If
End Sub

The problem may be with misunderstanding Access control events. Unlike in many other languages, control events are rarely triggered by changes made in VBA code. In other words, the event handler cboTwo_AfterUpdate() is not automatically called when cboTwo.Value = cboTwo.ItemData(0) is executed in code, so there will not be any automatic event cascade. Try the following pattern:
Private Sub cboOne_AfterUpdate()
Me.cboTwo.Requery
If Me.cboTwo.ListCount = 1 Then
With Me.cboTwo
cboTwo.SetFocus
cboTwo.Value = cboTwo.ItemData(0)
cboTwo_AfterUpdate
End With
End If
End Sub
Private Sub cboTwo_AfterUpdate()
Me.cboThree.Requery
If Me.cboThree.ListCount = 1 Then
With Me.cboThree
cboThree.SetFocus
cboThree.Value = cboThree.ItemData(0)
cboThree_AfterUpdate
End With
End If
End Sub

Related

Event handling class doesn't fire unless I break userform initialization

This is a follow-up to the following question:
Can't set Userform.KeyPreview to true
To recap: the goal is to build a form with some command buttons and a frame containing check boxes. The check boxes are dynamically populated at userform_initialize in the frame so the user can scroll through them. My problem was with keyboard shortcuts. It wasn't possible to brute force write KeyDown handlers for each of the checkboxes because I don't know which ones will exist. Unfortunately, Excel doesn't support KeyPreview so I had to mock up my own version. Thank you to #UGP for giving me promising avenues that seem to work, but not quite...
First, this is my class module called clsReasonPickKP. I create a new instance for each checkbox to listen for KeyDown events:
Option Explicit
Dim WithEvents vChkBx As MSForms.CheckBox
Friend Sub initializeListener(cControl As control)
Set vChkBx = cControl
End Sub
Private Sub vChkBx_KeyDown(ByVal keyCode As MSForms.ReturnInteger, ByVal shift As Integer)
frm2.keyChooser keyCode
End Sub
The line frm2.keyChooser keyCode launches a quick sub located in the userform code module. Code below:
Public Sub keyChooser(ByVal keyCode As MSForms.ReturnInteger)
Select Case keyCode
Case vbKeyEscape: cancelBtn_Click
Case vbKeyReturn: completeDecision_Click
Case vbKeyN: customizeNote_Click
Case vbKeyS: resetDecisionNote_Click
Case vbKeyR: chkRefGrnds_Click
End Select
End Sub
I've copied the relevant part of the UserForm_Initialize sub below. The loop creates the checkboxes and an event listener for each.
Sub UserForm_Initialize()
Dim x As Long, maxWidth as Long
Dim cControl As control
Dim keyPreviewCollection As New Collection
Dim keyPreviewer As clsReasonPickKP
For x = 1 To dTbl.Rows.Count - 1
Set cControl = chkBoxFrame.Controls.Add("Forms.CheckBox.1", "chkBox" & x, True)
With cControl
.AutoSize = True
.WordWrap = False
.Left = 10
.Top = 16 * x - 12
.Caption = dTbl(x, 1).Value
If .Width > maxWidth Then maxWidth = .Width
End With
Set keyPreviewer = New clsReasonPickKP
keyPreviewer.initializeListener cControl
keyPreviewCollection.Add keyPreviewer
Next x
'Additional initialization code here
End Sub
The odd thing is that unless I break code some time after keyPreviewCollection.Add keyPreviewer, the listener doesn't seem to handle the event. For example, if I set a break point at Next x or for x > 1 and then complete initialization, then when the form is finished initializing and appears the listener calls keyChooser and all is well; if I don't break code like that, it doesn't trap the event or call the sub, etc.
To trouble-shoot, I've tried not adding keyPreviewer to the collection, and then the listener also doesn't work, no matter if or when I break. It seems adding the object to the collection, and being in code break mode after adding it to the collection, somehow makes the listener trap the event.
Also interesting, if I put a breakpoint in the vChkBx_KeyDown module, it breaks when the event is raised (assuming an appropriate break as described above). After I then run the code, however, it stops handling the KeyDown event when its raised.
In case it helps, I'm currently working in Excel 2010.
Does anybody have any idea what's going on? Any idea how to solve this, even with a different code approach?
Thank you as always for everybody's help.
It turns out that the problem was so simple and right in front of my eyes. I just had to make the keyPreviewer and keyPreviewCollection variables public in my userform code module.
That still doesn't answer why breaking code execution after adding the object to the collection made VBA treat it as public, but just happy that it all works.
DoEvents might be the ticket. See the article below:
https://www.automateexcel.com/vba/doevents/

Microsoft Access applying 1 function to all fields automatically

I have a form that keeps track of assigned patient equipment. I have it set so that any changes made to text fields on the form automatically move down to the "comments" section of the form (this is done so that any changes made are documented in case the user forgets to manually document changes). I have a sub that I wrote that accomplishes this that I am currently calling for every single text field. This works but is messy.
Is there a way to apply the sub to all the fields in one procedure without calling it for every individual field? Code is below, please let me know if I can clarify anything.
Private Sub pPEMoveValue(sField)
'Moves the old field value down to the comments section automatically
Dim sOrigValue As String
Dim sCommentValue As String
sOrigValue = sField
sCommentValue = Nz(mPEComments, "")
Me.mPEComments = sCommentValue & vbNewLine & sOrigValue
End Sub
Private Sub sPEBatCharger_Dirty(Cancel As Integer)
pPEMoveValue (Nz(Me.sPEBatCharger.OldValue, ""))
End Sub
This is the solution I came up with to do what you are looking to do. I took advantage of the MS Access Tag system. You can add tags to your controls so you can sort of "Group" them.
First put the form in design view and adjust the tag for all of the fields you want to record to say "Notes".
Then in the Form's BeforeUpdate even you would add this:
Private Sub Form_BeforeUpdate(Cancel As Integer)
Call FindControlsForComments(Me.Form)
End Sub
Then you would use this function to find any fields that have the "Notes" tag and run it through the function you created:
Public Function FindControlsForComments(frm As Form)
Dim ctrl As Access.Control
For Each ctrl In frm
'If the control is tagged for notes
If ctrl.Tag = "Notes" Then
'If the old value is different than the current value
If Nz(ctrl.OldValue, "") <> Nz(ctrl.Value, "") Then
'Add to comment
Call pPEMoveValue(Nz(ctrl.Value, ""))
End If
End If
Next ctrl
End Function
You may have to adjusted this slightly to work with your system but this has worked well for me.

SetFocus inside a GotFocus procedure initiated by another SetFocus

Objective: Redirect focus from one command button to another using the first's GotFocus procedure.
Context: I have a form-independent procedure in a generic module that, on most forms, sets focus to the NewRecord button after saving the previous record. But on one form, I would like to redirect (based on certain conditions) focus back to the SignRecord button so the user can "sign" a second part of the same record (I may need this for other uses in the future). The target control is enabled and visible and can otherwise be focused and the original control can be focused when the redirect doesn't occur. Reference [2] below implies that this should be possible, though I'm not changing visibility of my controls.
Issue: When the conditions are met to redirect focus in the GotFocus procedure, it redirects as desired but the original (test) SetFocus call throws a "Run-time error '2110', Can't move focus to the control CommandNew".
What I've tried:
Exit Sub after my downstream SetFocus calls.
Call CommandSign.SetFocus in the hopes that it would make it happen outside the previous SetFocus process.
In a module,
Public Sub test()
Forms("TargetForm").CommandNew.SetFocus 'This gets the error '2110'
End Sub
In the 'TargetForm',
Private Sub CommandNew_GotFocus()
If IsNull(textDateTime) Then Exit Sub 'Works as expected
'I can see these two parts work. The framSign value changes
'and CommandSign gets focus
If checPPC And IsNull(textSigID_PPC) And framSign = 2 Then
framSign = 1
CommandSign.SetFocus
ElseIf checDAS And IsNull(textSigID_DAS) And framSign = 1 Then
framSign = 2
CommandSign.SetFocus
End If
End Sub
References:
[1]: SelectNextControl() a bad idea in a GotFocus event?
[2]: http://www.access-programmers.co.uk/forums/showthread.php?t=100071
I think your problem is that the call to Forms("TargetForm").CommandNew.SetFocus doesn't quite seem to, in fact, finish setting the focus to CommandNew until after Private Sub CommandNew_GotFocus() has finished executing. Because you've called another SetFocus before the first SetFocus could finish, there is a conflict that Access seems to be unable to cope with.
Whether or not that is the case, one thing is clear: the way you have your execution plan set up right now is unfortunately not going to work. You might try adding either a global variable or a public variable to each form that determines whether or not you should set your focus to CommandSign after you set the focus to CommandNew.
Ex. TargetForm:
Public boolSetCommandSignFocusInstead As Boolean
Private Sub CommandNew_GotFocus()
If IsNull(textDateTime) Then Exit Sub 'Works as expected
'I can see these two parts work. The framSign value changes
'and CommandSign gets focus
If checPPC And IsNull(textSigID_PPC) And framSign = 2 Then
framSign = 1
boolSetCommandSignFocusInstead = True
ElseIf checDAS And IsNull(textSigID_DAS) And framSign = 1 Then
framSign = 2
boolSetCommandSignFocusInstead = True
Else
boolSetCommandSignFocusInstead = False
End If
End Sub
Module:
Public Sub test()
Forms("TargetForm").CommandNew.SetFocus
If Forms("TargetForm").boolSetCommandSignFocusInstead Then
Forms("TargetForm").CommandSign.SetFocus
End If
End Sub

How do I load something when the page is selected?

I'm trying to make a form with a lot of pages. So far it works ok and I added the controls from the code. There are hundreds of them and it makes the app "heavy".
Now I'm adding all the stuff in the comboboxes in at Initialization but what I want to do is to load the Controls when I change the page.
So when I start the form the controls should be loaded for page 1. What I want to do is when I click on Page 2 of the Multipage to load it's components (instead of adding all of them at the initialization of the UserForm).
Thanks!
If you want to initialize each page only when it becomes clicked, you can track which has been clicked using something like;
Private mbInitialised() As Boolean
Private Sub UserForm_Initialize()
ReDim mbInitialised(MultiPage1.Pages.Count - 1)
SetupPage 0
End Sub
Private Sub MultiPage1_Change()
SetupPage MultiPage1.Value
End Sub
Private Sub SetupPage(index As Integer)
If (mbInitialised(index)) Then Exit Sub
mbInitialised(index) = True
MsgBox "init page " & index + 1
'//setup here
End Sub
I think this is an easier way to do it
Load UserForm: UserForm.MultiPage1.Value = 0: UserForm.Show
where the multipage value starts to "page1" as arrays starts from zero
if you want a different page change .Value = 0 into the page value - 1
ex:
Load UserForm: UserForm.MultiPage1.Value = 4 : UserForm.Show
this will take you to "page3" of multipage upon initializing userform
*edit:
misunderstood the question a bit, my code will take you to the page but not execute something when changing pages, use Alex's code instead

BeforeUpdate issue - Runtime (error 2115)

I have written the following query:
Private Sub Size_Sqft_BeforeUpdate(Cancel As Integer)
Me!Size_Sqft = Nz(Me!Size_Sqft, 0)
End Sub
But while removing the zero in the field to make it null, I am getting the following error:
Runtime error 2115
Macro and function set to before update and validation rule property for this field is preventing manual data entry screen for company from saving the data in the field.
You have to put that code in the AfterUpdate event of that field.
I know this is an old thread, and has already been answered, but there is another solution that doesn't require several writes back to your database. I'm adding it in case someone else comes across this question.
Private Sub ControlName_BeforeUpdate(Cancel as integer)
If isValid(Me.ControlName.Value) = False Then
Cancel = True
Me.ControlName.Undo
End If
End Sub
Private Function isValid(ByVal...) as boolean
' validate control value here
End Function