I've create a form with a tab control in MS-Access.
On one of the tabs I have 2 subform controls showing records from the same table. This table has a field I've called "Status" and can have an entry of either "Open" or "Closed"; the 2 subforms simply show these 2 options filtered as separate groups on the tab control.
I then have a button underneath each of the subforms; the user selects (clicks into) one of the records in either of the subforms and then the button will open a new "Edit" form at the selected record in subform so they can see more information and also toggle the "Status" field to "Open" or "Closed".
When the user presses the "Save and Close" button this saves the changes, closes the "Edit" form, and re-queries the 2 subforms.
What I'm trying to do is get a count of the number "Open" records still showing after the "Edit" form is closed. The count number is then to be passed in to the tab caption for this tab so users can see at a glance how many "open" records are against the record on this main form.
Here's my code so far:
Private Sub cmdAdminIssue_Edit_SaveClose_Click()
' save and close currently open edit form
DoCmd.Close acForm, "tblAdminIssue_Edit", acSaveYes
' requery the subforms to show the effect of the edits made via the edit form
Forms![tblJobs]![tblAdminIssue_Sub_Open].Form.Requery
Forms![tblJobs]![tblAdminIssue_Sub_Closed].Form.Requery
' count number of records still open and pass the number through to the tab control caption property
Dim AdIssOpenCount As Long
AdIssOpenCount = DCount("JobID", "qryAdminIssue_Open", "JobID = '" & Me![JobID] & "'")
Forms![tblJobs]![tab_AdIssues].Caption = "Admin Issues (" & AdIssOpenCount & ")"
End Sub
The above code triggers the error: "The expression you entered refers to an object that is closed or doesn't exist", highlighting my DCount expression in debug.
However I can get the DCount to work independently of the other code above it; they seem to be interfering with each other, but I don't quite understand how.
Referring to the error message above it seems to be suggesting that DCount needs the query I referenced to open in order to run... this doesn't make much sense to me as I thought these kinds of functions do not require you to explicitly open in code an object it's trying to get data from... I'm probably misinterpreting what this error actually means though.
Any explanation of the error and a possible workaround would be much appreciated. Thanks.
While you've clearly done your best to explain the situation it seems pretty complicated.
I'm going to try and break it down a bit, and suggest something you might do.
"I've create a form with a tab control in MS-Access.
On one of the tabs I have 2 subform controls showing records from the same table.
This table has a field I've called "Status" and can have an entry of either "Open"
or "Closed"; the 2 subforms simply show these 2 options filtered as separate groups on
the tab control."
So, you have a form, with 2 subforms. That they're on a tab page shouldnt really be relevant. I'm going to reference the form as frm & the subforms as sf_o & sf_c.
Button underneath each subform (opening the detail form with a double-click might be nicer) - the "save & close" button is presumably in the popup screen; This button is currently performing the save of it's own data, and doing everything else as well.
This is very messy, as it means that the contents of frm cannot be modified without having to make changes to the logic of the save/close button as well. A better approach would be to separate the logic for refreshing the main form from the editting form using a public method in the main form (frm):
Public Sub RefreshData()
sf_o.Form.Requery
sf_c.Form.Requery
'other logic
End Sub
then, you have:
Private sub cmdAdminIssue_Edit_SaveClose_Click()
'save & close logic...
Forms(frm).RefreshData
End sub
Okay now, the other logic. You want to find out how many records are now "Open" and put this information somewhere. In this case, somewhere is a tabpage caption, but this isnt really material.
I never use DCount (DLookup etc) as it is very slow. An alternative (and in this case, much simpler) method would be to use the forms own properties:
tab_AdIssues.Caption = "Admin Issues (" & trim(sf_o.Form.RecordsetClone.RecordCount) & ")"
How's that? Then you dont need to try and figure out what's gone wrong in all that pre-compiled logic which is microsoft access.
Hope this helps
Related
I have a bound userform with lot of fields in it, I use submit button to let user make entries in shared form. But whenever user clicks on submit button it requery whole form. However, there are around 4-5 fields which should not be cleared on submit button, they should retain the value and rest of the fields should get cleared on every submit button click.
Can this be done in access and how ? below is the simple code is use to submit and requery the record.
Private Sub SaveBtn_Click()
RunCommand acCmdSaveRecord
Me.Requery
Me.DataForm.Requery
End Sub
When you have a "Bound Form" in Access, this means that the Access Form is tied to the underlying data source, which is specified by, and stored in, the "Record Source" property for the form. In the "SaveBtn_Click()" method you provided above, this code does the following things:
RunCommand acCmdSaveRecord - Saves the current record in the form.
Me.Requery - Requeries the entire form.
Me.DataForm.Requery - Requeries the subform named "DataForm" on your main form.
So when you "Requery" the entire form (depending on how the "Record Source" and other form property settings are setup), the requery operation in some cases moves the cursor to the new record in the data source (...its the default settings for the drag and drop form designer tools in later versions of Access), and I suspect that is why you see the form being "cleared" when you call "Requery." A couple of items I would point out, just for clarity:
Note 1: "Requery" actually saves the record, so explicitly calling "RunCommand acCmdSaveRecord" should not be necessary if you're going to call "Requery" too.
Note 2: Depending on how the Record Source (and other) form properties are set, Requery in some cases just saves and refreshes the currently selected record, but it doesn't sound like that is how your form is working (based on what you said above), so I'm assuming that is not the case in your form.
Note 3: The subform will also be requeried when you call "Requery" for the main form, so that line of code may also be redundant here. The reason to call "Me.DataForm.Requery" is if you only want to requery the subform and NOT requery the entire main form.
Note 4: The "DataForm" subform (on your main form) will have a separate data source for it's own "Record Source" property, which is separate from the parent (main) form, so it is important to be aware of that fact, depending on which field values you want to save.
So, that said, there are a couple of options I might suggest, depending on exactly how you want your form to behave:
If you want to keep some of the field values and use those for the NEW RECORD when you hit the requery button, you could always save them off in a variable and then set those controls again after requerying from your variables. Just create a seperate variable for each value you want to save, copy the TextBox control values into each of variables respectively, call requery on the form, and then copy those values back into your TextBox controls after you requery. That code would be something like the following (depending on the exact names of your TextBox controls, I used the fictitious name "txtMyTextBox" for this example):
Private Sub SaveBtn_Click()
Dim vValue1 as Variant
vValue1 = txtMyTextBox
Me.Requery
txtMyTextBox = vValue1
End Sub
Or, if you're just trying to create "Default Values" for certain controls, TextBox controls have a "DefaultValue" property you can set to any value you would like to use for the default. Those can be set from the Property Sheet on the right side of the Access window when the form is opened in Design mode or Layout mode and the TextBox control is selected. (Press F4 to open the Property Sheet if it's not already open).
But, if you really want to Requery and then go back to the same record you were previously on, you could try the following code:
Private Sub SaveBtn_Click()
Dim iRecord as Integer
iRecord = Me.CurrentRecord
Me.Requery
DoCmd.GoToRecord , , acGoTo, iRecord
End Sub
Anyway, I hope this all makes sense and you find it helpful. Please let me know if you have any further questions about this answer.
I've been tasked with making updates to an MS Access Database and its forms.
Each form seems to be linked to a query. If I enter data into a text box on the form and then close the form without pressing the Save Record button a new record is still added to the Database which makes no sense to.
Any insight would be great, I'm a programmer but have little experience working with Access Forms and Databases.
Thanks.
Microsoft Access binds forms to data by default, and will automatically save data as soon as you either move between records or close the form you're working on. For the average user, this is actually a good thing because it makes it very difficult to lose data, even if you accidentally close the form after making an edit.
If this functionality isn't what you're looking for, I'd suggest removing the binding from the form, that is, set the Record Source property to blank, then manipulate all the data in code using unbound controls. It's a lot more fiddly, but it gives you a lot more control.
The other option would be to use the form's BeforeUpdate event to ask the user if they want to save their changes before allowing them to go through.
If your main concern is accidentally adding new records, set the Allow Additions property to false, and create new records programmatically.
A sample of a BeforeUpdate event procedure (the same code would work for the BeforeInsert event):
Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim intAns As Integer
intAns = MsgBox("Are you sure you want to save this record?", vbQuestion + vbYesNo, "Save Record")
If intAns = vbNo Then Cancel = 1
End Sub
If you were to use the above method, and the user clicked "No", they would have to then click the ESC key to undo the changes they had entered. Otherwise it would keep showing the dialogue when they moved off the record.
Is there a way to make Access write values immediately to a table when changing values in a form? Right now, I have to change a value in the form, and then click off the field onto the subform before the value is written to the corresponding table.
Sounds like no big deal, but certain controls require current data to give valid options. If the user doesn't know enough to click into the subform then the data they view could be out-of-date.
The behavior in your requirement are asking for is by default how access functions. In most typical scenarios your master form is going to be the parent table, and as a result before you can add child records to such a relational setup, then the parent record would have to be written and saved to disk.
And the reverse while not a requirement is also the default operation for access.
In other words it's not clear why when the focus moves from your parent form to a sub form, that the parent form's record is not been written to the table.
So something in your setup is incorrect here, or you'll have to expand on the behavior you're witnessing.
By default changing the focus from the main form to a sub form will cause the main form record to be written to the table, and changing the focus from a sub form tool main form as a general rule should also cause a sub form record to be written to the table.
If you are matching a combo box, you can use that as your link master field, that is, the name of the control itself. Your subform should update as soon as you select a ticket with the combo.
Link Master Fields: Combo1
Link Child Fields: Field1
You can force the save of the record when a user moves off a field in the Lost_Focus event.
Private Sub MyField_LostFocus()
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
End Sub
I was also facing the same problem, but in another scenario. I am not having sub-form, but a datasheet view of the same table on a split screen, which was not updating immediately after save button. On pressing DUPLICATE button, instead of getting duplicate data of latest record, I was getting pointer shifted to first record, and then the data from the first record was getting duplicated.
By inserting
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
before data duplicating code, I am now getting the data updated immediately with record pointer stick to the current record, and the same last record gets duplicated as required.
Sharing the experience.
Thanks ChrisPadgham
Just use this in your form model:
Private Sub {your textbox name her}_AfterUpdate()
Me.Refresh
End Sub
Essentially: in the AfterUpdate event of every desired control execute a acCmdSaveRecord command.
So:
In the design view of the form click on your control.
Menu > Design > Property Sheet (to bring up the property sheet for your control)
Property Sheet > Event [tab] > After Update. Choose "[Event Procedure]". Click on the elipsis "..." to take you through to the Code-Behind-Form VBA dev environment.
Supply your code as follows ...
Private Sub [Control Name]_AfterUpdate()
DoCmd.RunCommand acCmdSaveRecord
End Sub
E.g.
Private Sub cboFinancialYear_AfterUpdate()
DoCmd.RunCommand acCmdSaveRecord
End Sub
Repeat the above for every relevant control on the form.
The acCmdSaveRecord avoids an unecessary visual refresh of the current form, as when using Me.Refresh (which #medjahed mohamed has suggested).
I have only one table and I would like to create a form for the users to populate the table easily. There are at least 5 fields in the table that only needs to be completed if a certain type of inspection (Fire) is selected from a drop down list and is left blank otherwise.
I would like to create a subform that will only pop up when inspection type "Fire" is selected from the drop down list in the main form. How can I do this?
I used the wizard to create the form and I am stuck because I really don't know VBA. So far, I went to the inspection type field on the form, clicked on "Properties", then clicked on "After Update", then selected the Macro that I created to open the subform when the inspection type = "Fire", but it isn't working.
What happens is the subform gets opened up no matter what type of inspection I select, then the ID number on the subform doesn't correspond to the main form (the subform ID will remain on id#1). Also, when I do input data in the subform, the information ends in the next record.
I was wondering if that is happening because I am using both the form and the subform to enter data into the same table. I hope this is a clear explanation of what I want to do.
Just to try to improve a bit on Kovags excellent proposition:
Sub InspectionType_AfterUpdate
Subform1.Visible=(InspectionType.Text="Fire")
End Sub
Sub Form_Current
InspectionType_AfterUpdate
End Sub
Just change this code to adapt your needs and add it to the Change Event of the Inspection Type textbox:
Sub InspectionType_Change
if InspectionType.Text="Fire" Then
Subform1.Visible=True
else
Subform1.Visible=False
endif
End Sub
If the fields are on the same table then I'd suggest placing the fields on the subform onto the main form. Then making them visible or not depending on the state of the combo box.
Using a subform gets a bit more complex as, among other things, you will need to save the record in the main form before opening the subform. Otherwise the subform won't be able to update the record and will give you a locking message.
I'm currently using the MS Access 2010 template for contacts. I've modified it to do a multiple criteria search, but it's still the same.
What I'd like to know is how to modify the contact details window that opens when you click "open".
Basically, I want it to open the window, but then I want to add two buttons to go to the next person without having to close the window and reopen another one. I've added the buttons, but the window opened is for a specific ID, so it won't go to the next record.
Does anybody know how to open the contact details window for a specific contact but still be able to go through all of them? (them being the contacts given after the search)
EDIT:
Ok, i've just tried to create a clone, but all i get is a error "3251 - this operation is not supported for this type of object. I'm relatively new to VBA code, so its kind of hard to find the issues, but i'm guessing it has something to do with type of recordset (DAO or ADO). Here is what i have:
Private Sub txtOpen_Click()
DoCmd.OpenForm "Contact Details", , , , , acHidden
With Forms![Contact Details]
.Recordset = Me.Recordset.Clone
.RecordsetClone.FindFirst "[ID]=" & Me!ID
If Not .NoMatch Then
.Bookmark = .RecordsetClone.Bookmark
End If
End With
End Sub
I'm currently looking into it though!
I haven't checked the A2010 contacts template, but my guess is that you have a list of results and you select one and click a command button to open the details.
Likely that command button has code something like this:
DoCmd.OpenForm "frmContactDetails", , , "[ContactID]=" & Me!ContactID
...which opens the form and filters it to a single record.
To have a navigable set of records, you'll instead want the form to be filtered to the same set of records as is displayed in the list of search results, and then you'll want to navigate among those. To do that, you'd have to set the criteria to the same criteria that filtered the results list, and then navigate to the currently selected record. I don't know what the criteria are, so I'm just going to represent it with a variable, strCriteria but the requirement is that it be formatted like a SQL WHERE clause without the word "WHERE":
DoCmd.OpenForm "frmContactDetails", , , strCriteria
With Forms!frmContactDetails
.RecordsetClone.FindFirst "[ContactID]=" & Me!ContactID
If Not .NoMatch Then
.Bookmark = .RecordsetClone.Bookmark
End If
End WIth
This opens the form to the same set of records, then navigates to the one with the ContactID of the record that was selected in the results when you clicked the command button to see the details. Once that detail form is loaded with that set of records, you can navigate to the next or previous records as you like.
It may also be possible to open the detail form and assign the form's Recordset to be a clone of the results form's Recordset, but I've never tried that. In that case, it would be something like (untested code, just guessing at how this would be done):
DoCmd.OpenForm "frmContactDetails", , , , , acHidden
With Forms!frmContactDetails
.Recordset = Me.Recordset.Clone
.RecordsetClone.FindFirst "[ContactID]=" & Me!ContactID
If Not .NoMatch Then
.Bookmark = .RecordsetClone.Bookmark
End If
End WIth
Another thing to consider is not having a popup form at all, but use a split form, something that was introduced in A2007, and that I've not had the opportunity to use yet (no clients who've switched fully to A2007/2010). It has a list view and a detail view on the same form.
The old-fashioned way to implement the same thing was to have two subforms on an unbound form, the top form being the list of results, and the top form being a detail form that is tied to the current record of the top form. No code is required, you just set the LinkChild and LinkMaster properties of the detail form like this:
LinkMaster: ListForm.Form!ContactID
LinkChild: ContactID
This approach and the split form both avoid the popup form, which I consider somewhat user-hostile (it needs to be used sparingly).
Which approach you use depends on exactly how you app fits together.