Program change of row source induces rogue values - ms-access

I have a combo-box on a form with:
ControlSource=Target Basis ---------- [A field in the form recordsource]
RowSource=Target Basis -------------- [A separate table of the same name]
RowSourceType = Table/Query
The table "Target Basis" has 20-odd values. The above behaves as expected ... until,
VB code changes the above as follows:
Select Case xyzVar 'xyzVar is taken from another field on form.
Case xyzValue
Me.Target_Basis.RowSourceType = "Value List"
Me.Target_Basis.RowSource = vbNullString
Me.Target_Basis.RowSource = "'Trend Channel'; 'Adjusted Trnd Ch.'"
Me.Target_Basis.Locked = False
Me.Target_Basis = "Choose"
Case Else 'etc.
Upon completion of the above code, the text "Choose" appears in the combo as expected, but when the user drops the choice list, only one option appears, and it's the following text --
SELECT [Target Basis].Basis FROM [Target Basis]
I used documenter on the entire database and determined that the above query is not defined anywhere. I assumed that the system must be generating it from the controlSource name. To check this, I changed the name of the separate table (i.e. the rowSource table) from "Target Basis" to "Target Type". I got the identical behavior, except that now the single item in the list was --
SELECT [Target Type].Basis FROM [Target Type]
At that point I changed the name of the table back to "Target Basis"; however, the item that appears in the droplist remains as above -- despite that now, neither the query nor the table "Target Type" exist in the system. I tried closing, compacting and re-opening the Db; no change.
Further experimentation revealed the following:
When the code completes execution, the rowSourceType and rowSource are as they should be. I checked this by creating a button that outputs these properties. As soon as the user clicks on the combo's drop arrow however, the rowSource property changes to the SELECT statement above. The rowSourceType stays "Value List", which is why it appears in the drop list rather than executing.
If the controlSource is deleted, i.e. the control is unbound, the problem disappears.
Where is this query coming from??
Much obliged for any insight - IG

Firstly, I'm not sure why you have:
Me.Target_Basis.RowSource = vbNullString
I think it is unnecessary and may be causing the problem.
Try adding Me.Target_Basis.Requery to refresh the combobox list.

Access often leaves "ghosts in the machine". I've been working with Access for close to 20 years and I can't tell you how many times I've had to fix a problem and then import all the forms/macros/queries/etc.. into a fresh empty database. it happens. Not sure if that's your current problem, but it does happen.

Related

Unfreezing columns in a subform in Access

I have a form with a subform that displays records, and the user is able to rearrange that subform and save their preferences. This means they can change the width of the columns, the order, hide columns, etc. and save their changes at any time, and those changes are applied when the form next loads. This is done by saving the preferences in a table and then pulling those preferences from the table in the subform's Form_Load event. This all works fine, but I'm trying to add the ability to save frozen columns as well. I'm able to track how many columns are frozen, and I'm also able to restore the order of the columns, so it should be a simple matter to unfreeze all of the columns and then refreeze the first X columns.
I know that I can use 'RunCommand acCmdUnfreezeAllColumns' to unfreeze all of the columns. I also know that I need the focus to be on the form, so I tried to do myForm.setFocus (where myForm is a reference to the subform) before unfreezing the columns. This gives the error "There is an invalid method in an expression". I thought maybe this was because you can't set focus to a form with enabled controls, so I tried myForm.Controls(0).SetFocus instead. This doesn't give an error, and it seems to set the focus to the first cell in the subform based on the value of Screen.ActiveControl (and the fact that the cell has the focus after the form loads). However, I then get the error "The command or action 'UnfreezeAllColumns' isn't available now." on the line RunCommand acCmdUnfreezeAllColumns. I also thought maybe I was having problems because I was trying to focus in a load event, so I tried calling the repaint method first, but that didn't make any difference. I thought about unfreezing all of the columns in the design view so that I wouldn't need to unfreeze them at runtime, but the user may not have any preferences saved and I want some columns frozen by default.
I'm running out of ideas on this. What exactly needs to happen for the command to unfreeze all columns to work? Or is there an easier way to do this that I'm missing? The solution needs to be versatile enough to work on any subform with any field names. Note that I do have a list of the field names that were present when the user saved their preferences, but I cannot guarantee that they will still all exist when their preferences are restored.
This normally worked for me:
myForm.SetFocus
RunCommand acCmdUnfreezeAllColumns
However, sometimes it failed, so I tried to repeat it:
On Error Resume Next
myForm.SetFocus
RunCommand acCmdUnfreezeAllColumns
If Err.Number <> 0 Then
myForm.SetFocus
RunCommand acCmdUnfreezeAllColumns
End If
On Error GoTo 0
That nearly always worked, but the menu commands are not 100% reliable.

