Referring to specific record's Controls on subform - ms-access

I have a subform which makes a lot of use of conditional formatting to grey-out cells which cannot contain data, and highlight cells which have been adjusted by the user. The subform is a Datasheet.
This all works fine, except for the lag in applying the formatting, which sometimes even won't update until the user mouses over the cells.
What I would like to try is applying the formatting in VBA, rather than using the in-built conditional formatting, as I would rather the form takes a couple of seconds longer to load but with all formatting applied once it does, than to have the delay I'm currently getting.
So what I'm stuck on, is how to tie back a control on the subform to a specific record in the view that's populating it. For example, I can use the following code:
Dim ctrl As Control
For Each ctrl In Me.Controls
MsgBox ctrl.Name & " : " & ctrl.Value
Next ctrl
Which might display, for instance, "RowNumber : 010", so I could identify what record that control relates to, but when it says "TotalUSD : 1,234,567.89", I would have no way of knowing which record that related to, and therefore whether or not formatting should be applied.
Is there a better way of doing this, I guess ideally by being able to link each ctrl to its corresponding record in the Form.Recordset?
UPDATE
Below screenshot shows the conditional formatting as it is currently implemented, and what I am trying to achieve using VBA instead of conditional formatting.

I do not have in depth information about your implementation so here is a general suggestion that I have used in my implementations.
You should add some kind of distinguishing column to your table. For example I once had a form that showed projects and employees. The distinguishing column contained a boolean value "True" for each project, "False" for each employee. Based on that information I was able to format accordingly.
My suggestion: Add an integer distinguishing column to your recordset table. The value "0" would be a plain column, "1" would be for greyed out, "2" for user input.
In the Form_Load Event you would have to iterate through all your controls and set the formatting.

Related

MS Access: Easiest way to implement a "check all" box in a form with a datasheet subform

