Setting focus to a subform gives the error 2449 (there is an invalid method in an expression) - VBA - Access 2013 - forms

-----------UPDATE---------
I figured out how to get the focus on my subform. If it is a subform then :
ParentFormName = frm.Parent.Name
SubFormName = frm.Name
Forms(ParentFormName).Form(SubFormName).SetFocus
This seems to work (although Andre451's post is more effective). But this resulted in the underlying problem of my subform causing the suspended state. My subform is relatively big (194 records) and when doing a MoveFirst/Last on the recordset of larger records I get the error 3021 (no current record). This probably has something to do with the entire recordset of the subform isn't loaded on the load event of the main form. Smaller recordsets can be looped faster so won't generate the error (this is just an assumption though).
The new question is: How can I avoid the 3021 error and let the subform records load entirely so I can move through them with either:
DoCmd.RunCommand acCmdRecordsGoToLast
OR
frm.RecordsetClone.MoveLast
----------------------------------------------------------------------------------
A while a go we migrated our Access data to a SQL server with still using the Access as the frontend. We did this by linking the SQL Server tables with Access and this all works (almost) fine. Recently however we encountered a problem with Access queries getting in suspended state on the SQL server (mainly happens on queries in combo boxes).
After some research we figured out that this could be solved by looping through the query records on the load of the form (so by doing a simple acCmdRecordsGoToLast and acCmdRecordsGoToFirst) and this seems to work.
So to counter this suspended state we added to each forms load an init that runs through the records of the recordsource and rowsource (the main forms recordsource, the comboxes rowsource etc.).
So on a forms load:
modFunctions.InitForm(Me)
This init is in a module and looks like this:
Dim InitFormResult As Boolean
InitFormResult = InitFormRecordSource(frm) ' Initialize the recordsource of the form
InitFormComboBoxes frm ' Initialize all the comboboxes on the form
InitFormCommandButton frm ' Initialize all the buttons on the form
The first init of the forms RecordSource (where the error happens) looks like this:
Public Function InitFormRecordSource(frm As Form) As Boolean
If frm.RecordSource <> "" Then ' if recordsource found
On Error GoTo ErrHandler
frm.SetFocus
DoCmd.RunCommand acCmdRecordsGoToLast
DoCmd.RunCommand acCmdRecordsGoToFirst
GoTo EndSuccess
ErrHandler:
MsgBox (Err.Number)
Debug.Print "ERROR during InitFormRecordSource in form : " & frm.Name
InitFormRecordSource = False
Exit Function
EndSuccess:
InitFormRecordSource = True
Else
InitFormRecordSource = True
End If
End Function
To run through the forms recordset with First and Last it needs to have the focus. This all works great for one form but it doesn't for Subforms in a main form. In subforms we want to counter the suspended state as well by also running through the subforms records, though when using the above code we keep getting the error 2449.
After some research I encountered this post: Module Function frm.setfocus runtime 2449 error which is more or less the same problem. So I figured out that a subform needs to be given it's focus in a different way, yet all my efforts to do this won't work.
What I tried so far:
If frm.RecordSource <> "" Then ' if recordsource found
On Error GoTo ErrHandler
Dim HasParent As Boolean
Dim sParentForm
HasParent = TypeName(frm.Parent.Name) = "String"
If HasParent = True Then 'The form is a subform
'sParentForm = frm.Parent
'sParentForm.frm.SetFocus
'frm.Parent.frm.SetFocus
'sParentForm!frm.SetFocus
Else
frm.SetFocus
End If
DoCmd.RunCommand acCmdRecordsGoToLast
DoCmd.RunCommand acCmdRecordsGoToFirst
GoTo EndSuccess
The tried fixes are all commented out in the above code. The HasParent does work, with this I can check if the form is a subform (it has a parent) or not and setting the focus differently.
Though I can't seem to get the focus on the subform and I don't quite know how to get it fixed!

I have had a similar problem, queries on Sql Server 2008 R2 going into a "ASYNC_NETWORK_IO" wait state, until the last record was loaded.
My solution: don't use the form itself to go to the last record, but its .RecordsetClone:
frm.RecordsetClone.MoveLast

Related

MS Access VBA DoCmd.OpenForm using where clause not filtering DAO query based recordset

I,m trying to get rid of linked tables and use only VBA code to generate recordset. I found that filtering data using where clause in my DoCmd.OpenForm command doesn't work this way. Is that expected behavior? Or maybe it should work and the problem is located somewhere else... Are OpenArgs the only thing that left me to do this?
To clarify my question:
I have two ms access forms:
One (continuous form) with hyperlink and on click code behind like follows
Private Sub txtPerson_Click()
DoCmd.OpenForm "frmPersonnelDetails", , , "PersonId = " & Me.txtPersonID, acFormReadOnly, acDialog
End Sub
and second one (frmPersonnelDetails), not connected to any recordsource, with recordset created with:
Private Sub Form_Load()
strQuery = "SELECT PersonID, Abbreviation, FirstName, LastName FROM SomeTable"
Set objDaoDb = GetDAODbConn 'function that returns database connection object
Set objDaoRS = objDaoDb.OpenRecordset(strQuery, dbOpenDynaset, dbSeeChanges)
Set Me.Form.Recordset = objDaoRS
End Sub
Now, where clause doesn't work. Second form is opening always on the first record. Is it normal? What is the best way to make it open on specified record?

Why is Access form giving me the error message "A column number has been specified more than once in the order list"? The related queries open fine

I'm working on an Access database with a SQL Server back end. Some times when I open a particular form, I get an Error 3146 "Column has been specified more than once in the order by list."
There are several possible queries that might feed into this form but I checked and ran them all individually and they run fine, so it's something in the form but I'm not sure. Below is a screen shot of the Data Properties window. My hypothesis is that "Order By" property (which I did not populate manually) is not being cleared out when the form closes, and thus when the form is re-opened, that in effect puts in the same columns twice.
I can temporarily solve the problem by opening up the form and clearing those Filter and Order By fields. That solves it for a while but intermittently it will happen again so there's probably one other thing I'm overlooking. I put in code in the Form close event to set the orderby="" but even that doesn't totally solve it.
Here is the code that opens the form from the main menu (frmProdSched is the form in question)
Private Function OpenProdSched(RunQuery, RunCaption)
DoCmd.OpenForm "frmProdSched", , RunQuery, "", , acNormal
Forms!frmProdSched.Caption = RunCaption
gScheduleRptQry = RunQuery
Me.Refresh
End Function
and then the code that is in the Form_Current event
Private Sub Form_Current()
On Error Resume Next
Dim strForm As String
Dim strForm1 As String
Dim strWhere As String
strForm = "frmSubmittalStatus"
strForm1 = "frmProdSched"
strWhere = "StatusJobno = " & Me.JOBNO & ""
If IsLoaded(strForm) Then
If Forms(strForm).Filter = strWhere Then
Forms(strForm1).SetFocus
Else
DoCmd.Close acForm, strForm
DoCmd.OpenForm FormName:=strForm, WhereCondition:=strWhere
Forms(strForm1).SetFocus
End If
End If
End Sub
I tried adding this code to the Form Close event and it seemed to work most of the time but it still intermittently happens
Private Sub Form_Close()
Me.Filter = ""
Me.OrderBy = ""
End Sub
I'm not sure what to check next. Any ideas?

How to explicitly save or close a recordset when a form deactivates?

I apologize if this is too vague a question, but we're having the following problem:
We use a search form to find a record, then load it in a bound-control form where changes are made. Then we return to the search form and open another record. When we do that, the form's BeforeUpdate property fires a 3020 error, "Update without Add New or Edit" and stepping through the code it's referring to the FIRST opened record. This is strange because there is no explicit update call, but after much trial and error I think the error is thus:
Record #1 is opened via the form and changes are made. Record #2 is opened on that same form without closing the first recordset. Even though we now re-opened the form with the second record, Access still assumes we're editing record 1, i.e. that we're trying to edit 2 records concurrently. Same as though we had a datasheet form and we edited one row and then tried to edit a second row without saving the first. What I want to be able to do is have it automatically do an update on the first record when the form deactivates so loading a new record doesn't cause this conflict.
So the bottom line is this: **Is there a way, on say the form's Dirty or Deactivate event, that we can force the form's recordset to update and close ** before loading a second record?
I hope I made this clear enough, it's a complex problem, so any small guidance would help. Btw, you may ask, "Why are you running the same code to open the same form twice?" Good question! Hey it's an old and badly written app (the thing has GoSubs in it for Pete's sake) but I have no choice but to make the best of a bad situation.
EDIT: I was asked to post code, which is reasonable, but it's in several different places. So I have the data form, it has a "Search" button to go back to the search form for opening another record. The search button on the data entry form is:
Private Sub CommandSearch_Click()
On Error GoTo Err_CommandSearch_Click
DoCmd.OpenForm "Reference Form", acNormal 'This is the form that does the searching
Exit_CommandSearch_Click:
Exit Sub
Err_CommandSearch_Click:
MsgBox Err.Description
Resume Exit_CommandSearch_Click
End Sub
When a record is selected on that search form, then the new form is opened with this code. Now this is where it gets tricky. I'm not the original programmer, as I said I think it was written in Access 97 by someone after taking an hour to read "Access for Dummies" :). But it always looks like only one copy of the form is open, so maybe it's re-opening it?
Public Sub CommandLoadCase_Click()
Dim LoadUTUCaseNumber As String, lengthUTUCaseNumber As Integer
lengthUTUCaseNumber = InStr(Forms![Reference Form]![Reference Query SubForm]![UTU Case Number], "-") - 1
If (lengthUTUCaseNumber = 0) Then
LoadUTUCaseNumber = ""
Else
LoadUTUCaseNumber = Left$(Forms![Reference Form]![Reference Query SubForm]![UTU Case Number], lengthUTUCaseNumber)
End If
On Error Resume Next
Forms![Case Appeal_Add-On Form].UTUCaseKeyToFind = LoadUTUCaseNumber
DoCmd.OpenForm "Case Appeal_Add-On Form", acNormal, , , , , LoadUTUCaseNumber
'Case Appeal Add On Form referred to here is the data entry form I refer to above.
End Sub
Finally, the Error 3020 (Update without Add/Edit) is occurring after it executes this line on the data entry form. (I know the code is complicated which is why I didn't enter it at first).
Private Sub Form_BeforeUpdate(Cancel As Integer)
[UTU Claim Sequence Key] = [UTU Claim Alpha Sequence] & CStr([UTU Claim Numeric Sequence])
End Sub

MS Access - me.recordset not passing to sub

I have a form that loads a single record. The user does what they need to do on the form...in this case, they enter a date, and a button becomes available to click to advance the record to the next step in the process.
I have a public function that is logging the activity to tblActivity, and sets the record's new Status and Location. This Function takes 3 variables, and was working fine until today.
'I'm calling the function with this line from the button's Click event
LogActivity 15, Screen.ActiveForm, Me.Recordset
Public Function LogActivity(ByVal lSID As Long, Optional fForm As Form, Optional ByRef fRS As Recordset)
With fRS
Do Until .EOF
Debug.Print .Fields(5)
.MoveNext
Loop
End With
...
End Function
This should be printing the form's Status value, but fRS is passed in with no values. The form's recordset has values prior to being passed as the form has data. Some how it is getting lost in the pass. This was working fine, I have multiple buttons across 5 different forms that all call this same Function. Suddenly today, none of them can pass the recordset. I can think of nothing that was changed that would effect this. Most of the changes recently involved locking down fields and the appearance of buttons at the right time...nothing related to the recordset.
Naturally, this DB is supposed to go live on Monday.
Found the problem.
I had a backup from yesterday that was working fine.
One by one, I went through the changes I logged from yesterday and found that by changing some fields to .enabled = False and .locked = True is what was doing it. Apparently that was enough to clear all the values when passing.
Left the fields enabled, just locked them and it passes all values correctly.
Even though this was a failure on my part, I'll leave this up in case some one else makes the same mistake I made.
**** Update ****
I also found out that if I did a
fRS.movelast
fRS.movefirst
before anything else, it found the data. Not sure why it started happening, but these two things seem to have fixed it completely.

MS Access VBA: Closing Transaction without destroying Form's class module variables

I have an unbound form used to enter constituent data. I can't use subforms because data is spread across multiple tables and linked with foreign keys. Since I want to use the field validation rules to validate, I use a transaction to make sure new records are committed in an all-or-nothing fashion. I don't want names committed without addresses, etc.
The form is designed to iterate over a recordset, "tempImportRs", to give the user an opportunity to make sure the data looks OK and to manually correct or skip invalid entries. It works by populating the form's fields with values from tempImportRs for each entry. There's also a subform that searches for similar records and displays them to the user to decide if this is really a new person or if they already exist in the DB with somewhat different info.
I am running into a problem using this "tempImportRs" recordset, which persists for the lifetime of the form and is used by various methods in the form, alongside Access's transactions (which I don't understand very well, admittedly). If at any point I close the transaction like
WrkSp.Close
then my tempImportRs object will disappear and give me "Requires object" type errors. If I don't close the transaction, which is bad practice anyway, my form will work like I want for up to maybe a dozen records before giving me the error "Could not start transaction; too many transactions already nested."
How can I close the transaction cleanly, without destroying "tempImportRs"?
Here is a very simplified version of the VBA code for my form:
Public tempImportRs As RecordSet
Sub TryToAddRecords()
'Try to add the data from the now-populated form fields to my tables'
On Error GoTo Error_TryToAddRecords
Dim WrkSp As Workspace
Set WrkSp = DBEngine.Workspaces(0)
WrkSp.BeginTrans
'In my real code I use several recordsets to store the imported data'
'but I am simplifying here by using just one:'
Dim someOtherRs As Recordset
Set someOtherRs = CurrentDb.OpenRecordset("tblNamesAddressesEtc")
'...Do stuff using several other recordsets and '
'the form fields populated from tempImportRs'
someOtherRs.Update
WrkSp.CommitTrans
Exit_TryToAddRecords:
someOtherRs.Close
'I leave tempImportRs open for now.'
'WrkSp.Close <--This is what messes up tempImportRs'
Set someOtherRs = Nothing
Set WrkSp = Nothing
Exit Sub
Error_TryToAddRecords:
MsgBox Err.Description & vbNewLine & _
"Please fix the field and try again."
Resume Exit_TryToAddRecords
End Sub
Private Sub GoButton_click
Set tempImportRs = CurrentDb.OpenRecordset("tblTempImport")
If !tempImportRs.EOF
'<populate the form fields with the current '
'tempImportRs data here>'
TryToAddRecords
tempImportRs.MoveNext
Else
tempImportRs.close
Set tempImportRs = Nothing
MsgBox "All records imported"
End If
End Sub
This is not a problem specific to transactions and workspaces, but a more general matter of program structure. You should just make the workspace persist for the duration of the form, and commit to it multiple times. You don't need to make a new workspace for each transaction.
So, treat the workspace just like tempImportRs is treated in the code example. You can both set and close WrkSp in the GoButton_click sub. Don't set or close it in the TryToAddRecords function.
You will also have to make sure to roll back with WrkSp.Rollback if there's an error, add that line to Error_TryToAddRecords.
Public tempImportRs As RecordSet
Dim WrkSp As Workspace
'...
Private Sub GoButton_click
Set tempImportRs = CurrentDb.OpenRecordset("tblTempImport")
Set WrkSp = DBEngine.Workspaces(0) 'move this here
If !tempImportRs.EOF
'<populate the form fields with the current '
'tempImportRs data here>'
TryToAddRecords
tempImportRs.MoveNext
Else
tempImportRs.Close
Set tempImportRs = Nothing
'close the workspace here when you're done with tempImportRs
WrkSp.Close
Set WrkSp = Nothing
MsgBox "All records imported"
End If
End Sub
PS: Sorry folks for asking and answering this dumb question, I had a mental block and thought the issue was about managing workspaces and not a simpler matter of code design. Maybe this will still help someone someday.