Access TextBox does not update during VBA loop, only after - ms-access

In access using VBA.
I have a quite simple situtation, and I see some different solutions online, but non of them works for me.
I have a form with a textbox that needs to be updated in a for loop.
I have reduced the script to the following:
For counter = 1 To 100
Application.Echo False
DoCmd.OpenQuery "Query1"
DoCmd.Close
Application.Echo True
strStatus = "Loop is " & counter & " % done"
Call dsp_progress_AfterUpdate
Me.Refresh
DoEvents
Next
And the sub thats called:
Private Sub dsp_progress_AfterUpdate()
Me.Dirty = False
End Sub
The textbox controlsource is the strStatus (Through a function).
Every loop takes about 4 seconds (The query), so it is not because its over in 2 ms.
It only updates when the for loop is finished.
The strange thing is, if i use the curser and manually click on the text box while the loop is running, it actually works.........
So the question is, how do i make it update "live" without having to click on the textbox with the mouse/curser? That is not convenient for a status display.
Help, please... :)
Best Regards, Emil.

I'm not sure what the root cause of it failing to update the text box is, but adding in one line fixed this issue for me. Setting focus to the textbox that you are updating (which is what you are doing when you click on it), causes it to update.
textBox.SetFocus
Add that in to your code before starting the loop.
Changing the code to the following should get rid of the flickering and the fact that the text is highlighted.
For counter = 1 To 100
Application.Echo False
DoCmd.OpenQuery "Query1"
DoCmd.Close
strStatus = "Loop is " & counter & " % done"
Call dsp_progress_AfterUpdate
Me.Refresh
TextBox.SetFocus
TextBox.SelStart = 0
TextBox.SelLength = 0
DoEvents
Application.Echo True
Next

Related

When click Cancel button on form, do not run On Exit code on a field

On my form, the first field has to be entered, so I have an On Exit command. Since it also checks to see if the value exists in my table, I want it to check right away - is it blank (thus require data to be put into field) OR does it already exist. My question has to do with the first part - is it blank.
On this form, I also have a Cancel button. When the user click Cancel, I want it to close the form without saving any of the data.
Problem - If the user doesn't enter anything in the first field and clicks Cancel, it's running the On Exit code requiring data to be entered into the first field - which they don't have to because they are cancelling. I'm getting a Run-time error '2585'.
Is there anyway to have the Cancel code stop the On Exit code from running? Any other ideas?
Here is my code for Cancel:
Private Sub CancelFormButton_Click()
Me.Undo
DoCmd.OpenForm "fmuMainMenu"
DoCmd.Close acForm, "frmVetNewMainForm"
End Sub
Here is part of my On Exit code for the field:
If IsNull(Me.txtSSN) Then
strMsg = "Social Security Number Must Not Be Left Blank!" & vbCrLf
strMsg = strMsg & "Do you want to add new veteran's record?" & vbCrLf
If MsgBox(strMsg, vbQuestion + vbYesNo, "Go to Record?") = vbYes Then
Me.txtSSN.SetFocus
Exit Sub
Else
Me.Undo
DoCmd.OpenForm "fmuMainMenu"
DoCmd.Close acForm, "frmVetNewMainForm"
End If
Else
'RUNS THE REST OF CODE
End if
OnExit events occur everytime you leave the textbox, even a simple tab or click to go to another combo box or select some text will trigger it.
You may want to try the code in the AfterUpdates.
and try a ,nosave after the close form command...
In addition, any checks to ensure a field is filled/has data is usually encouraged to be performed in the Form_BeforeUpdate section... Or so I have been advised

How to grey out items in an Access combobox.