I have a form whose main purpose is to contain a datasheet subform (the form provides a common visual theme across the pages as well as navigation buttons and sometimes selection criteria for the subform).
One of my subforms has a check box.
The user behavior will be to:
Open this form
Filter the datasheet according to some specification
Do something off-line involving, e.g., the ten visible line items
Check the field (yes/no field) on those ten line items to indicate that it's been done
What I'd like is for the user to be able to check one box that then checks all the visible boxes.
The challenge is that it must only be the visible ones, i.e. dependent on what filters are in place.
I tried to create a checkbox in the main form labeled "Check All" which runs code something like:
me.sfrmReport.Control.reported.value = me.chkCheckAll.value
But unfortunately, this only checked the active row, not all of them. I know I could run a query on the underlying table, but it would need to replicate the user filters.
In fact, what might be even cooler would be a "Check highlighted" box that checks the highlighted rows only.
But I think "Check all" would be better.
I tried to find this, but my searching skills failed me. I'd have thought this was something somewhat common, but perhaps I'm thinking about this incorrectly. I do note that there's a sum function which automatically sums just the filtered/visible rows in a datasheet, so the notion of interacting with just the visible rows is at least somewhat standard.
Well, the check all box certainly should not be on a "row". So one would assume that say beside the combo box, you place a check all box. (can't imagine that the button or code would be place on the row(s)).
So, say beside the combo box in your screen shot, you have a button called check all.
The code behind that button would/could be:
dim rst as dao.RecordSet
set rst = me.MySubForm.Form.RecordSetClone
rst.movefirst
do while rst.EOF = false
rst.edit
rst!ResultReported = true
rst.update
rst.moveNext
loop
me.MySubForm.Form.Refresh
So above will operate directly against the sub forms data source (current filtered recordset).
I "think" from your screen shot, the top part of the form (with combo) is the main form, and the sub form you have displayed is the sub form.
So, of course you will have to replace the name of the controls I used above, and also the field name for the check box.
So the format to get that recordset is:
me.MySubFormControlName.Form.RecordSetClone
and to fire off a refresh of the sub form after the code runs is of course.
me.MySubFormControlName.Form.Refresh
Note that intel-sense should work and help you. if the code does not compile, then don't try and run it until it compiles.

Set unbound combo box to match on full string for not in list event

I have an unbound combo box that gets it's row source values using a select query in VBA. The values shown are filtered by other selections on the form. If a value entered is not on the list I have a prompt to ask the user if they would like to add a new record. So far this has worked without any problems.
I overlooked one issue. If the "new" item is a partial match the selection defaults to the partial match entry. (Ex. I want to add part 4321437 but part 4321437-01 is already present.) How can I get the field to match using the full field?
I have tried playing with auto expand, allow value list edits, show only row source values, and inherit value list. Nothing seems to stop it from auto-filling. I have also tried clicking out of the field versus tabbing out.
If I remove my for key-down event that displays the list options with the arrow keys, it works. However, the customer would like to keep that feature.
Is there a way to have both?
Thinking outside the box, perhaps consider using a TextBox in place of a ComboBox.
This way, using the AfterUpdate event, you would have full control over how the entered value is handled: test for a match in your dataset using a simple query and branch accordingly.
You could even cycle through existing options by successively populating the TextBox with existing values from your dataset on the KeyDown event triggered by the arrow keys.
I found a way to have both. In my Key Down Event, I check if the down arrow is pressed. If it is, I display the drop down menu.
If KeyCode = vbKeyDown Then
Me![cboNewPart].Dropdown
End If

Getting data from Word doc form controls

I'm building a form in a Word .docm (macro enabled Word 2013 doc) with the aim of programming an Access database to import data from completed forms. I've placed textBox and comboBox controls to receive user input, but I can't get the data back out.
The examples I've seen use the Document.FormFields collection like so (in Word):
Dim fld as FormField
for each fld in ActiveDocument.FormFields
Debug.Print fld.Name & " - " & fld.Result.Text
next
However in my doc Document.FormFields is empty, but Document.Fields has 19 elements, which happens to be the number of controls in my form. That's great, except that I can't seem to get the name or value of any of the controls using a Field object. Field.Result.Text is always blank, and there is no Field.Name attribute.
So what's the difference between Field objects and FormField objects, and why are my controls showing up in Fields when all the examples I've seen use FormFields?
Am I using the wrong form controls? There are three types (I hope I'm not the only one who thinks this is ridiculous) legacy controls, ActiveX controls, and content controls. I'm using the ActiveX type.
A few things...
In MS Word terms, a 'field' doesn't have to be a form field. E.g., an auto-updated date, linked graphic, page number, etc., are all types of 'field' (or at least, were until the most recent versions of Word).
For compatibility reasons, it can be better to avoid the ActiveX controls. E.g., the Mac version of Word doesn't support them.
For best compatibility I would personally stick to the traditional form controls. Instances are named according to their bookmark name, which is settable by right clicking the control and selecting Properties. In VBA, their data is then got via the FormFields collection; if you want the value for a specific field, use
Value = ActiveDocument.FormFields("MyFieldName").Result
If all you want is to use the current fields you have, you can get the value or name from the OLEFormat.Object:
Application.ActiveDocument.Fields.Item(1).OLEFormat.Object.Value
Or
Application.ActiveDocument.Fields.Item(1).OLEFormat.Object.Name
However, I would agree with Chris in recommending that you avoid the ActiveX controls. As Microsoft says, there are a lot of reasons that they aren't the best choice for Word forms, except in very specific cases.
Using the "legacy" formfields and
//key
Application.ActiveDocument.FormFields(1).Range.Fields(1).Code
//value
Application.ActiveDocument.FormFields(1).Result
Is the solution I came up with.
I've seen companies use the bookmarks, default values, status / help texts for different purposes as well. Not that I would recommend it, but maybe they are working around an issue I'm not aware of.
I feel like .range is a bit of a hack, but considering it's the range of only the form field, unless form fields or fields can have nested fields, it should be ok.

Obtaining textbox value in change event handler

I've written a form that performs queries asynchronously as text is typed into a textbox, however I somewhat arbitrarily seem to get the following error thrown: "You can't reference a property or method for a control unless the control has focus."
The immediately relevant code is:
Dim UpdateRequested As Boolean
Dim qryText As String
.
.
.
Private Sub txtBox_Change()
qryText = txtBox.Text
UpdateRequested = true
End Sub
Some place in the ellipses is the code that handles dynamically loading an ADODB record set, populating a local table, and refreshing a sub form. However, even when I disable this code, the problem persists: Sometimes I get the error. Sometimes I do not.
This seems to be persistent through closing the database and reopening it. Every time it starts working again, it's because I've been fooling around with code in the debugger, but I'm not sure what exactly is causing it to magically "just work" or what is causing it to not work at all.
Update
Just to make things more puzzling, I added a couple of simple event handlers:
Private Sub txtBox_GotFocus()
MsgBox "Got focus"
End Sub
Private Sub txtBox_LostFocus()
MsgBox "Lost focus"
End Sub
I run the form. I click in the test box. I receive the "Got focus" message. As soon as I type I see the error as described above. If I re-open the form, I can click between the text box in question (which itself is unbound) and a bound text box in the sub form and see both "Got focus" and "lost focus" messages as one would expect. Furthermore, showing a message box with the current value of "Screen.ActiveControl.Name" shows the expected name just before the Text property is accessed.
I know this is an old thread but it's the first I found when I had the same problem. None of the answers helped except Kaganar's own solution, which pointed me in the right direction. I'm guessing the reason people had trouble reproducing the error is there are some important details missing from Kaganar's description:
The Textbox was in the form header (or footer).
The form did not allow additions.
Because I believe the full answer is...
The Text property of any control is inaccessible when the form has a record source with no records to edit
I think there is part of Access that does not realise the textbox exists :) To understand how that might come about...
Put the unbound TextBox in the detail of the form
Do not allow additions
Set the recordsource to return no records
Open the form.
Hey presto! No Textbox.
Return a record, or allow additions, or delete the recordsource, et Voila! There is your Textbox with it's Text.
I added a text box named txtFoo to my form. Here is the procedure for its change event.
Private Sub txtFoo_Change()
Debug.Print "Value: " & Nz(Me.txtFoo.value, "*Null*") & _
"; Text: " & Nz(Me.txtFoo.Text, "*Null*")
End Sub
Then, with nothing in txtFoo (IOW its value is Null) when I type "abc" into txtFoo, here is what I see in the Immediate window.
Value: *Null*; Text: a
Value: *Null*; Text: ab
Value: *Null*; Text: abc
Basically, each character I add to the text box triggers its change event and prints the text box's current contents to the Immediate window.
As far as I understand, you want to do something similar ... except you want a different action in place of Debug.Print. Take another look at your change event procedure and compare it to mine.
Private Sub txtBox_Change()
qryText = txtVendorName.Text
UpdateRequested = true
End Sub
That is the change event for a control named txtBox. Within that procedure you reference the .Text property of a control named txtVendorName. However txtBox is the active control at the time its change event code runs ... so you can not access the .Text property of txtVendorName because it is not the active control.
Given that this problem surfaces for only the one form, but not on other new forms, I would suspect the problem form has become corrupted. Read the 2 answers to this SO question and try decompile to cure the corruption: HOW TO decompile and recompile. Decompile is often recommended as a routine practice during development.
You could also use the undocumented Application.SaveAsText method to save your form as a text file. Delete the bad form, and use Application.LoadFromText to import the saved text copy.
Make sure you have a backup copy of your db file in case anything goes wrong.
To set or return a control's Text property, the control must have the focus, or an error occurs.
To move the focus to a control, you can use txtBox.SetFocus or DoCmd.GoToControl "txtBox".
Also, the Text property is not always available:
While the control has the focus, the Text property contains the text data currently in the control; the Value property contains the last saved data for the control. When you move the focus to another control, the control's data is updated, and the Value property is set to this new value. The Text property setting is then unavailable until the control gets the focus again.
The form had a lingering data source. I'm not sure why this would cause to the behavior described above, especially considering the text box controls are unbound, however since removing the data source the text boxes are behaving as expected.
You said "somewhat arbitrarily" I think if everything is fine you must get the error when your form's recordset is empty.
In fact it's a know bug in Access and this error can occur if these conditions are met:
a) The control is in the Form Header or Form footer section
b) The form is filtered such that no records match (or there are no records)
c) No new record can be added.
In this case, the Detail section of the form goes blank. The controlis still
visible, but Access gets really confused and can throw the error you
describe.
More info:
http://allenbrowne.com/bug-06.html
I know my answer is out of date. Yet you just can set focus three times. On TextBox in header, on any texbox in detail space and On TextBox in header again. I use access 2003.

