I am trying to make a memo field be spell checked when it leaves the field, and when it leaves the subform to the main form. I want to have this done automatically without having a click button, as I need the data to be as accurate and error-free as possible.
In the main form I have used this code to spell check a title field once you move off the field.
Private Sub DetailedTitle_Exit(Cancel As Integer)
'This code works to spellcheck the title after leaving.
With Me!DetailedTitle
If Len(.Value) > 0 Then
DoCmd.SetWarnings False
.SelStart = 1
.SelLength = Len(.Value)
DoCmd.RunCommand acCmdSpelling
.SelLength = 0
DoCmd.SetWarnings True
End If
End With
End Sub
This works well, as users will 99% of the time moving from this field to another field in the main form. This does not work when I use in on the subform even when I use the on exit field event AND the on exit subform event. It seems to work, but if you click around it will actually give an error. I wasn't smart enough to note the error, and I have since removed the code.
I would also not be opposed if I could get it to loop thought the subform, as it is a continuous form, but only that one field.
Any advice or tips would be welcome. I just recently learned of the Do.Cmd.RunCommand acCmdSpelling, but there seems to be a lack of content in regards to subforms. Thanks.
I did some research: http://www.pcreview.co.uk/threads/run-the-spell-check-through-code.1636292/
One user mentions that the control you want to check has to be in focus.
Sub subformMemo_OnExit()
Me.subformMemo.setfocus
DoCmd.RunCommand acCmdSpelling
End Sub
On the other hand this code creates the problem that you cannot leave the control because you will always setfocus on it again.
Does this help?
Related
New to Access (still), have only basic VBA skills.
I've got 3 subforms (subfrm_PackingSteps1 , subfrm_MetalDetection and subfrm_Weights - the first 2 are continuous and the other one is single form) within a main form (frm_daily_packing_record) that users go through and input data. The user should be able to input data in no particular order, and only at the end there would be a button to confirm that the user is ready to save this form.
I'd like to have this button on the main form that checks each control (in main form and subforms) for empty values. I found and adjusted a code to check the recordset of one of the continuous forms (see below), but I can't figure out:
how to include a code that checks each control instead of manually adding all of them (I've used a function before that utilises the Tag property, but can't add it to this)
how to keep the button in the main form while checking the controls/recordsets in the other subforms.
Thanks in advance.
Private Sub ConfirmBtn_Click()
Dim blnSuccess As Boolean
blnSuccess = True
Me.Recordset.MoveFirst
Do While Not Me.Recordset.EOF
If IsNull(Me.pc) Or IsNull(Me.InnerP) Then
blnSuccess = False
Exit Do
End If
Me.Recordset.MoveNext
Loop
If blnSuccess = True Then
MsgBox "You may proceed to save this record"
Else
MsgBox "You still have some empty fields to fill in!", vbCritical + vbOKOnly, "Empty Fields!"
End If
End Sub
I personally don't like this because it is too code-heavy and can easily break when the form is modified.
Instead may I suggest doing it slightly different, for each field have vba code .ondirty or .onupdate and do the validation checking right as the user is actually on that field.
This has 2 benefits, it is creating the validation when you are creating each form field and it STOPS the user right when their first mistake or bad data is entered. The last thing I want is to enter 50 fields, scroll to the bottom, submit fails then scroll back and try to find where the mistake was. If validation is done while the user it doing the actual data entry, when you get to the bottom you should have valid data and the submit should succeed without further testing.
Less code to debug and timely messages to the user if an error is caught!
I'm building a userform where it has two text boxes to enter dates. Once the date is entered, I'm validating them when the Exit event fires. If the validation turns up that the user data isn't what is needed, the user is notified, the text box is cleared, and the focus is returned back to the textbox.
The issue comes if the user uses the mouse to select outside of the box, rather than Tab. If Tab is used, it fires perfectly and as expected, and the field is cleared and the focus is returned. If the mouse is used, it doesn't fire. According to this article, this is expected behavior (It's for Access, but I didn't see the similar relevant MSDN article for Excel.)
So instead I tried the AfterUpdate event. However, SetFocus doesn't work within an AfterUpdate event, I'm assuming because of the chain of events as outlined in the response to this question. Thus, I don't have a way to return the focus back to the textbox after it has fired. That thread had a suggestion as an alternate answer to SetFocus to another control and come back as a workaround, but that doesn't work for me, so I assume that may be an Access-specific workaround.
My last option I've considered is having the AfterUpdate event just call the Exit event, however the Exit event has a required argument (ByVal Cancel As MSForms.ReturnBoolean), which is how you cancel out of the exit and return the user to the textbox. As such, there isn't a value that you can pass to it that doesn't throw an error that I can find (the closest I found was passing Nothing but it failed out when trying to set it to True later to cancel the exit.)
Is there a way to achieve what I'm looking for here, or should I just stick to the AfterUpdate and ignore the SetFocus I'm trying to achieve?
I know that this was answered a few months back but giving an alternative solution. For any one who finds this question.
For validation of Excel Textbox data use the BeforeUpdate Event, it fires before the AfterUpdate Event and has the ability to prevent losing Focus on the control.
Rework the sample code to your requirements
Remember Cancel = True stops the control update to the control and it remains in focus.
Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
Cancel = doValidation(Textbox1.Text) 'Validation Route
End Sub
Private Function doValidation(strText) as Boolean
'Do Validation
if Valid then
doValidation = False
Else
msgBox "Not Valid"
doValidation = True
End if
End Sub
In my opinion this is the easiest way to validate an input on an Excel Userform Textbox
I can't right now find the correct MSDN article at this time, all Google wants to return is Access Results.
Exit event works on all the mouse clicks which fire up Enter for another Control on the Form. But When you click, directly on the form instead of any other control, nothing happens.
Here, use the ActiveControl property to determine about the last control you were in, before exiting and moving to user form.
Sample Code, rework it according to your requirement.
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Call doValidation(Me.TextBox1.Text) '/ Validation Routine when user leaves TextBox
End Sub
Private Sub UserForm_Click()
'/ If user clicked on the user form instead of ay other control
If Me.ActiveControl.Name = Me.TextBox1.Name Then
Call doValidation(Me.TextBox1.Text) '/ Validation Routine when user leaves TextBox
End If
End Sub
Private Sub doValidation(strText)
MsgBox strText
End Sub
Advise: For Date inputs, use DateTimePicker instead of TextBox, will save you from alot of trouble in future.
In VBA you can call any defined sub or function with the word Call Subname:
e.g. Call Textbox1_exit(params)
However from the somewhat confusing description I believe your problem is that you limit yourself to just a few event functions. I would suggest exploring all event functions and see which one is a good fit for your event fire.
Here is a list of events and their sequences in Access VBA:
https://msdn.microsoft.com/en-us/library/office/jj249049.aspx
and the order of form events:
https://support.office.com/en-us/article/Order-of-events-for-database-objects-e76fbbfe-6180-4a52-8787-ce86553682f9
I think for your application from the description you gave the lost_focus or got_focus for certain components might be useful.
Furthermore, you can manually set the focus to almost any component it is a built in method: compName.SetFocus()
Hopefully I can explain what I want to do well enough...here it goes...
I have a data entry form...the user will be entering employeeIDs. Once in normal operation, most people will be entering only their own EmpID, and they should know it, so this won't be a big problem 99% of the time once this DB goes live.
However, I need some temps to enter historical data from paper sheets into the DB. These people will not know anyone else's EmpID. I'd like to set the Student field's OnDblClick event in the subform's datasheet to open a small form with a combo box. The combo box has a list of all Employee Names, and is bound to the EmpID. Once this user enters the name, and selects the person, I have a button they can click to return to the datasheet.
I can use a function to launch the form, no problem there. But how do I return the EmpID to the field in the datasheet that was double clicked?
When user double clicks in the Student field...I want the next form to appear, and then once they type in the name and select the correct person...and then click Found Them!...I need that bound value to return.
I'd love to say I have code to share right now...but the only code I have is to launch the look up form. I'm brain farting on how to pull the value back down.
The way to do this to launch your little dialog form as “acDialog”. This will cause the calling code to WAIT.
And then the “magic” part is when they click on “Found Them” you do NOT close the popup form, but simply set the form’s visible = false. This has the effect of the calling code that popped up this form that halted to continue (the form is kicked out of dialog mode when you do this). So now your calling code continues.
So your code will look like this:
Dim strF As String ' name of popup form
strF = "frmPopUp"
' open form, wait for user selection
DoCmd.OpenForm strF, , , , , acDialog
' if for is NOT open, then assume user hit cancel buttion
' (you should likly have a cancel button on the form - that cancel buttion will
' execute a docmd.close
If CurrentProject.AllForms(strF).IsLoaded = True Then
' grab the value of thee combbo box
strComboBoxValue = Forms(strF)!NameOfComboBox
DoCmd.Close acForm, strF
End If
As noted, the code behind the Found Them button DOES NOT do a close form, but sets forms visible = false (me.Visible = false), and this trick allows the calling code to continue at which point you can examine any value on the form. Remember to then close the form after you grab the value.
It looks like your data table is in a subform so there is a little more work but it does not have to be as complex as the above solution if you don't want it to be. #Andre451 was close but you need the extra step of identifying the form and subform. For the purpose of demonstration let's call the form Attendance and subform Entry then I'll call the second form LookUp. So the code for your double click in the subform field will of course look something like this :
Private Sub Student_DblClick(Cancel As Integer)
DoCmd.OpenForm "LookUp"
End Sub
You really don't need anything else fancy there. For the button on "LookUp" you will put this:
Private Sub Command2_Click()
Forms![Attendance]![Entry].Form![Student] = Forms!Lookup!Student
DoCmd.Close acForm, "LookUp"
End Sub
And that should get you what you want without any overhead or having to leave any ghosts open.
I have a main form with a tab control containing multiple subforms. I need to be sure that the data in a subform is saved when the user switches tabs. The problem is that DoCmd.RunCommand acCmdSaveRecord seems only applies to the current form so it doesn't save the data in the subform.
I have tried different events on the subform such as deactivate, OnLostFocus etc but they don't fire until another field somewhere else gets the focus.
The ideal solution would seem to be to put something on the OnChange event of the tab control to be sure that all the data is saved. That is my question, how to do I save the record in a subform?
In Access, the default is to save, so unless you have done something pretty complicated to prevent this, moving the focus from a subform will automatically save the record. You can test this by adding a record, moving from the subform, and then checking the table.
You don't have to do anything at all, as the subform is saved as soon as it loses focus (when the tab control changes).
But as an exercise, I've outlined the code you'd write if you needed to.
You can save any form by setting it's .Dirty property to False. For something like this that's going to run a lot, I think I'd write a sub to walk through the subforms, check if any are dirty, and save the dirty ones. Something like this:
Public Sub SaveSubFormsOnTab()
Dim pge As Control
Dim ctl As Control
For Each pge In Me!ctlTab.Pages
Debug.Print pge.Name
For Each ctl In pge.Controls
If ctl.ControlType = acSubform Then
If ctl.Form.Dirty Then
ctl.Form.Dirty = False
End If
Debug.Print ctl.Name
End If
Next ctl
Next pge
Set ctl = Nothing
Set pge = Nothing
End Sub
Now, that's actually quite inefficient in cases where you have lots of controls on your tab control that aren't subforms. If your tab has nothing but subforms, it will be fairly efficient. In either case, it's much more efficient to use a custom collection populated in the form's OnLoad event, and then you'd walk that collection that includes nothing but your tab control's subforms, and save any that are dirty.
Either of these is preferable to using the OnChange event of the tab, because each time you add a tab page with a subform or change the name of a subform control, you'd have to alter the OnChange event.
I was having a similar issue where I needed various code to run in the subform and values in the main form - that were based on values in the subform - to be recalculated. Here is what finally worked:
This is an example with a main form named 'frmCustomers' containing a subform 'sfmTransaction', which in turn has a subform called 'sfmOrderLine'. The forms have a button 'cmdSave' that is only visible when the user clicks the 'cmdEdit' button (the purpose of which is to lock all the controls until the user clicks the edit button, and then to re-lock them when he clicks save):
On the main form ('frmCustomer') go the subform's exit event, and add 'me.recalc' twice, like this:
Private Sub sfmTransaction_Exit(Cancel As Integer)
If (Not Form_sfmTransaction.NewRecord And Form_sfmTransaction.cmdSave.Visible) Or (Not Form_sfmOrderLine.NewRecord And Form_sfmOrderLine.cmdSave.Visible) Then
'To save subforms
Me.Recalc
Me.Recalc
End If
End Sub
In the subform ('sfmTransaction') go the subform's subform's exit event, and add 'me.recalc' twice, like this:
Private Sub sfmOrderLine_Exit(Cancel As Integer)
If Not Form_sfmOrderLine.NewRecord And Form_sfmOrderLine.cmdSave.Visible Then
'To save subform
Me.Recalc
Me.Recalc
End If
End Sub
Hope this helps.
Setting the dirty property to false may force Access to save the data to the database, but it bypasses the before_update event. This means that if you've used this event for validation or other purposes, you can now have bad data in your database.
I have a UserForm with some textbox entry fields on it that are enabled/disabled by a checkbox. When a checkbox is clicked to check it, I'd like to move the focus into the now-enabled textbox.
The textbox is the next control after the checkbox in the tab order, so it seems like using the tab order to find the appropriate textbox would be a good idea.
But... how can I find the next control in the tab order after a given control? Is there a method to do that, or do I have to enumerate all the controls and figure it out for myself?
I appreciate that this comes under the heading of "enumerating all the controls" but it's pretty simple and I attach the code for completeness:
Private Sub CheckBox1_Click()
Dim ctl As Control
For Each ctl In Me.Controls
If ctl.TabIndex = Me.ActiveControl.TabIndex + 1 Then
ctl.SetFocus
Exit For
End If
Next
End Sub
As a different way of looking at this.
Can you not rather use the textbox you want focussed, and set that name in the checkbox.tag
then in you vba code use
DoCmd.GoToControl CheckBox.Tag
Where the CheckBox.Tag is the Textbox.Name?
EDIT:
OK, I found this
SendKeys "{Enter}", True
at VBA code for moving to next control? It must be eeeasy
I had trouble with
SendKeys "{Enter}", True
With a little experimentation I found this works
SendKeys "{TAB}", True
One caveat...if you're in the VBE stepping through the code, and watching it on the form, SendKeys is executed is executed in the code. Confused the heck out of me at first why my code started to look odd, e.g. extra spacing and extra lines!
With all due respect to the kind advice offered thus far, this issue shouldn't be addressed through programming when there's a way to address it within the userform itself.
Click on the userform itself, then right click and select "tab order". You can then move each element within the userform to whatever position you want without having to resort to complicated and unstable programming tricks.