I am currently trying to do some lock downs on a combobox within Access and I’m not sure if I can. The flow of the form is that a user will select a part, then, area, and then a process group. Afterwards there will be a list of items in the process steps. The process group selection is based upon what part is selected and the area. The process steps is based upon which process group is selected.
Process steps selection
What I want to achieve is so that a user has to go through the list in chronological order. There are some steps that shouldn’t be started until the previous is done. Currently when the user has completed a step, they will click a button that runs an update query to change the comp_count status from 0 to a 1 for that step.
Process Status Table
I’m not sure I can use the comp_count for the lockdown and if I can, how can I make it so the first item in the list is always selectable. In addition I would prefer if all off the items in the dropdown are visible, but the ones that are not available be greyed out.
You can't grey it out, but you can tell the user not to pick it:
Private Sub myCombo_BeforeUpdate(Cancel As Integer)
If Not Me.myCombo.Column(1) Then
MsgBox "Do not choose this one yet."
Cancel = True
End If
I ended up finding a non elegant solution but it works. I added one column to the process status table called "Check" with default value of 0. Then I created a query that would change the value of that field for any of the processes that had the sequential value of 1.
Then I created a two queries to compare the "Comp_Count" with "Check". I then created an after update event for my selection.
Private Sub Sel_Process_AfterUpdate()
If DCount("*", "qry_process_step_check") = DCount("*", "qry_process_step_check_count") Then
Update_All_Forms
Else
MsgBox ("Previous step/steps not done.")
End If
End Sub
I then created a series of queries that did a similar check function like I used for the process step selection.
Check Query 2
In addition to those queries I also created an update query.
Update Query
This is where the non elegant portion came in. I made 16 copies of each of these queries and changed the sequential number each was looking for. After this was completed, I changed the on click event for a button that would update "Comp_Count" field in Process_Status table.
Here's a sample of the code used.
Private Sub Refresh_Sign_Offs_Button_Click()
If DCount("*", "qry_verifications_check_count") = DCount("*", "qry_verifications_check") And DCount("*", "qry_sub_verif_check_count") = DCount("*", "qry_Sub_Verif_check") And DCount("*", "qry_measurements_check_count") = DCount("*", "qry_measurements_check") And DCount("*", "qry_equipment_check_count") = DCount("*", "qry_equipment_check") And DCount("*", "qry_kits_check_count") = DCount("*", "qry_kits_check") And DCount("*", "qry_material_check_count") = DCount("*", "qry_material_check") Then
Me.Completed.Visible = True
DoCmd.SetWarnings False
DoCmd.OpenQuery ("qry_update_process_status")
DoCmd.SetWarnings True
MsgBox ("Process has been completed.")
If DCount("*", "qry_seq1check") = DCount("*", "qry_seq1count") Then
DoCmd.SetWarnings False
DoCmd.OpenQuery ("qry_seq1u")
DoCmd.SetWarnings True
ElseIf DCount("*", "qry_seq2check") = DCount("*", "qry_seq2count") Then
DoCmd.SetWarnings False
DoCmd.OpenQuery ("qry_seq2u")
DoCmd.SetWarnings True
Else
Me.Completed.Visible = False
MsgBox ("Not all items filled out.")
End If
The only thing missing in code above is the rest of elseif statements I was using. It's not the most elegant solution, but it will do everything I need it to do.

From main form, run VBA to validate total in subform