access vba requery oddness

It's been awhile since i've worked in VBA. I have a bound form. It has two drop down lists. One list is bound the other not (the first ddl is a list of values. The second gets refreshed when the first one changes, using the value of the first to create a query for the second. That value is used as a fk in the table the form is bound too).
Anyway, when the form is first run and uses the default value for ddl 1, if the second combobox is empty, and I try to get the value, it's null, which is what you would expect. But, I have code that runs when ddl1's value changes, to requery ddl2. When it requeries, if the list is empty, and I do combobox1.value, instead of being null, the value is 1. This is confusing, because, since the list is empty, I would think it should be null. What's going on here? Here is what I have:
Combo1 is bound to a table
Combo 2 uses this query:
SELECT tbl_office.id, tbl_office.office_name
FROM tbl_office
WHERE (((tbl_office.otherTable_id)=[Forms]![dlg_addDivision].[Combo1]));
On Combo1 afterUpdate event:
me.Combo2.requery
So, after combo1 afterUpdate, the above sql gets called. If this produces an empty dataset, and I try to get the value of combo2, even though the list is empty, the value says it's 1
thanks
The requery requeries the List - not the value. The value stays whatever it is/was before. Its not a bug - its a feature ;-)
If you page through datasets that are already filled you wouldn't want them to be changed without user interaction. The List is just a helper for dataEntry (and validation - if you check "only listitems allowed") - but it has nothing to do with your dataset.
By setting the value to your first Entry like you proposed: Me.Combo2.Value = Me.Combo2.ItemData(0) you change the dataset intentionally. And that is how it is supposed to happen. Not via changing the list.

"Run time error 3167: Record is deleted" when unchecking a selection in multi select listbox

I have a form that contains a multi-select list box, a user can check off as many selections as required, then hit the "save" button, which saves the form fields, and the checked off selections in their respective tables.
Everything works perfectly fine, until a user unchecks a selection that had previously been saved.
I have tried several things, including refreshing the form, and the recordset, to no avail.
While testing to find a solution, I commented out every single line of VBA code tied to the form's save button. When clicking this button, obviously nothing happens with the code commented out, but if I try and close the form with the window's "x" button, I then get a message box stating "Record has been deleted"
I have no idea where to look at this point, I've tried to compact and repair database, also with negative results.
I have no code to post, as it doesn't matter what code I try to run, I get the error, a completely empty sub still gives me the error.
Again, this only happens when a user unchecks a selection that previously had been checked off.
Here is a screen shot of the form:
The list box in red is the culprit.
A user can edit every single field on the form and it all works fine, a user can even "add" selections from the listbox by checking items off, and it will save them, and show them the next time the record is viewed.
The problem occurs when a user unchecks one of the selections that were previously checked off.
Details of the listbox:
It is a multi select list box populated by a "lookup" that was created with the listbox wizard
The values selected are saved in a field as a comma separated list
The field itself is a lookup of another table, that allows multiple values
At this point I'm not even sure I'm explaining myself properly, I've gone so far down the "rabbit hole"!
If any clarifying statements are needed please ask away.
The following describes my implementation of what I think you were trying to do, but there may be some variations. The key point is I was able to reproduce your 'Deleted record' error on a regular basis, but somehow finally got it to stop.
I created a table named 'res_area' with an ID field and an 'area'. I populated with rows for '1E, 1F, 1I, 1J, 3C, 3D, 3E, 3F, 3I, 3J, 3K, etc.'
I created table 'res_tow' with all the fields you show on your form. I included a field named 'area' that is a LOOKUP field with the following source: "SELECT [res_Area].[TTID], [res_Area].[Area] FROM [res_Area] ORDER BY [Area];". Allow Multiple Values = yes.
I created form 'frmEditTow' with the Record Source:
SELECT res_Tow.TowID, res_Tow.TCompany, res_Tow.TPhone1, res_Tow.TPhone2,
res_Tow.TPhone3, res_Tow.TType, res_Tow.TTown, res_Tow.TAddress,
res_Tow.TFileName, res_Tow.TComments, res_Tow.TChecks, res_Tow.area
FROM res_Tow;
I added the 'Save Record' button with code to: 'DoCmd.RunCommand acCmdSaveRecord' and 'Me.Requery'
I am able to add or delete any combination of list items and save the changes.
For what its worth, I think my earlier version of the rowsource for the form included field 'area' and 'area.value'. With that, the form recordcount reflected the total number of listbox items selected - not the number of rows in table 'res_Tow'.
Good luck!

