MS Access implenting hyperlink like behavior for records to switch between forms - forms

I'm currently working on a Database which requires the following functionality:
For example given a specific project, I have a series of structures which belong to that project, which are displayed in a datasheet view on the project form. I am attempting to allow the user to on double click to navigate to that specific structure which is displayed on another form. Currently I am using filters to implement this behavior, however, this results in the filter being left on, and when I manually turn off the filter, the form I switch to returns back to the first entry.
I am using the current code on the datasheet:
Private Sub struct_name_DblClick(Cancel As Integer)
LookupValue = Me.struct_ID
Form_frm_control.pg_structure.SetFocus
Form_frm_control.subform_structure.Form.Filter = "struct_ID = " & LookupValue
Form_frm_control.subform_structure.Form.FilterOn = True
End Sub
Any help would be greatly appreciated. Thanks in advance.

It all depends on what you need to do.
If you want to display all records and navigate to the selected record, then you can use bookmark navigation:
With Forms!MyOtherForm
.RecordsetClone.FindFirst "struct_ID = " & Me!struct_ID
If Not .RecordsetClone.NoMatch Then
If .Dirty Then
.Dirty = False
End If
.Bookmark = .RecordsetClone.Bookmark
End If
End With
This assumes that the other form is open with all the records loaded.
Another approach to this problem, which I find more useful for popup situations like this, is to just open the other form as a dialog and require it be closed before returning to the calling context. In that case, you'd do this:
DoCmd.OpenForm "MyOtherForm", , , "struct_ID = " & Me!struct_ID, , acDialog
You'd then have to close the other form to get back to the original context.
You might think that with large numbers of records this would be inefficient, but it's actually very fast, as this operation is highly optimized within Access (moreso than filtering an already open form, in fact).

Related

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 linking each checkbox(true/false column) to run a individual code in forms

New Edition June 2, 2017
I'm creating a database for a preventative maintenance program for the equipment in my facility. We have about 70 different machines with individuals preventative maintenance instructions, and I've been able to create tables for machines with the instructions attached to them by using forms that will update the database. I created a main form that will filter which PM needs to be done based on the date that is searched that will filter through the machines in the database, access does this fine. The problem I'm currently having trouble with is finding a way to add checkboxes to this main form, and print off only the ones that are selected with a button. The plan we have is to have the mechanics to be able to print off the PM instructions, and use the print off to update the database. Is there a way this can be done with checkboxes or optionboxes?Here's a picture of the main form that doesn't have the checkbox field attached to it yetHere's a picture of the forms that will show the preventative maintenance instructions that we will be printed.
Your question is a little vague and doesn't provide any insight on what you might have tried (this would actually help us determine what you want to do). That said, based on what's given:
If you have a single checkbox and want some action to be performed when it is checked/unchecked.
Private Sub CheckboxName_Click()
If CheckboxName.Value = -1 Then
'Do something
Else
'Do something else (Else is optional)
End If
End Sub
If you have multiple checkboxes you want to confirm which are checked and which aren't when a button or something is pushed you use the Controls object to cycle through all the checkboxes.
Private Sub ButtonName_Click()
Dim ctrl as Control
Dim i as Integer
For Each ctrl In Me.Controls
If ctrl.ControlType = acCheckBox and ctrl.Value = -1
'Do something (e.g., Select Case ctrl.name)
End If
End Sub
Hopefully that at least points you in the right direction.

Error in Opening Report from Form

I have a Form which will help me to filter out the records I want for my Report. The button will open the Report On Click.
This is the code in the button:
Private Sub Open_OEE_Click()
DoCmd.OpenReport "OEE_Report", acViewReport, , , acWindowNormal
End Sub
I keep getting the error:
I also have placed the query in my report under the Record Source as:
SELECT * FROM 3_OEE WHERE ((([3_OEE].RecordID)=Forms![3_OEE_Report]!cboRecordID) And (([3_OEE].Date_Recorded)=DateValue(Forms![3_OEE_Report]!Date_Recorded)) And (([3_OEE].MC_No)=Forms![3_OEE_Report]!cboMCNo) And (([3_OEE].Product)=Forms![3_OEE_Report]!cboProduct));
I want to search based on one criteria (text box or combo box) and not all four at once.
Am I missing out something?
MS-Access does tend to go a bit overboard with the brackets. Make the report's Record Source a bit easier to read by trimming out the unnecessary ones. You also need to get your date criterion in the right format - Access always uses US formatting in SQL queries and needs # signs around the date:
SELECT * FROM 3_OEE
WHERE [3_OEE].RecordID = Forms![3_OEE_Report]!cboRecordID
And [3_OEE].Date_Recorded = Format(Forms![3_OEE_Report]!Date_Recorded, "\#mm/dd/yyyy\#")
And [3_OEE].MC_No = Forms![3_OEE_Report]!cboMCNo
And [3_OEE].Product = Forms![3_OEE_Report]!cboProduct;
I would also suggest creating a named query for this and setting the report's Record Source to the named query. You can then test the query in isolation without having to run the report (but make sure the Form is open and the relevant controls are populated).
I asked for help from another source.
Answer to Question

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.

Passing arguments to Access Forms created with 'New'

I have a form called 'detail' which shows a detailed view of a selected record. The record is selected from a different form called 'search'. Because I want to be able to open multiple instances of 'detail', each showing details of a different record, I used the following code:
Public detailCollection As New Collection
Function openDetail(patID As Integer, pName As String)
'Purpose: Open an independent instance of form
Dim frm As Form
Debug.Print "ID: " & patID
'Open a new instance, show it, and set a caption.
Set frm = New Form_detail
frm.Visible = True
frm.Caption = pName
detailCollection.Add Item:=frm, Key:=CStr(frm.Hwnd)
Set frm = Nothing
End Function
PatID is the Primary Key of the record I wish to show in this new instance of 'detail.' The debug print line prints out the correct PatID, so i have it available. How do I pass it to this new instance of the form?
I tried to set the OpenArgs of the new form, but I get an error stating that OpenArgs is read only. After researching, OpenArgs can only be set by DoCmd (which won't work, because then I don't get independent instances of the form). I can find no documentation on the allowable parameters when creating a Form object. Apparently, Microsoft doesn't consider a Constructor to be a Method, at least according to the docs. How should I handle this? (plz don't tell me to set it to an invisible text box or something) Thanks guys, you guys are the best on the net at answering these questions for me. I love you all!
Source Code for the multi-instance form taken from: http://allenbrowne.com/ser-35.html
Inside your Form_detail, create a custom property.
Private mItemId As Long
Property Let ItemID(value as Long)
mItemId = value
' some code to re query Me
End Property
Property Get ItemId() As Long
ItemId = mItemId
End Property
Then, in the code that creates the form, you can do this.
Set frm = New Form_detail
frm.ItemId = patId
frm.Visible = True
frm.Caption = pName
This will allow you to pass an ID to the new form instance, and ensure it gets requeried before making it visible. No need to load all of the results every time if you're always opening the form by Newing it. You let the property load the data instead of the traditional Form_Load event.
This works because Access Form modules are nothing more than glorified classes. Hope this helps.
You could try applying a filter:
frm.Filter = "[ID] = " & patID
frm.FilterOn = True
The Record Source of the Detail form will need to be set to the table to which the ID belongs.
UPDATE
As you requested, here is the code to set the RecordSource:
frm.RecordSource = "select * from TableName where [ID] = " & patID
This is probably cleaner than using a filter given that a user can remove the filter (depending on the type of form).