I have a form in MS Access 2002 with the following snippet of code behind a command button.
'Requery subform to make sure total is calculated
Me.fsubUpdateShipments.Requery
DoEvents
'Confirm quantities have been entered
If Form_fsubUpdateShipments.txtTotalShipmentQty.Value <= 0 Then
MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
Exit Sub
End If
For months this has been working fine but today a colleague came to me and explained that the error message was showing even when they had entered quantities.
After doing a bit of digging I determined that the .Value <= 0 expression was being evaluated before the textbox in question had finished calculating its value: =Sum([QtyToShip]). This seems to only occur when the subform has a non-trivial number of records (around 10 or more) and is obviously a serious problem.
Adding a breakpoint on the line containing the If statement allows the code to run correctly but I obviously cannot use this method permanently.
Is there some way I can force the code to pause until the subform controls have finished recalculating their values?
I would skip using the user interface altogether and look at the tables. If this is run on a main form command button, the subform has lost focus and saved its values. A requery is unnecessary. Just re-create the Parent-Child relationship in the criterion (third parameter) of a DSum:
If DSum("QtyToShip", "ShipmentDetails", "ShipmentID = " & Me!ShipmentID) <= 0 Then
MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
Exit Sub
End If
There are two options, both already identified by others, but as their suggestions are either incomplete or untidy (slow), I'm making this post.
A. You could put in an enforced wait to delay the IF test:
Put this in a module:
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
...
Public Sub GoSleep(millisecondDelay as long)
Sleep (millisecondDelay)
End Sub
And in your form:
'Requery subform to make sure total is calculated
Me.fsubUpdateShipments.Requery
DoEvents
GoSleep 1000 '1 second delay. Or 2000, whatever is required really
'Confirm quantities have been entered
If Form_fsubUpdateShipments.txtTotalShipmentQty.Value <= 0 Then
MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
Exit Sub
End If
B. As another answer says, you can recalculate it yourself, and test on the basis of this result.
However rather than using DSum as pteranodon suggests, I would suggest something faster than DSum is appropriate as DSum (DCount etc) is very slow.
I typically use ADO for data operations, though my code can be adapted for DAO readily enough. To use this code you'll need a reference to Microsoft activeX 2.8 if you don't have one already:
In a module:
Public Function GetDBValue(qry as string) as variant
dim rst as new adodb.recordset
rst.open qry, currentproject.connection, adOpenKeyset, adLockReadOnly
if rst.eof then
GetValue = null
else
GetValue = rst.fields(0)
end if
end function
public Function IsNullSQL(basevalue as variant, replacementvalue as variant) as variant
isNullSQL = iif(isnull(basevalue), replacementvalue, basevalue)
end function
in your form:
''Requery subform to make sure total is calculated
'Me.fsubUpdateShipments.Requery
'DoEvents
'Confirm quantities have been entered
If IsNullSQL(GetValue("SELECT Sum(QtyToShip) FROM tbl WHERE ..."), -1) < 0 Then
MsgBox "Cannot create shipment, no quantities have been entered.", vbCritical, "No Quantities to Ship"
Exit Sub
End If
'If Form_fsubUpdateShipments.txtTotalShipmentQty.Value <= 0 Then
' MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
' Exit Sub
'End If

How to keep from going to first record when requerying?

Making a form in Access 2010. I'm trying to make a button that moves to the next record (or the first if it's at the end), but because I want to account for other users' updates to the data set that have occurred in the meantime, I'm requerying the form before going to the next record.
I'm using the following code, adapted from this SO post:
Private Sub NextRec_Click()
Dim currentID As Long
currentID = Me.id.Value
'Here is where the requery brings the form back to the first record
Me.Requery
With Me.RecordsetClone
.FindFirst "id=" & currentID
If Not .NoMatch Then
If Me.Dirty Then
Me.Dirty = False
End If
Me.Bookmark = .Bookmark
End If
End With
If Me.CurrentRecord < Me.Recordset.RecordCount Then
DoCmd.GoToRecord , , acNext
Else
DoCmd.GoToRecord , , acFirst
End If
End Sub
It's working fine, except that .requery causes the form to briefly return to the first record before going back to the current record and then on to the next record. I don't want it to do this--is there any way I can just keep the current record showing in the form while .requery takes place, instead of showing the first record for the split second while .FindFirst is looking for the record at CurrentID?
Requery the recordset, not the form itself:
Me.Requery '<-- resets the current record to the first one in the recordset
Me.Recordset.Requery '<-- doesn't affect the current record
Sorry but I am strongly against those programmers who reinvent a NEXT button that is already provided.
If you need to do things when the user moves to the next record, use the OnCurrent event. This way you will also trap Page Down keys that users are entitled to use.
For me that code is unnecessary and a waste of resource. The recordset IS automatically updated every n seconds by Access (as specified in the refresh interval).
Is is true that deleted records stay there and appear as #deleted# and new records, might not appear unless you requery. But this is rarely a problem, and requery is really heavy and unfriendly.
Edit, after seeing #sigil's own reply:
Just an idea to reduce the number of recordset refreshes, which seem important to you: you might compare Me.Recordset.RecordCount with dCount("*", Me.RecordSource) and only requery when those differ.
So, as Remou wrote in the comments under iDevlop's answer, ODBC refresh doesn't show new records. I suspect that is the issue here because the form is getting its data from a linked table.
I'll still need to have my own Prev/Next buttons because I want to be able to loop back to the first record when I pass the last record. But I've modified the code; instead of requerying every time, I check to see if the record is the last one, and only requery when I click Next on the last record, or Prev on the first. This solves the problem adequately.
Modified code for NextRec_Click as follows:
Private Sub NextRec_Click()
Dim currentID As Long
currentID = Me.id.Value
If Me.CurrentRecord = Me.RecordsetClone.RecordCount Then
Me.Requery
With Me.RecordsetClone
.FindFirst "id=" & currentID
If Not .NoMatch Then
If Me.Dirty Then
Me.Dirty = False
End If
Me.Bookmark = .Bookmark
End If
End With
If Me.CurrentRecord < Me.Recordset.RecordCount Then
DoCmd.GoToRecord , , acNext
Else
DoCmd.GoToRecord , , acFirst
End If
Else
DoCmd.GoToRecord , , acNext
End If
End Sub