Control can't be edited; it's bound to an unknown field [FieldName]

I have 2 bugs beginning to show up in my app, both in subforms. New text boxes, labels and check boxes will only display in design view.
Bigger problem: I can add a check box by dragging it from the field list but get the error 'Control can't be edited; it's bound to an unknown field [FieldName]' when I attempt to click it in form view.
I can edit the value directly in the table and I can edit the value in the form's underlying query as well.
I've tried decompiling/recompiling and importing all my objects into a new database. I tried using a different machine to see if there was something wrong on mine. nada
This is an existing application with hundreds of man hours into it. I can't just start from scratch.
I was getting this - It took me a while to realize because the error statement only persisted while data list item was click-depressed. It turns out in my case that somehow a number of spaces existed in the Control Source property. I found this when I entered the table name in the field and the error statement switched from Control can't be edited; it's bound to an unknown field '(bunch of spaces) ' to Control can't be edited; it's bound to an unknown field [tableName]. After deleting the [tablename] and ensuring no spaces in Control Source, the combobox toggled to 'unbound' and there was peace in the valley.
I had a similar problem. After many hours of work I discovered that all of a sudden I had to add the table name to the Control Source property.
Go to the properties for the control and add the table name i.e. tbl_MyTable.MyField.
I have no idea why Access sees an ambiguity problem all of a sudden for just that one field but that was obviously the case.
I had this problem and tried adding the table name to no avail. On the load event of the form, I changed the recordset (same table but filtered using what is passed by me.openargs instead of on the docmd.openform command due to needing the id passed for other things to work) in VBA. I thought that once you set the form to the recordset, you were done and could close the recordset. Not so, leave the recordset open until you actually leave the form or this error will occur.
Change the control source from "=[Some_Value_in_Table]" to [Some_Value_in_Table]. To be more precise remove the equal to sign.
I tried all these things - to no avail. I had a few fields bound and only one of them was causing grief - the others were fine. I swapped the Control Source around between controls to isolate the issue - it was definitely only related to the field somehow - after refreshing external links, removing and re-adding the offending field from the Access query opening and closing the offending form - everything somehow fell into place - I can't say exactly what fixed the problem - but my life can now return to normal! In a nutshell - these are the steps I followed:
* refresh links to external table
* remove and re-add fields in query
* confirm the the field is editable at the query level
* copy and paste the table.fieldName from the query's sql statement
* save - close query
* paste into the Control Source
* save and close form!
Good luck!

Custom row source for combo box in continuous form in Access

