I am using Access 2016, and here is my Access form:
When a new record is created, I need the user to click whether or not the clinical case is true. Moreover, I need the true/false options to be unmodifiable upon clicking and either a trueCase or falseCase instance is created. "True" and "False" will generate the values 1 and 2, respectively, and be stored in the statusTrueFalse field of my Cases table. I don't have any idea of how to proceed from there and would greatly appreciate the community's feedback/hints.
Here is my relationship diagram:
If true, I would like to have the open date/close date and reason closed fields appear. Thank you for your help!
You could change the subform to a list box control with a row source equal to the original subform query.
You could then add macros to the AfterUpdate() event of the list box to show your True/False command buttons (Visible=True) when the user selects a row in the list box. Then add OnClick() events to the command buttons to make them visible=false so they can only choose once.
Add any processing routines to the OnClick() event needed to capture the True vs. False values and store them in your table. You should be able to reference the list box control in your macro/code to choose the correct row in your table to update, or grab other information from the list box to generate a new record.
You might want to add a requery method to the command buttons OnClick() events to also filter any records that have already been answered true/false in the list box to avoid someone re-selecting a row they already answered and adding another True/False record.
Edit: AFAIK Access can't determine which row is selected in a subform, which is why I suggested using a list box instead.
Edit 2 (sorry): I missed your last question. You can have the date fields and reason for closure fields work the same way as your true/false command buttons. Make them visible=false by default and in the OnClick() event for the True command button, set those fields to visible=true, pause your code to wait for entries, then trigger off of another command button or AfterUpdate() event of the last field in your tab order to add the data to your table. Let me know if you need further explanation, I just finished a similar application.
Related
I am pretty much a newbie to using VBA in Access and I'm having trouble with something that seems like it should be quite simple.
I have two listboxes (called LB1_ID and LB2_ID) on my form (MainForm) that I want to list related IDs from their respective Row Sources. I need LB2 to be populated based on the selection in LB1. They both have Row Sources from the same Table (Table1) and it is a many to many relationship of Requirement IDs ("Req ID1" and "Req ID2"). My current form, which is not working, has the Row Source of LB1 as:
SELECT Table1.ID, Table1.[Req ID1] FROM Table1 ORDER BY Table1.ID;
and the Row Source of LB2 as:
SELECT Table1.ID, Table1.[Req ID2] FROM Table1 WHERE ([Forms]![MainForm]![LB1_ID]=Table1.[Req ID1]);
When I make a selection in LB1, nothing happens in LB2. The column widths are formatted correctly and I can get it to work if I use Me.[Forms]![MainForm]![LB1_ID] but I have to type out the LB1 selection manually in a popup box if I use that.
What am I missing?
If your listbox is multi-select, you cannot use a simple form reference as query criteria. If it is not multi-select, keep in mind that its value may be a hidden column (usually an ID field), so there are two possible issues and solutions:
Possible Issues:
Single-Select listbox has an ID field that is hidden (column width = 0") and you are matching it to the wrong field in your table. To check the output of the listbox, open the VBE and type ?[Forms]![MainForm]![LB1_ID] into the immediate window and press enter when your form is open in form view and a row is selected in LB1_ID. If the returned line is what you expect, then the problem must be elsewhere.
Multi-Select listbox property is enabled. In this case, your query will not work, because the listbox will only return Null. You will need to write some VBA to loop through the rows and figure out which ones are selected, which is a bit of a pain. Ultimately you'll build some code that will alter your query with the specific criteria for each selected row. Instead of explaining here, take a look at this article for a tutorial.
The .Requery method is still important to put in the AfterUpdate event of your first listbox to refresh the second.
Your query seems to work, but you need to refresh your listbox2 whenever you make selection into listbox1, so if both listbox are in the same form add this event handler :
Private sub LB1_ID_Change()
Me.LB2_ID.Requery
End sub
Without this, your listbox2 will only get populated once on load based on the initial value of listbox1.
Also, if you have not already done it, I would recommend to add your listbox1 control as a parameter into your listbox2 query (in query builder, right click -> parameters).
Background: The Record Source for my form is a query ("BigQuery") that just combines several related tables. I am setting up combo boxes to edit fields; the Control Source for these combo boxes is always just a field from BigQuery. One of the fields is UnitType, and another is UnitSubType. There is about 100 distinct entries for UnitSubType, but many of them make no contextual sense when paired with a particular UnitType: If UnitType="Car", then UnitSubType="18 wheeler" makes no sense, and I'd just as soon not give the client the opportunity to make mistakes.
Question, Part A: When the user chooses a value for UnitType on the form, I would like to limit the combo box for UnitSubType to those UnitSubType values already paired with UnitType values in the database. How is this done?
Example: If 1 or more instances of a record containing UnitType="truck" and "UnitSubType="18 wheeler" already exist in the table, then assuming that the user has already selected "truck" in the combo box for UnitType one of the choices presented in the combo box for UnitSubType should be "18 wheeler".
Question, Part B: I would also like for the user to be able to add a new UnitSubType simply by typing it into the combo box: if the user has already selected "truck" in the combo box for UnitType and manually types "flatbed" in the combo box for UnitSubType, then the edited record should have "flatbed" in the UnitSubType and future editing operations should include "flatbed" as a UnitSubType choice whenever the UnitType is "truck". In simpler situations setting "Allow Value List Edits" to "Yes" took care of this, but I want to make sure this functionality is available in the solution provided to Question Part A.
There are similar question threads already in SO, but I am such a noob at Access that I have been unable to extrapolate the answers to fit my need. I am sorry; please, be as specific as possible.
Thank you so much!
Dave
I've arrived at one solution that I wanted to share. Access is difficult to describe, but I will list exceptions to the norm and try to communicate the solution that way.
First (primary) Combo Box for picking the Unit Type:
Name: CBUnitType
ControlSource: UnitType '''A field in the Main Table: MainTbl
Row Source:
SELECT DISTINCT MainTbl.UnitType
FROM MainTbl
ORDER BY MainTbl.UnitType;
Bound Column: 1
Allow Value List Edits: Yes
Locked: No
After Update: [Event Procedure] '''See subroutine: ComboBoxUnitType_AfterUpdate() shown below.
Second (dependent) Combo Box for picking the Unit Sub-Type:
Name: CBUnitSubType
ControlSource: UnitSubType '''A field in the Main Table: MainTbl
Row Source: ComboQueryUnitSubType '''Built by Query Builder, detailed below.
Bound Column: 1
Allow Value List Edits: Yes
Locked: No
Query Builder object: ComboQueryUnitSubType
SELECT DISTINCT [MainTbl].UnitSubType
FROM [MainTbl]
WHERE ((([MainTbl].UnitType)=[Forms]![Unit Editor]![UnitType]))
ORDER BY [MainTbl].UnitSubType;
VBA subroutine created by selecting "[Event Procedure] for the After Update event in combo box "CBUnitType" described above. The VBA subroutine is automatically placed in the Microsoft Office Access Class Objects folder in the VBA environment in a module named: "Form_Unit Editor". The subroutine name is also pre-chosen to be: "ComboBoxUnitType_AfterUpdate()" Almost certainly if you change any of these names the linkages will break horribly. The VBA code in the module is:
Option Compare Database
Option Explicit
Private Sub ComboBoxUnitType_AfterUpdate()
Forms![Unit Editor]![ComboBoxUnitSubType].Requery
Forms![Unit Editor]![ComboBoxUnitSubType].Value = ""
End Sub
So, the effect is this: After the user updates the combo box for Unit Type, the vba routine executes and re-queries the query for the combo box for Unit SubType, and then it arbitrarily takes the .value parameter of the Unit SubType combo box and clears it to the empty string.
I would like to gratefully acknowledge the generous tutorials provided by Blue Claw Database Design. Specifically, the VBA code that re-queries the combo box query was a life-saver, and is detailed in their tutorial on Dependent Drop Down List Box Why the query ComboQueryUnitSubType, specified as the combo box's Row Source, is not re-run automatically by Access every time the combo box is selected by the user is anyone's guess.
I'm sorry for both the long-winded question and answer. I hope to be heading back to some nice, terse code in the near future!
Dave
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!
I have a form with a few bound fields and a few 'custom made' checkboxes, which set values per vba. When I now start a new record with 'DoCmd.GoToRecord , , acNewRec', Access clears my form but won't create a fresh ID until a value is entered into one of the bound fields. Since my checkbox-fields are not bound, any changes before a bound field has been edited won't be saved. I tried adding values via sql statements, but access throws an error after I change another field stating that the current recordset has been changed so I doubt that this is the way to go.
My form is based on a query and witch vba I set the checkboxes like [value_x] = true (which works fine when I first enter data into a bound field and thereby a new record is created).
(Another way to avoid this would be to set any bound field during the onload-event to a value and remove the value afterwards. But that's not very clean I guess ..)
After testing a few approaches I found my earlier stated idea to be the easiest approach. This means I set a value of a bound field in the form_load event and work with me.dirty where neccessary.
Of course I have to delete empty recordsets afterwards (if someone only opens and closes the form), but that can be handled very easy.
Another great idea was the one Robert Harvey gave me. He suggested to add a click event to my unbound custom checkboxes and use this to change a hidden bound checkbox field, which also worked great.
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.