Started to get a Run-Time error '3159'; Not a Valid bookmark all of a sudden in Access 2007

I am really new to programming access. I converted an old 97 access db to access 2007 and it has been working fine for awhile. Today I go into it and was snooping around in design view and looking at the code. I didn't change anything. Just looked. When I went to run the code I kept getting the "Not a valid bookmark" error. Just to be sure I opened up the old program I converted and all the code is the same the line that is giving me the problem is the
Me.Bookmark = pos
The following is the whole routine.
Thanks in advance.
Private Sub ProductID_AfterUpdate()
Dim pos As Variant
Me![UnitPrice] = Me![ProductID].Column(2)
Me![ProductName] = Me![ProductID].Column(1)
Me![GLAcct] = Me![ProductID].Column(3)
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
pos = Me.Bookmark
Me.Requery
Me.Bookmark = pos
End Sub
Edit: I already tried compact and repair.
You will need to change this code as it is out of date for quite some time. I am not sure why it worked in the first place, because the bookmarks will have changed after updating, saving and requerying. I have commented the requery line, because it does not seem to serve a useful purpose, if you wish to update the combo, requery that. If you wish to find a record after an action, save the unique ID to a variable and find it. DoMenuItem is deprecated, you can use RunCommand instead, in this case, however, Me.Dirty=false will save.
Private Sub ProductID_AfterUpdate()
Dim pos As Variant
Me![UnitPrice] = Me![ProductID].Column(2)
Me![ProductName] = Me![ProductID].Column(1)
Me![GLAcct] = Me![ProductID].Column(3)
Me.Dirty=False
'pos = Me.Bookmark
'Me.Requery
'Me.Bookmark = pos
End Sub
To requery a form and return to the same record, do this:
Dim lngPKValue As Long
With Me.RecordsetClone
lngPKValue = Me!ID
' Me.Dirty = False is unnecessary, as the Requery saves the data
Me.Requery
.FindFirst "[ID]=" & lngPKValue
If Not .NoMatch Then
Me.Bookmark = .Bookmark
End If
End With
Now, where you put this code depents. I don't quite see in the original code why there's a need to requery, as the updates have been done to the record you're already on, so you're not really accomplishing anything by requerying and returning to the record you started on. But there are circumstances in which you'd want to do that (e.g., in an ordered recordset where you've edited values that will change the place of this particular record in the sorted result), and the above is the way to do it.
If it's necessary, you'd probably want to turn off painting of the form (Me.Painting = False, the Me.Painting = True once you've set the bookmark) or of the application (Application.Echo = False/True) so the screen doesn't flicker and you don't see the requerying and navigating. But be sure you add an error handler, since if an error happens while screen painting is turned off, your user may end up stuck and unable to continue.