I have searched around, and it seems that this is a limitation in MS Access, so I'm wondering what creative solutions other have found to this puzzle.
If you have a continuous form and you want a field to be a combo box of options that are specific to that row, Access fails to deliver; the combo box row source is only queried once at the beginning of the form, and thus show the wrong options for the rest of the form.
The next step we all try, of course, is to use the onCurrent event to requery the combo box, which does in fact limit the options to the given row. However, at this point, Access goes nuts, and requeries all of the combo boxes, for every row, and the result is often that of disappearing and reappearing options in other rows, depending on whether they have chosen an option that is valid for the current record's row source.
The only solution I have found is to just list all options available, all the time. Any creative answers out there?
Edit Also, I should note that the reason for the combo box is to have a query as a lookup table, the real value needs to be hidden and stored, while the human readable version is displayed... multiple columns in the combo box row source. Thus, changing limit to list doesn't help, because id's that are not in the current row source query won't have a matching human readable part.
In this particular case, continuous forms make a lot of sense, so please don't tell me it's the wrong solution. I'm asking for any creative answers.
I also hate Access, but you must play with the cards you are dealt.
Continuous forms are a wonderful thing in Access, until you run into any sort of complexity as is commonly the case, like in this instance.
Here is what I would do when faced with this situation (and I have implemented similar workarounds before):
Place an UNBOUND combobox on the form. Then place a BOUND textBox for the field you want to edit.
Make sure the combobox is hidden behind (NOT invisible, just hidden) behind the textBox.
In the OnCurrent event fill the listBox with the necessary data. Go ahead and "Limit to list" it too.
In the OnEnter or OnClick event of the textBox give the combobox focus. This will bring the combobox to the forefront. When focus leaves the combobox it will hide itself once more.
In the AfterUpdate event of the combobox set the value of the textbox equal to the value of the combobox.
Depending on your situation there may be some other details to work out, but that should more or less accomplish your goal without adding too much complexity.
use continuous forms .. definitely. In fact you can build entire applications with great and intuitive user interface built on continuous forms. Don't listen to Toast!
Your solution of listing all options available is the correct one. In fact there is no other clean solution. But you are wrong when you say that Acccess goes nuts. On a continuous form, you could see each line as an instance of the detail section, where the combobox is a property common to all instances of the detail section. You can update this property for all instances, but cannot set it for one specific instance. This is why Access MUST display the same data in the combobox for all records!
If you need to accept only record-specific values in this combobox, please use the beforeUpdate event to add a control procedure. In case a new value cannot be accepted, you can cancel data update, bringing back the previous value in the field.
You cannot set the limitToList property to 'No' where the linked data (the one that is stored in the control) is hidden. This is logical: how can the machine accept the input of a new line of data when the linked field (not visible) stays empty?
You could also make the value of the combo box into an uneditable text field and then launch a pop-up/modal window to edit that value. However, if I was doing that, I might be inclined to edit the whole record in one of those windows.
I don't think that Access continuous forms should be condemned at all, but I definitely believe that they should be avoided for EDITING DATA. They work great for lists, and give you substantially more formatting capabilities than a mere listbox (and are much easier to work with, too, though they don't allow multi-select, of course).
If you want to use a continuous form for navigation to records for editing, use a subform displaying the detailed data for editing, and use the PK value from the subform for the link field. This can be done with a continuous form where you place a detail subform in the header or footer, linked on the PK of the table behind the continuous form.
Or, if you are using a continuous form to display child data in a parent form, you can link the detail subform with a reference to the PK in the continuous subform, something like:
[MySubForm].[Form]!MyID
That would be the link master property, and MyID would be the link child property.
We also encounter this a lot in our applicatins. What we have found to be a good solution:
Just show all rows in the comboboxes.
Then, as soon as the user enters the compobox in a specific row, adjust the rowsource (with the filter for that row). When the combobox loses the focus, you can re-set the rowsource to display everything.
I have a simpler way to go than Gilligan. It seems like a lot of work but it really isn't. My solution requires having my continuous form as a subform datasheet. On my subform I have two lookup comboboxes, among other fields, called Equipment and Manufacturer. Both simply hold a Long Integer key in the data source. Manufacturer needs to be filtered by what is selected in Equipment. The only time I filter Manufacturer.RowSource is in the Manufacturer_GotFocus event.
Private Sub Manufacturer_GotFocus()
If Nz(Me.Equipment, 0) > 0 Then
Me.Manufacturer.RowSource = GetMfrSQL() '- gets filtered query based on Equipment
Else
Me.Manufacturer.RowSource = "SELECT MfgrID, MfgrName FROM tblManufacturers ORDER BY MfgrName"
End If
End Sub
In Manufacturer_LostFocus I reset Manufacturer.RowSource to all Manufacturers as well. You need to do this because when you first click in the subform, GotFocus events fire for all controls, including Manufacturer, even though you are not actually updating any fields.
Private Sub Manufacturer_LostFocus()
Me.Manufacturer.RowSource = "SELECT MfgrID, MfgrName FROM tblManufacturers ORDER BY MfgrName"
End Sub
In the Enter event of Manufacturer you have to check if Equipment has been selected, if not set focus to Equipment.
Private Sub Manufacturer_Enter()
If Nz(Me.EquipmentID, 0) = 0 Then
'-- Must select Equipment first, before selecting Manufacturer
Me.Equipment.SetFocus
End If
End Sub
You also need to requery the Manufacturer combobox in Form_Current event (i.e. Me.Manufacturer.Requery), and you should set the Cycle property of this subform to "Current Record".
Seems simple enough, but you're not done yet. You also have to reset Manufacturer.RowSource to all Manufacturers in the SubForm_Exit event in the parent form in case the user goes to the Manufacturer combobox but does not make a selection and clicks somewhere on the parent form. Code sample (in parent form):
Private Sub sFrmEquip_Exit(Cancel As Integer)
Me.sFrmEquip.Controls("Manufacturer").RowSource = "SELECT MfgrID, MfgrName FROM tblManufacturers ORDER BY MfgrName"
End Sub
There is still one piece of this that is not clean. When you click on Manufacturer and have multiple rows in the datasheet grid, Manufacturer field will go blank in other rows (the data underneath the comboboxes is still intact) while you're changing the Manufacturer in the current row. Once you move off this field the text in the other Manufacturer fields will reappear.
This seems to work well.
CBOsfrmTouchpoint8 is a combobox shortened to just the dropdown square.
CBOsfrmTouchpoint14 is a textbox that makes up the rest of the space.
Never say never:
Private Sub CBOsfrmTouchpoint8_Enter()
If Me.CBOsfrmTouchpoint8.Tag = "Yes" Then
CBOsfrmTouchpoint14.SetFocus
Me.CBOsfrmTouchpoint8.Tag = "No"
Exit Sub
End If
Me.CBOsfrmTouchpoint8.Tag = "No"
Me.CBOsfrmTouchpoint8.RowSource = "XXX"
Me.CBOsfrmTouchpoint8.Requery
Me.CBOsfrmTouchpoint8.SetFocus
End Sub
Private Sub CBOsfrmTouchpoint8_GotFocus()
Me.CBOsfrmTouchpoint14.Width = 0
Me.CBOsfrmTouchpoint8.Width = 3420
Me.CBOsfrmTouchpoint8.Left = 8580
Me.CBOsfrmTouchpoint8.Dropdown
End Sub
Private Sub CBOsfrmTouchpoint8_LostFocus()
Me.CBOsfrmTouchpoint8.RowSource = "XXX"
Me.CBOsfrmTouchpoint8.Requery
End Sub
Private Sub CBOsfrmTouchpoint8_Exit(Cancel As Integer)
Me.CBOsfrmTouchpoint14.Width = 3180
Me.CBOsfrmTouchpoint8.Width = 240
Me.CBOsfrmTouchpoint8.Left = 11760
Me.CBOsfrmTouchpoint8.Tag = "Yes"
End Sub
What if you turn off the "Limit To List" option, and do some validation before update to confirm that what the user might have typed in matches something in the list that you presented them?
Better...
Set you combo box Control Source to a column on the query where the values from your combo box will be stored.
For Me I think the best way and easiest way is to create a temporary table that has all your bound fields plus an extra field that is a yeas/no field.
then you will use this table as the data source for the continuous for. You can use onLoad to fill the temporary table with the data you want.
I think it is easy after that to loop for the choices, just a small loop to read the yeas/no field form the temporary table.
I hope this will help
Use OnEnter event to populate the combo box, don't use a fixed rowsource.
I've just done similar. My solution was to use a fixed row source bound to a query. The query's WHERE clauses reference the form's control i.e. Client=Forms!frmMain!ClientTextBox. This alone will fill the combo boxes with the first row's data. The trick then is to set an 'On Enter' event which simply does a re-query on the combo box e.g. ComboBox1.Requery, this will re-query that combo box alone and will only drag in the data related to that record row.
Hope that works for you too!
Disclaimer: I hate Access with a passion.
Don't use continuous forms. They're a red herring for what you want to accomplish. Continuous forms is the same form repeated over and over with different data. It is already a kludge of Access's normal mode of operation as you can't have the same form opened multiple times. The behavior you are seeing is "as designed" in Access. Each of those ComboBox controls is actually the same control. You cannot affect one without affecting the others.
Basically, what you have done here is run into the area where Access is no longer suitable for your project (but cannot ditch because it represents a large amount of work already).
What seems to be the most likely course of action here is to fake it really well. Run a query against the data and then create the form elements programmatically based on the results. This is a fair amount of work as you will be duplicating a good bit of Access's data handling functionality yourself.
Reply to Edit:
But as they are, continuous forms cannot accomplish what you want. That's why I suggested faking out your own continuous forms, because continuous forms have real limitations in what they can do. Don't get so stuck on a particular implementation that you can't let go of it when it ceases to work.