How to grey out items in an Access combobox. - ms-access

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.

Related

Access TextBox does not update during VBA loop, only after

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

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

Hiding Null Records on Command

My question is fairly straight forward and hopefully simple.
I've got a database that has a long list of part numbers, and some items without part numbers. What i'd like to do is have a command button or Radio button that when activated, hides these null records, and when pressed again, shows them once more.
I can give more detail if needed
Thanks for any help!
Here is an example of using the Filter (same as using of out-of-the-box filtering capability from the Access/Office menu):
Setup:
Table (e.g. Parts):
Form (PartsForm):
Event handler for the Checkbox (OnClick):
Private Sub DisplayAllFilter_Click()
Me.Filter = ""
If Me.DisplayAllFilter.Value = False Then
Me.Filter = "[PartNumber] is not null"
Me.FilterOn = True
Else
Me.FilterOn = False
End If
End Sub
Result would be something like these:

In an Access form, how to return to previous record after requery

This should be an easy one. This form is filtered by [Dismissed] = "N". When the user clicks the "Dismiss" button, the [Dismissed] field changes to "Y". After the requery, the form should then return to the same row where the user was previously at.
Private Sub DismissButton_Click()
Me!Dismissed = "Y"
MsgBox "Dismissed!", vbOKOnly
Dim GoBackToThisRecord As Integer
GobacktothisRecord = Me.CurrentRecord
Me.Requery
Set Me.CurrentRecord=GoBackToThisRecord
End Sub
However, even though the built-in help files say that CurrentRecord is a read/write property, I get an "Invalid use of property" error message on this last line.
After setting the [Dismiss]="Y", and requerying the form, how do I get the user back to his/her previous location in the form?
I don't understand how your solution can work if the form is filtered to a value that the edited record no longer matches -- if you're filtered on [Dismissed] = "N" then changing the current record's Dismissed field to Y should cause the requeried form to exclude the record you've just updated.
That aside, I would never do it the way you've done it, as Me.CurrentRecord returns a number representing the position in the record. Since a requery can cause the number of records to change (e.g., somebody else edits or adds or deletes a record causing it to be included/excluded from the form's recordset) and the position of the sought-for record to change, I would use the PK instead.
Dim lngPK as Long
lngPK = Me!MyPKID
Me.Requery
With Me.RecordsetClone
.FindFirst "[MyPKID]=" & lngPK
If Not .NoMatch Then
If Me.Dirty Then
Me.Dirty = False
End If
Me.Bookmark = .Bookmark
End If
End With
That won't deal with the filter issue, but I leave that aside, since it didn't seem to be the issue that I thought it would be from the description of the original problem.
Nevermind. Fixed it myself. The last line is now:
Me.Recordset.Move GoBackToThisRecord
The right way to move to the previous record, whether it is a new one or not, is
Me.Recordset.Move GoBackToThisRecord -1
I use this function:
Public Sub RequeryFormAndKeepCurrentlySelectedRecord(f As Form)
Dim Position As Long
Position = f.CurrentRecord
f.Requery
If Position > 1 Then
f.Recordset.move Position - 1
End If
End Sub