Access 2010 VBA ControlSource Loop - ms-access

I have multiple fields on a form that have no values associated with them until data is entered into that field. At which point those controls have the #Name? value. All I'm trying to do is hide the fields using a loop as opposed to writing If statements. Here is the code working with a single If statement to hide said field/fields.
If Not Me.txtField.ControlSource = "#Name?" Then
Me.txtField.ColumnHidden = True
End If
I can't seem to figure out how to hide multiple controls that have the #Name? flag. Any help would be appreciated.
Dim ctl As Control
Dim ctlError As String
ctlError = "#Name?"
For Each ctl In Me.Controls
If Not ctl.ItemsSelected(txtField) = ctlError Then
ctl.ColumnHidden = True
End If
Next ctl
EDIT:
On the main form there is a subform with a cross tab query. But the problem is results on the query are populated from a combobox. So I've added fields that are not yet available in the query to the subform and end up with #Name? Attached is a screenshot to better illustrate the issue.
There is a 90% chance I'm going about this process wrong, so it's a learning process right now.

It would be easier to address this question if you described the specific errors that are raised by your code. This answer assumes that the control in question is a TextBox.
"#Name?" means that a control's ControlSource refers to a field that is not in the form's RecordSource. For example, if your query has two fields (ID and BirthDate, for example) and your form has three controls, with ControlSource of "ID", "BirthDate", and "BirthCountry", then the control whose RecordSource is "BirthCountry" will show "#Name?". However, its Value is not "#Name?". Rather, calling the Value property raises error 2424 (with the rather unhelpful message "The expression you entered has a field, control, or property name that Microsoft Office Access can't find").
The only way I know to check for the string "#Name?" is through the control's Text property, which is only available when the control has the focus, so that's not much help. If you retrieve the value of the ControlSource property, you'll get "BirthCountry" (of course). So you could check all ControlSources on your form against the fields of the form's RecordsetClone (add error handling, please):
For Each ctl In Controls
If Not RecordSourceContains(ctl.ControlSource) Then
ctl.Visible = False
End If
Next
Function RecordSourceContains(strFieldName as String) As Boolean
Dim fld As Field
For Each fld In RecordsetClone.Fields
If fld.Name = strFieldName Then
RecordSourceContains = True
Exit For
End If
Next
End Function
You'll probably want to do some other bookkeeping. For example, you'd want to check for expression control sources (which begin with "=") because they won't be in the RecordSource. You'll also either need to handle error 438 (Object doesn't support this property or method) or check each control's type to make sure it has a ControlSource property.

In design view for the form, what does the field say for Control Source? It should be blank, meaning the form is unbound. If it's not, then that explains #Name.
More information: http://www.databasedev.co.uk/unbound-forms-add-data.html

Here is code I use to take care of that:
Sub setControl
'' You can use this in your loop
Call FieldEnable(subFrm, "boxQty", booSellSet)
End Sub
''----------------
Private Sub FieldEnable(subFrm As Access.Form, strCtlName As String, _
booEnableMe As Boolean)
On Error GoTo errHandler
subFrm(strCtlName).Enabled = booEnableMe
subFrm(strCtlName).Visible = booEnableMe
errExit:
Exit Sub
errHandler:
If Err.Number = 2164 Or Err.Number = 2165 Then ''control has the focus
Resume Next
Else
DoCmd.Hourglass False
Screen.Application.Echo True
MsgBox "Problem with control named " & strCtlName
Resume errExit
End If
End Sub
I have to adjust many fields on the form, so this is very reusable. I might be turning them "on" or "off"; this takes care of either. Let me know if you have questions.
It all boils down to this method for hiding the field:
Control.Enabled = False
Control.Visible = False

Delete the field Black from table.
And then make make again the Black field in your table.

Related

.OldValue property in Undo button handler of form gives run-time error 2448

This is my first question, so I hope I get it right.
I am working on a project to review Employee information. I am using two forms, the first one is "ReadOnly," the second is "EditOnly."
The "ReadOnly" form is bound to a query, which defines criteria for specific records to be displayed.
The "EditOnly" form has a subform, bound to 2 tables (which are also part of the query that is the source for the "ReadOnly" form), that have a 1-to-many relation (using info from this answer:
Error 3251 on .oldValue control property).
The "EditOnly" form is opened via a button and is used to edit the data of a specific Employee. In this form I have an Undo button, using this code:
Sub UndoEdits()
If Me.Dirty Then
Me!btnUndo.Enabled = True
Else
Me!btnUndo.Enabled = False
End If
End Sub
Sub btnUndo Click()
Dim ctlC As Control
For Each ctlC in Me.Controls
If ctlC.ControlType = acTextBox Then
ctlC.Value = ctlC.OldValue
End If
Next ctlC
End Sub
which reverts unwanted changes. But when testing the function, after changing some data I get run-time error 2448. Using Debug highlights this row:
ctlC.Value = ctlC.OldValue
Is there a way to make the Undo button work?
Error 2448 is "Can't assign a value to this object".
You will get this error if you try to assign a value to any text boxes on the form that are read only/locked or disabled.
Modify your code to check ctlc for Enabled and Locked and skip any controls that aren't assignable, like this:
Dim ctlC As Control
For Each ctlC in Me.Controls
If ctlC.ControlType = acTextBox Then
If ctlC.Enabled And Not ctlC.Locked Then
ctlC.Value = ctlC.OldValue
End If
End If
Next ctlC
Debugging Tip:
You can check the value of ctlc.Name either by stepping or by writing to the immediate window (Debug.Print ctlc.Name) to see which text box is the source of the problem.

MS Access TextBox Value is always NULL

I am doing some work for someone that insists on using MS Access. I don't usually use it so I am a bit new to the whole control structure and best practices. What I am trying to achieve is to have a filter textbox on a form which, when a value is entered, it will filter the rows in the detail section. That seems like a straightforward use case. I initially tried the following behaviour as the event handler for the On Change event:
Private Sub FilterGrid()
Me.Text32.SetFocus
If Not IsNull(Me.Text32.Text) And Me.Text32.Text <> "" Then
Me.Filter = "JobNumber LIKE '*" & Me.Text32.Text & "*'"
Me.FilterOn = True
End
Else
Me.FilterOn = False
End If
End Sub
This worked perfectly until I typed something that didn't have any rows matching and the whole thing exploded with this error (and was unrecoverable without closing the form):
Run-time error '2185': You can't reference a property or method for a control unless the control has the focus.
I did some reading around and the general opinion was that .Text should not be used and .Value (or simply the Text32 without a property) should be used. That produced some very strange behaviour. The Text32.Value is ALWAYS null. I have a watch window and I can see that for the normal behaviour, Text32.Text has an actual value, but Text32.Value is NULL.
Obviously I am doing something wrong, but I don't have enough experience with Access to know what it is.
Just as an aside, another suggestion was to do Text32.SetFocus right before accessing the Text property. This doesn't resolve the error I mentioned. It still throws the exact same error.
Is anyone able to point me in the right direction here?
As you found out, the textbox's Value is only set after the control loses focus.
Conversely, the Text property is only accessible while the control has focus.
The Value property is defined as the default member for controls; that means Text32 will be implicitly the same as Text32.Value, however, depending on the context,Text32 can sometimes refer to the control itself, not just its value.
All these discrepancies can sometimes be infuriating.
To go back to the matter at hand: you have 2 ways to handle filtering.
if the list to filter is large, it's probably better that the user type their filter, then press ENTER to validate it.
if your list is not too large, you can implement filter as you type.
First case, wait for user input to be validated by ENTER.
Say your filtering textbox is called txtFilter and is located on a form whose subform is showing a datasheet (or continuous form) that you want to filter.
All you need to do is wire the textbox OnKeyDown events as such:
' We will only perform the filter if the user press the ENTER key
Private Sub txtFilter_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case 13, 9
KeyCode = 0
QuickFilter
End Select
End Sub
' Perform the actual filtering on the subform
Private Sub QuickFilter()
Dim sql As String
Dim filter As String
If txtFilter.Text = vbNullString Then
' Reset the filter if the textbox is emtpy
SubForm.Form.FilterOn = False
Else
'Some common substitutions that users may have already inserted as wildchars
filter = Replace(txtFilter.Text, "%", "*")
filter = Replace("*" & filter & "*", "**", "*")
' We construct the filter SQL
sql = "([JobNumber ] LIKE """ & filter & """)"
sql = sql & " OR ([ProjectCode] LIKE """ & filter & """)"
sql = sql & " OR ([SupplierName] LIKE """ & filter & """)"
'... Add as many columns to filter on as you want
' Assign the filter to the subform
SubForm.Form.filter = sql
SubForm.Form.FilterOn = True
End If
End Sub
Second case, filter as you type
Well, it's fairly easy, we just need to add to the above solution a way to track changes as the user is typing.
This is best done through the OnChange event of the texbox.
Private Sub txtFilter_Change()
QuickFilter
End Sub
That's all you need to add.
dont use .text property
instead use .value property
text3.value=text1.value+text2.value
you dont need setfocus like .text property everytime.
it works perfectly....vba ms access

Printing non-null fields only in MS Access 2003 report

I am trying to generate a report whose source is a form and that only prints fields that are not null.
To do so I have two text-boxes for every field. They are both resizealbe (can shrink/grow).
The first text-box is for the caption, and its source is
=IIf([record] Is Null,"","Caption:")
The second is the record value itself. If record is null then the value of both text-boxes is "" and null and they do not appear nor take any space in the form.
Two question:
This doesn't seem like the smartest way to do it. Anyone has a better idea?
The report also contains check-boxes, and this method only works if I check/uncheck at least one check-box before I generate the report. Otherwise all captions appear anyway. This is very weird - anyone has any idea why it happens?
This will only work properly with Print Preview, not MS Access 2010 report or layout view. You will need Can Shrink on the control to close up the gaps. In Access 2010, "*_Label" is the default name assigned to labels of controls.
Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
Dim ctl As Control
For Each ctl In Me.Controls
If ctl.ControlType = acLabel And ctl.Name Like "*_Label" Then
ctl.Visible = Not IsNull(ctl.Parent)
''Bound checkboxes are never null, so hide false
If ctl.Parent.ControlType = acCheckBox Then
ctl.Visible = ctl.Parent
ctl.Parent.Visible = ctl.Parent
End If
End If
Next
End Sub

How to set a textbox to enabled on event

I have a textbox on an access form that I would like to enable for edditing if the textbox is blank. I am new to VBA, and am uncertain of the best way to go about it. Using a double-click event would be fine with me, but I would prefer to do it on load.
I have tried the following code, but can't seem to get it to work. Any help would be appreciated.
Private Sub EmpID_DblClick(Cancel As Integer)
If EmpID.Text = "" Then
Me.EmpID.Enabled = True
End If
End Sub
For things that should happen on a per record basis, you are best off using the current event. Do not use the .Text property for controls. If you must use a property, use .Value. The .Text property is only available when the control has focus and is generally only used in special cases such as the Change event.
Private Sub Form_Current()
If IsNull(EmpID) Then
Me.EmpID.Enabled = True
End If
End Sub
In a normal set-up, there is no chance of the control being equal to a zero-length string ("")
You can cover all bases by saying:
Trim(EmpID & "") = ""
You are running into the Null problem
Null is not the same as ""
Try this instead
If EmpID.Text Is Null Then
Me.EmpID.Enabled = True
End If

In MS Access form, how to color background of selected record?

I have a somewhat complicated looking Access Form with a continuous display (meaning multiple records are shown at once). I'd like to change the background color of the selected record only so the end-user can easily tell which record they are on.
I'm thinking of perhaps a conditional format or maybe something like this:
Private Sub Detail_HasFocus()
Detail.BackColor(me.color)=vbBlue
End Sub
and something similar for when that row loses focus. This code snippet obviously won't work, but it's the kind of code I'd like to achieve.
Here is a complete solution that correctly treats newly-edited records, as well as handles Access UI quirks (i.e. failed re-paints, inconsistent behavior depending on how a record is selected--via mouse or keyboard or record selector, etc.). I include verbose comments, because Access requires thorough explanations due to its many inconsistencies and/or bugs. I have attempted leaner solutions, but inevitably it does not behave well without the tricks to force Access to repaint the form, or complicated ways for determining the current record within the Detail_Paint() event handler.
The code is for an Access form with a textbox bound to an ID Autonumber field. The form also has a Rectangle control named boxCurrent which is updated to highlight the currently-selected record (it has a wide brightly-colored border). I find a Rectangle control offers more visual options than setting Detail.BackColor, although such details are openly configurable using the overall pattern. Developed and tested with Access 2013 and 2016.
'* Set this value in From_Current event handler
Private vCurrentAutonumber As Variant
Private Sub Detail_Paint()
'* Delcare static variables to make often repeated calls more efficient.
Static iActive As Integer
Static vThisValue As Variant, vOldValue As Variant
On Error Resume Next
iActive = 0 '* Default to False/hidden value
vThisValue = Me.ID.Value
If Err.Number = 0 Then
If Not IsNull(vCurrentAutonumber) Then
If vThisValue = vCurrentAutonumber Then iActive = 1
ElseIf Me.NewRecord Then
'* Form currently set to "New Record", but may or may not be in edit mode.
'* When in EDIT MODE, AutonumberControl.Value will HAVE A VALUE
' AND AutonumberControl.OldValue will be null
' When NOT in edit mode, AutonumberControl.Value will be null
' AND AutonumberControl.OldValue will also be null
'*** That is the only way I have found to determine for sure
' if the currently-edited-new-record is the available record for
' this particular call of Detail_Paint().
' Other properties like CurrentRecord, NewRecord, etc. remain
' unchanged during repeated calls to Detail_Paint()
' and access has no other convenient way to determine the
' newly-added autonumber value, so it must be deduced using
' this trick.
If IsNull(vThisValue) Then
If Not Me.Dirty Then
'Record selector on *(New Record) row, but not edited yet.
If Err.Number = 0 Then iActive = 1
End If
Else
vOldValue = Me.ID.OldValue
If Err.Number = 0 Then
If IsNull(vOldValue) Then
'* Newly-edited record with fresh autonumber value is selected.
iActive = 1
'Else if vOldValue is not null, it is an existing record.
'* Not the current record since it can't be both existing and new.
End If
End If
End If
End If
End If
'* Set these values on EACH CALL, since their values will be retained
'* on subsequent calls.
With boxCurrent
.BackStyle = 0 'iActive
.BorderStyle = iActive
End With
End Sub
Private Sub Form_AfterDelConfirm(Status As Integer)
Me.Repaint
End Sub
Private Sub Form_AfterInsert()
If IsNull(vCurrentAutonumber) Then
'* If a new record is saved while staying on that record,
'* the Form_Current() handler is not called and so the
'* vCurrentAutonumber would not be updated with the newly
'* saved value. But now Me.NewRecord is false, so the
'* currently record would not be updated properly unless
'* vCurrentAutonumber is explicitly updated here.
On Error Resume Next
vCurrentAutonumber = Me.ID.Value
'* Force repaint (see comment in Form_Current)
boxCurrent.BackColor = vbBlue
End If
End Sub
'Private Sub Form_BeforeInsert(Cancel As Integer)
'* Attempted to set some variable or property in this event handler
'* --something to indicate to Detail_Paint() which record is the
'* new record being edited. But no matter what I set here, the
'* change is present and identical for each call of Detail_Paint(),
'* so for the most part this technique was not useful.
'* The only alternative is to set one of the data fields, because
'* those DO change for each each to Detail_Paint().
'* IF THE PRIMARY KEY IS NOT AN AUTONUMBER FIELD (OR IF ANOTHER
'* DATA FIELD IS AVAILABLE TO MANIPULATE), ONE COULD FLAG A NEWLY
'* EDITED RECORD BY SETTING SUCH A FIELD HERE AND INSPECTING
'* it in Detail_Paint(). Personally, I avoid dummy fields just for
'* making Access work well and my primary key is Autonumber so it cannot
'* bet set to a known new value.
'End Sub
Private Sub Form_Current()
On Error Resume Next
vCurrentAutonumber = Me.ID.Value
If Err.Number <> 0 Then vCurrentAutonumber = Null
On Error GoTo 0
'*** FORCE REPAINT of record detail section
'* If not forced, records are not necessarily repainted for every type of
'* UI event. For instance, changing records using the record selectors
'* has different behavior than clicking inside a record, but either way
'* the current record has changed and so should be repainted.
'* But calling Me.Repaint is not sufficient to actually repaint the form.
'* Even if the Detail_Paint event is called, the actual visible elements
'* are not always repainted (bug?). It seems that only changing some
'* visible feature/control of the form will force an actual repaint.
boxCurrent.BackColor = vbBlue
End Sub
Private Sub Form_Load()
vCurrentAutonumber = Null
End Sub
OP here. Cade pointed out that the original solution's link points to a '97 db which may not be openable anymore. Also, unfortunately, my original code is long since gone.
However, I recently did something like this using the conditional formatting method which requires no VBA. This is in Access 2016 on a Continuous Form:
In your dataset, add a yes/no field. Let's call it Rcd_Selected.
Make a text box. Also set the Control Source to Rcd_Selected.
Change the Fore Color to #FFFFFF (This will be the non-selected color)
Change Format to 'True/False'
Set Enabled=Yes,Locked=No
In the Ribbon, go to Format->Conditional Formatting and make a new rule: Where Field Value =True, set the fore color and back color to your Selected color and click on Enabled.
Clean up by stretching the text box over the entire Detail section and moving it to the back.
Make a check box field. Sent the Control Source to Rcd_Selected.
Stretch the checkbox over the entire Detail section and move it to the front.
Whenever you click on the area, the checkbox will turn on/off triggering the conditional formatting of the textbox in the background to change color.
One limitation of this is that it makes the entire record read-only. I've never had speed issues and it works when multiple records are selected and un-selected.
One more way for continuous forms...
In the Form's Form_Current event set a TempVar to equal the value of the current record ID e.g. `
TempVars!CurrentRecordID = Me.ID_xxx.value
Me.ControlName.Refresh 'This must one of the conditionally formatted controls per Step 2 below
NB. the second line of code above is necessary for the conditional formatting to be triggered. You only need to refresh one of the conditionally formatted controls.
Conditional Formatting rule expression is: [ID_xxx]=[TempVars]![CurrentRecordID] and set the desired formatting e.g. BackColor
Apply step 2 to any control that you want conditionally formatted when the record is selected.
Only the controls on the current record are highlighted
Private Sub Form_Current()
10 On Error GoTo Form_Current_Error
'=============================================================
20 TempVars!CurrentRecordID = Me.MatterID.Value
30 Me.fldDateEngagedEnquiry.Requery
'================================================================
MyExit:
40 On Error GoTo 0
50 Application.Screen.MousePointer = 0 'reset to default mouse pointer
60 Exit Sub
Form_Current_Error:
70 MsgBox "Code Line Number: " & Erl & vbCrLf & vbCrLf & "Error " & Err.Number & vbCrLf & vbCrLf & " (" & Err.Description & ") in procedure " & vbCrLf & vbCrLf & " Form_Current of Sub Form_frmMyMatters_SVR"
80 GoTo MyExit
End Sub