I have a Microsoft access form that allows users to navigate through records via two buttons "Back" and "Next". The problem is that if a user is on the first record and clicks "Back" a really tacky error message appears. How can I prevent this from happening?
So pretty much, If the record selected is =1 and Back button is pressed then do nothing.
Try this:
Previous record:
If CurrentRecord <> 1 Then DoCmd.GoToRecord , , acPrevious
Next record:
If CurrentRecord <> Recordset.RecordCount Then DoCmd.GoToRecord , , acNext
Consider adjusting your error handler accordingly:
Private Sub Back_Click()
On Error GoTo ErrHandle
'...
ExitHandle:
' UNINITIALIZE SET OBJECTS
Exit Sub
ErrHandle:
If Err.Number = 2105 Then
DoCmd.GoToRecord , , acFirst
Else
MsgBox Err.Number & Err.Description, vbCritical, "RUNTIME ERROR"
End if
Resume ExitHandle
End Sub
Related
I have a form that if a field is blank, it tells the user that it can't be blank, and gives the user the option to fill in the blank field (Yes) or close the form (No). Problem is that when the user picks No (or close the form), it gives me a run-time error 2585. After researching, I have it checking if the field is blank on the field after using On Got Focus. (After Update on the field didn't work because there's no update if it's blank.) Here's my code:
If IsNull(Me.txtSSN) Then
strMsg = "Social Security Number Must Not Be Left Blank!" & vbCrLf
strMsg = strMsg & "Do you want to add new veteran's record?" & vbCrLf
If MsgBox(strMsg, vbQuestion + vbYesNo, "Go to Record?") = vbYes Then
Me.Undo
Me.Refresh
Me.txtSSN.SetFocus
Exit Sub
Else
Me.Undo
Me.Refresh
Me.txtSSN.SetFocus
'DoCmd.OpenForm "fmuMainMenu"
DoCmd.Close acForm, "frmVetNewMainForm"
End If
Else
'Rest of Code...
I have a humongous Access form with hundreds of controls, all with some events.
One of them is throwing an error crying about "Validation or After Update event". No control name, nothing more to go by.
So I figured I'd list all the forms with all their validation rules and all their events (not all of them are "[Event procedure]", some are custom function calls).
Is there any way to get a list of a control's events?
My code so far:
Sub ListAllControlsAndTheirEvents(FormToCheck As Form)
Dim ctrlCurrent As Control
For Each ctrlCurrent In FormToCheck.Controls
Debug.Print "Name: " & ctrlCurrent.Name
On Error Resume Next
Debug.Print "Validation: " & ctrlCurrent.ValidationRule 'Not all controls have a ValidationRule property.
On Error GoTo 0
For Each eventCurrent In ctrlCurrent.events 'this thing doesn't exist :(
Debug.Print "Event: " & eventCurrent.Name & "; value: " & eventCurrent.whatever
Next
Next
End Sub
So, is there a way to implement this part?
For Each eventCurrent In ctrlCurrent.events 'this thing doesn't exist :(
Debug.Print "Event: " & eventCurrent.Name & "; value: " & eventCurrent.whatever
Next
Digging through the code module won't give me the custom function event calls, just the [Event Procedure] codes.
You could use typename to determine the type of control, then set so something like if typename(x) ="CommanButton" then set cb=x, then loop the properties, for property names beginning with On, then if there is an [Event Procedure] you know there's an event, also you can then narrow down your search to x_Click
The code below should identify and list all events (whether blank, [Event Procedure], or a custom function) for each control on the form. I tested it on Access 2010 by creating a blank form and adding several controls. I made sure to assign some standard events and custom functions to a few controls in order to validate.
Public Sub ListAllControlsAndTheirEvents(FormToCheck As Access.Form)
Dim ctrlProp As Object
Dim ctrlCurrent As Access.Control
For Each ctrlCurrent In FormToCheck.Controls
Debug.Print "Name: " & ctrlCurrent.Name
If PropertyExists(ctrlCurrent, ctrlProp, "ValidationRule") Then
Debug.Print "Validation: " & ctrlProp.Value
End If
For Each ctrlProp In ctrlCurrent.Properties
' Category 4 type 8 identifies an event property
If ctrlProp.Category = 4 And ctrlProp.Type = 8 Then
Debug.Print _
"Event: " & ctrlProp.Name & "; " & _
"Value: " & ctrlProp.Value
End If
Next
Next
End Sub
Private Function PropertyExists( _
ByRef ctrlCurrent As Access.Control, _
ByRef ctrlProp As Object, _
ByRef PropName As String _
) As Boolean
On Error Resume Next
Set ctrlProp = ctrlCurrent.Properties(PropName)
PropertyExists = Err.Number = 0
End Function
I know that this question has been marked as answered like 4 years ago.
I have been to this question in my search for the same thing in Excel.
This question was the source behind my search for an answer to a similar question.
I have found an answer to this question using TLI.
The answer I provided here is meant to help other people reach for a solution quickly and painlessly.
There is NO intention of gaining merits/points/reputation but just to help others get an answer I've only reached after spending so much time and energy.
I am no expert in Access VBA nor am I in Excel VBA but I think TypeLib Info can be used in Access too.
Requirement: Reference to TypeLib Information library at C:\Windows\SysWow64\TLBINF32.DLL
Sub printControlEventNames(ByVal o As Object)
Dim t As TLI.TLIApplication
Set t = New TLI.TLIApplication
Dim ti As TLI.TypeInfo
Set ti = t.ClassInfoFromObject(o)
Dim mi As TLI.MemberInfo
For Each mi In ti.DefaultEventInterface.Members
Debug.Print mi.Name
Next
End Sub
My question and my own solution can be found here.
Is there a generic VBA code for initializing all fields on an MS Access form? I am very weak in the VBA coding department but i would like to have the form initialized in the following 2 conditions:
1) When someone double clicks the form to open it.
2) After someone clicks the "save" button to save the newly created record.
Any help would be greatly appreciated.
edit
i have tried using the me.refresh code in the "on click" event and it does not seem to initialize all fields. The only code in the form is the following:
Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim ctl As Control
On Error GoTo Err_BeforeUpdate
If Me.Dirty Then
If MsgBox("Do you want to save?", vbYesNo + vbQuestion, _
"Save Record") = vbNo Then
Me.Undo
End If
End If
Exit_BeforeUpdate:
Exit Sub
Err_BeforeUpdate:
MsgBox Err.Number & " " & Err.Description
Resume Exit_BeforeUpdate
End Sub
The only differences between #'s 1 and 2 are the event handlers.
1
Private Sub Form_Load()
DoCmd.GoToRecord , , acNewRec
End Sub
2
Private Sub SomeButton_Click()
'Your code to save the record the user entered
DoCmd.GoToRecord , , acNewRec
End Sub
Both of these Subs should go to the next (new) record.
On Me.Refresh :
Me.Refresh only updates the records in the current form, but will not show new or deleted records.
On a TabHost view I found there are 3 events. Click, LongClick and TabChanged. I found that only TabChanged works and I would like to use Click since the user may tap a tab and go back to the home screen and may want to tap the same tab again.
Here is the Sub Routine I used with TabChanged, but I would like to use Click instead. Maybe I need to change something in my code other than just changing the _TabChanged to _Click. If so, could you let me know what to change?
Sub tbhPagesEventHandler_TabChanged
ToastMessageShow(tbhPages.CurrentTab,False)
' These will make the code easier to read.
'-----------------------------------------
Dim intVisitsTab As Int : intVisitsTab = 0
Dim intMaintenanceTab As Int : intMaintenanceTab = 1
' Start the activity the user wants.
'-----------------------------------
Select tbhPages.CurrentTab
Case intVisitsTab
StartActivity("Visits")
Case intMaintenanceTab
StartActivity("Maintenance")
End Select
End Sub
I see you found a solution, based on your comment, but thought I'd post this for future readers in case it was useful.
The 'TabHost.Click' event fires when the content of the TabHost tab is clicked, not the tab itself.
If you use the following for your code, you can see the difference (this uses tbPages as the TabHost variable):
' Displays the 0-based index of the tab being activated
Sub tbPages_TabChanged
Msgbox("Current tab is " & tbPages.CurrentTab, "")
End Sub
' Fires when you click inside the content of the tab page,
' not on the tab itself.
Sub tbPages_Click
Msgbox("Current tab is " & tbPages.CurrentTab, "")
End Sub
This means you can use the CurrentTab property to determine which page the user has selected, and react accordingly:
Sub tbPages_TabChanged
Dim TabIdx as Int
TabIdx = tbPages.CurrentTab ' Get the tab just activated
Select TabIdx
Case 0
' First tab is now active
Case 1
' Second tab active
Case 2
' Third tab active
Case Else
MsgBox("Something is badly wrong! We have only three tabs", "HEY")
End Select
End Sub
I'm developing a data retrieval application in Access 2010 in which the user chooses which table, columns, and rows to view by selecting listbox entries. The VBA code generates a SQL statement from these choices and then creates an ADBDB.Recordset object from this.
How can I display the recordset records in Access? None of the grid controls work in Access 2010 and the subform just isn't designed for this purpose. Can someone recommend another strategy?
You could save the SELECT statement as a named query, then open the query as a datasheet. It's not really a form, but somewhat form-like.
Call DatasheetFromSql(strSql)
Public Sub DatasheetFromSql(ByVal pSql As String)
Const cstrQuery As String = "qryDiscardMe"
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strMsg As String
On Error GoTo ErrorHandler
Set db = CurrentDb
db.QueryDefs.Delete cstrQuery
Set qdf = db.CreateQueryDef(cstrQuery, pSql)
DoCmd.OpenQuery cstrQuery, , acReadOnly
ExitHere:
On Error GoTo 0
Set qdf = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
Select Case Err.Number
Case 3265 ' Item not found in this collection. '
Resume Next
Case Else
strMsg = "Error " & Err.Number & " (" & Err.description _
& ") in procedure DatasheetFromSql"
MsgBox strMsg
GoTo ExitHere
End Select
End Sub
I opened the query read-only. If you want to allow users to edit the data returned by their custom queries, I would not recommend this approach. Instead I would invest the effort in the approach HK1 offered because it can support better control of the user data changes.
With the query opened as a datasheet, you can use Screen.ActiveDatasheet to inspect its properties. At least some of the methods are also available to you. For example you could resize/reposition the datasheet like this:
Screen.ActiveDatasheet.Move Left:=0, Top:=0, Width:=(4 * 1440), Height:=(3 * 1440)
The units are twips (1440 twips / inch), so that would make the width 4 in., height 3 in., and move it to the upper left corner of the Access window.
Here's what I think you'll have to do to get this kind of functionality.
You'll first need to create enough of the correct controls on a form to handle every possible scenario. You'll then need to set the form to be a datasheet form so that it appears as a grid.
Now set the controlsource on your controls to correspond with one of the fields in the recordset. On every control that is not in use needs to have it's ColumnHidden property set to true. You'll also have to change the caption of the associated label to show the appropriate column name for each control that will be visible.
Now, bind that form to your ADO recordset object.
Me.Recordset = rst
'or
Me.Subform1.Form.Recordset = rst
Is this a perfect solution? Most certainly not. Access doesn't have anything that compares to the DataGridView in .Net or even to the Grid controls that are used in VB6. In my opinion, you're really pushing the limits of Access to try to get this sort of functionality. It's like swimming upstream. You'll find that everything you do is going to be fairly difficult and some things just aren't going to be possible.
for an ADP Project where you can't have local MS Access query definitions, you can create a datasheet form with multiple textboxs named txt1, txt2,.... txt30 and labels name lbl1 ... lb30 and this code will set the form.recordsource and set the textbox.controlsource and the label.caption to the appropriate fields from an ADO recordset object. This form will allow you to view your ADO Recordset similar to the Docmd.OpenQuery method.
You must pass the ADO recordset's SQL statement to the form using the OpenArgs property of the form. the code below shows the VBA code to call\open the form (which shows the ADO Recordset like a query) and pass your sql string. The vba code on the form's load event will set all the control properties, resize the columns that have data and hide the columns that do not have a corresponding field from the ado recordset:
'stevekirchner 09/29/2012 Replace Access parameterized query with SQL Server in-line function
'DoCmd.OpenQuery "qry_SearchMaster_CaseTitles", , acReadOnly
strsql = "Select * from dbo.UDF__qry_SearchMaster_CaseTitles ('%" & Me.tbxSearchTerm.Value & "%') "
Call Display_ADO_Recordset_from_Datasheet_Form(strsql, "frm_Display_ADO_Recordset_Result1")
'create a non-form module and put the code for the sub Display_ADO_Recordset_from_Datasheet_Form
'and function fIsLoaded in it (this will allow you make several forms to view ADO recordset and
'call the code from one place\module):
Sub Display_ADO_Recordset_from_Datasheet_Form(sSQL As String, sFormName As String)
On Error GoTo Error_Handler
If fIsLoaded(sFormName) Then
DoCmd.Close acForm, sFormName
End If
DoCmd.OpenForm sFormName, acFormDS, , , acFormReadOnly, , OpenArgs:=sSQL
Exit_Sub:
Exit Sub
Error_Handler:
MsgBox Err.Description & " Error No: " & CStr(Err.Number)
Resume Exit_Sub
End Sub
Function fIsLoaded(ByVal strFormname As String) As Boolean
On Error GoTo Error_Handler
'Returns False if form is not open or True if Open
If SysCmd(acSysCmdGetObjectState, acForm, strFormname) <> 0 Then
If Forms(strFormname).CurrentView <> 0 Then
fIsLoaded = True
End If
End If
Exit_Function:
Exit Function
Error_Handler:
MsgBox Err.Description & " Error No: " & CStr(Err.Number)
fIsLoaded = False
Resume Exit_Function
End Function
'Create a datasheet view form (named frm_Display_ADO_Recordset_Result1) with 30 textboxes and 30
'30 labels named txt1 - txt30 and lbl1 - lbl30 and put this code in the form's module:
Option Compare Database
Private Sub Form_Load()
On Error GoTo Error_Handler
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim rsClone As ADODB.Recordset
Dim strsql As String
Set conn = CurrentProject.Connection
Set rs = New ADODB.Recordset
strsql = Me.OpenArgs
rs.Open strsql, conn, adOpenStatic, adLockOptimistic
Set rsClone = rs.Clone
Call Update_Form_Controls("your text goes here", strsql, rsClone)
Exit_Sub:
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
Exit Sub
Error_Handler:
MsgBox Err.Description & "; Error Number : " & Err.Number, vbOKOnly
Resume Exit_Sub
End Sub
Sub Update_Form_Controls(Header_Label As String, SQL As String, CloneRS As Recordset)
Dim rsCount As Integer
Dim i As Integer
On Error GoTo Error_Handler
Me.Form.Caption = Replace(SQL, "Select * From ", "Display: ")
rsCount = CloneRS.RecordCount
If rsCount <= 0 Then
MsgBox "The Query did not return any data to view", vbOKOnly
DoCmd.Close
Else
Me.Form.SetFocus
Me.RecordSource = SQL
i = 1
Do Until i = 31
Me("lbl" & i).Caption = ""
Me("txt" & i).ControlSource = ""
Me("txt" & i).ColumnHidden = True
i = i + 1
Loop
i = 1
With CloneRS
For Each Field In .Fields
On Error Resume Next
Me("lbl" & i).Caption = .Fields(i - 1).Name
Me("txt" & i).ControlSource = .Fields(i - 1).Name
Me("lbl" & i).Visible = True
Me("txt" & i).ColumnHidden = False
Me("txt" & i).SizeToFit
i = i + 1
'Debug.Print Field.Name
On Error GoTo 0
Next Field
End With
End If
Exit_Sub:
Me.Requery
Exit Sub
Error_Handler:
MsgBox Err.Description & "; Error Number : " & Err.Number, vbOKOnly
Resume Exit_Sub
End Sub
I don't use ADO but simply VBA, in which case above approach is wrong: one doesn't need any means for displaying an existing recordset in a form, but should on the contrary define an adequate recordset within the form !
Instead of creating MyDataBase.OpenRecordset ("SELECT … [SQL query] ;"), just set the form's RecordSource to the very same query definition:
Forms![MyDisplayForm].RecordSource = "SELECT … [SQL query] ;"
Forms![MyDisplayForm].Requery
For me this works perfectly (Windows 7 pro ×64 / MS Office pro ×64)