Hide a column programmatically in MS-Access

I want to hide or show a column based on variable data from a users selection. How do you set a column to hidden in MS-Access 2003?
For Example,
After user change event...
For Each ctl In Me.FormNameHere.Form.Controls
If (TypeName(ctl) = "Textbox") Then
If InStr(GetTextList(), ctl.Name) > 0 Then
ctl.hidden = True
Else
ctl.hidden = False
End If
End If
Next ctl
What is the best approach to this type of challenge?
Is there a more obvious solution?
Controls do not have a "hidden" property (no objects in Access have a hidden property). They do have a .Visible property.
For future reference, I suggest you familiarize yourself with the Object Browser in the VBE -- open the VBE and hit F2. You can then restrict your search to the individual libraries used in your project. It does take a while to get to the point where you understand the object model, though.
Also, you can rely on Intellisense to learn the properties/methods of an object, so in the code of the form you're working with, you can type "Me.MyTextBox." and the Intellisense dropdown will show you all the properties and methods of that particular control. It doesn't work for a generic control variable (as in your code) because different control types have different properties.
And, of course, the properties sheet gives the names of the properties, even though in code they don't always use the same orthography (usually they are the same with spaces removed).
Also, there are differences in how you might want to do this depending on whether it's a regular form or a datasheet form. In a datasheet, your controls also have .ColumnHidden and .ColumnWidth properties (setting those in any view other than datasheet view has no effect, and neither of those properties are available in the standard property sheet, but changes to them are retained when you save the form).
I answered a similar question to this not long ago to do with hiding columns on a datasheet. However you seem to want to hide textboxes arranged in a column on a form, is that correct?
Iterating over all the controls in the form could be slow if you have many controls. If you really need to use textboxes like that, then you could try group the 'columns' together then hide the groups. Another approach would be to use a listbox or datasheet to represent the data, where you can alter the layout of the columns directly.
I found the ColumnHidden property does the trick.
For Each ctl In Me.FormNameHere.Form.Controls
If (TypeName(ctl) = "Textbox") Then
If InStr(GetTextList(), ctl.Name) > 0 Then
ctl.Columnhidden = True
Else
ctl.Columnhidden = False
End If
End If
Next ctl
I got a hint from this related question.
A one-liner approach is using:
forms(fname).Controls(ctrlname).columnhidden = false
where
fname is name of your form
ctrlname is name of your control