MS Access 2010 Object Passing Value instead of Reference ... Sometimes - ms-access

I have a small bit of code for a much larger MS Access database that likes to behave erratically. I can't say for sure, but it seems to change when I compile or recompile the database. Here's the code:
Private Sub FindCtls()
Dim DatType as String
Dim thisCTL as Control
DatType = Right(ActiveControl.Name, Len(ActiveControl.Name)-4)
For Each thisCTL In Me.Controls
If InStr(1, thisCTL.Name, DatType, 2) > 0 Then
Select Case Left(ThisCtl.Name,4)
Case "Sel_"
Set TgtCtls(0) = thisCTL
' A buncha other Cases
End Select
End If
Next thisCTL
End Sub
The problem is, when I step through the code, for some reason thisCTL is being passed as the value of the control, not the control itself. I've looked everywhere for this answer, that brought me to using the "Set" command instead of just putting TgtCtls(0) = thisCTL. That worked until I decompiled the DB and recompiled it. Made one change (changed the name of the first variable), compiled it again, and it worked. As I got into work today, someone told me that it was no longer working. Any ideas would be greatly appreciated.
Edit: I forgot to mention that TgtCtls is a form-wide array that was Dim'd at the top as such:
Option Compare Database
Dim TgtCtls(2) as Control

Related

Access VBA - Turn off aggregation when opening form/sub-form

What I'm trying to do is, whenever a user opens a form (and the sub-form that opens by default), I want to search through all the columns (controls?) on the form, check to see if they are currently set to aggregate (sum, count etc.) with Access' built-in Totals row, and if so, set them to not aggregate.
The reason for this is there are several millions records that are stored, so when someone queries it down to 3-4 and turns on Sum, then closes it, when the next person opens it, it tries to sum millions of numbers and freezes up. The form displays the queried results from a table which is populated via SQL (I think, if that sentence makes sense). Here's what I have so far:
Private Sub Form_Load()
'this form_load is in the UserApxSub sub-form, for reference
Call De_Aggregate
End Sub
Private Sub De_Aggregate()
Dim frm As Form, con As Control
Set frm = Forms!UserAPX!UserApxSub.Form!
For Each con In frm.Controls
If con.ControlType = acTextBox Then
If con.Properties("AggregateType").Value <> -1 Then
'crashes on following line
con.Properties("AggregateType").Value = -1
End If
End If
Next con
End Sub
I have not so much experience in Access VBA (usually work in Excel VBA) so please forgive me if I'm entirely off the mark here. The command con.Properties("AggregateType").Value = -1 doesn't throw an error, but Access just straight-up crashes when reaching that line specifically.
I've tried a number of variations in the syntax with no success, and I've also tried looping through other elements of the file (tabledefs, querydefs, recordsets, etc.) as well to see if I'm trying to change the wrong value, but the controls on this subform are the only things in the entire .mdb file that results when I search for elements with the AggregateType property.
I switched out the line that errors with Debug.Print con.Name & " - " & con.Properties("AggregateType").Value and I can check, have nothing return anything other than -1, turn on aggregation in some column manually, and have it return the correct result (0 for sum for example), so I think I'm looking in the right place, just missing some key factor.
I've been working on this for a couple weeks with no success. Any way to fix what I have or point me toward the right direction would be greatly appreciated!
This is not necessarily the answer but I don't have enough reputation
to give a "comment"...
I tried your scenario and verified can change the property value as you are however I did not iterate through all controls and simply used an onDoubleClick event on a column to simulate.
I would suggest trying to fire your sub with Form_Open or Form_Current to see if the property is getting reset after your code has been called for some reason.
UPDATE:
You are referencing the "Subform" Object of your main Form:
Set frm = Forms!UserAPX!UserApxSub.Form!
Try referencing the actual UserApxSub FORM explicitly.
Something like Set frm = Forms!UserApxSub! (assuming UserApxSub is the name of the form)
then stick in the Form_Open of your main form:
Private Sub Form_Open(Cancel As Integer)
'// the following would set a single control only. You can add your loop through all controls
Me!{your control name}.Properties("AggregateType").Value = -1 '// set control in your main form
Form_UserApxSub!{your control name}.Properties("AggregateType").Value = -1 '// set control in your "sub" form
End Sub

File Dialog error access VBA

I am trying to have the file dialog box pop up so the user can select a file path to export a file in VBA but for some reason it throws this error on the following line of code.
Error: Method 'FileDialog' of object '_Application' failed
Code: longResult = Application.FileDialog(msoFileDialogFolderPicker).Show
All Code:
If choice = 6 Then
Dim intResult As Long
Dim strPath As String
'the dialog is displayed to the user
longResult = Application.FileDialog(msoFileDialogFolderPicker).Show
'checks if user has cancled the dialog
If intResult <> 0 Then
'dispaly message box
Call MsgBox(Application.FileDialog(msoFileDialogFolderPicker _
).SelectedItems(1), vbInformation, "Selected Folder")
End If
Else
End
End If
I am really unsure how to fix this issue. I checked my syntax and everything.
I know that this is a bit of an old question at this point, but since it doesn't actually have the answer, and I needed one today, I'm going to chime in with what I found just in case anyone else needs the answer too.
To fix this you need to add a reference to "Microsoft Office [yourversion] Object Library" in Visual Basic Editor >> Tools >> References...
The dialog in question should look like this:
Was trying to do the same thing myself and found this question. I realize that it is over a year old.
Try using the actual number (4) instead of msoFileDialogFolderPicker, that worked for me. I think something needs to be installed for the msoFileDialog constants to be initialized, when I tried printing any of the constants defined in the help file in the immediate window nothing was printed.
Also, why does your code have one variable longResult and one variable intResult?
Almost nothing is an integer in VB6 as integer is a VB4 16 bit type. Win32 Integers are called Long in VB6/VBA.
This was to make porting 16 bit code to 32 bit easy.
Check out http://msdn.microsoft.com/en-us/library/office/ff865217%28v=office.15%29.aspx for more information on proper syntax with FileDialogue.Show method. It appears you need a Set in front of your variable.
If you are after some cool UI, you can checkout my Github for sample database using .NET wrapper dll. Which allows you to simply call a function and to open filedialog with file-drag-and-drop function
Dim FilePaths As String
FilePaths = gDll.DLL.ShowDialogForFile("No multiple files allowed", False)
'Will return a JSONArray string.
'Multiple files can be opend by setting AllowMulti:=true
here what it looks like;

MSAccess 2010 + VBA: Value is not a default property of recordset Field object

I needed to copy some values from MS Access table into Excel using VBA code. I had done this many times and considered myself experienced. In my code I export the data using the following statements:
sh.range("A" & row).Value = rs("MyField")
where sh is Excel sheet, row is integer (long) and rs is recordset (either DAO or ADO, without any effect on the problem under consideration).
My code worked well on my computer with installed MS Office 2007. But when my client ran the code on his machine with MS Office 2010, it failed and kept failing very randomly. E.g. when debugging VBA in MS Access module step by step by pressing F8 it always worked. But when I pressed 'run' F5, it failed very soon.
After many trials and errors (I tried to open the recordset using different options and recordset types and caching the records etc.), I finally found that if I write
sh.range("A" & row).Value = rs("MyField").Value
everything works just fine. But according to the docs (e.g. http://msdn.microsoft.com/en-us/library/office/ff197799(v=office.15).aspx ) the Value property is the default property of the field object, which in turn is the default collection of the recordset object.
But it seems that I cannot rely on the defaultness, which I have been doing in most of my code. Actually I found a solution to my problem, but I still have no idea about the cause. Does anyone know why is the code doing this? What is the problem?
PS: I also found that if I expand the one-line statement into two lines (three with declaration):
dim v as Variant
v = rs("MyField")
sh.range("A" & row).Value = v
it also works...
Since rs("MyField") is a Field object, if you do ...
MsgBox TypeName(rs("MyField"))
... Access will tell you its type is Field.
So TypeName() is one example where the object itself is referenced directly instead of its default .Value property.
But something like Debug.Print always references .Value, so Debug.Print rs("MyField") is the same as Debug.Print rs("MyField").Value
If you know exactly when .Value will be referenced implicitly and when it will not, you can add it only when absolutely required and omit it the rest of the time.
However, some Access developers recommend always including .Value to avoid such confusion. If that seems like too much effort to you, at least consider including .Value when you do any assignment ...
something = rs("MyField").Value
... and be watchful for any other contexts where you don't get what you want without .Value

MS ACCESS---Refresh all via VBA

How do I refresh all via vba?
I want to refresh all open forms...
The reason #CodeSlave's answer probably didn't do what you needed, is that the VBA method you need is requery, not refresh. Refresh will display changes made to existing records, but only requery will display newly added records.
Here is a more concise version of the code (put this in a Module, so that you can call it from any form):
Public Sub RequeryOpenForms()
Dim f as Form
For Each f In Access.Forms
f.Requery
Next
End Sub
NOTE: Unfortunately, requery has the side-effect of losing the currently selected record. This can be particularly frustrating for a use if there are a long list of records, since they may have to scroll down a long way to find the record that they were previously looking at.
What about something like this?
Sub AllForms()
Dim obj As AccessObject
dim dbs As Object
Dim i As Integer
dim intFormCount as Integer
Set dbs = Application.CurrentProject
intFormCount = dbs.AllForms.Count - 1
For i = 0 To intFormCount
If dbs.AllForms(i).isloaded = True Then
dbs.AllForms(i).refresh
End If
Next
End Sub
In a comment above you say:
I'd like the new record just added to
the table to be available in that form
when I go back to it
You might want to examine the Activate event. But it bothers me to requery the form unless you know for a fact that records have been added. I expect that if I needed to do this (I've never done so, actually -- my users know about Shift-F9 where it's relevant, but most of them never need it), I'd use the OnActivate event and check the current count of records and only requery the form if the count doesn't match the current recordset.
But again, I consider it a fairly exotic thing to do. In general, it's probably an architectural error to have too many forms sitting around with an open recordset that you depart from and then come back to. They should probably be entirely closed instead of being left open when you're away from them. This reduces the number of table handles, as well as the number of locks held on the back end database, both of which can be problems if not properly managed.

DAO .Execute method fails in Access mde

First off, I'm using Access 2000 and DAO. I have code that executes a simple INSERT INTO query that I call using db.Execute. This code works fine inside an mdb. However, if I compile into an mde then I get
error 5 - Invalid procedure call or argument
on this line and the record does not get inserted. However, if I change from db.Execute to DoCmd.RunSQL using the exact same SQL statement the record is inserted with no problems. Does anyone know why the DAO Execute method of the DAO database object would suddenly stop working once I compile into an MDE?
Note: I only get the error if I specify the dbFailOnError option of the .Execute method. If I leave that option off, I get no error but the record is still not inserted.
EDIT:
This line fails in the MDE (but works fine in the MDB):
App.db.Execute InsertSQL, dbFailOnError
From the immediate window with a breakpoint on the above line of code:
?InsertSQL
INSERT INTO Changes
(PropertyID, FieldID, [Which], [When], [Before], Reason, ReportChange)
VALUES (1, 2, "M", #2/19/2010 4:51:44 PM#, "Suite 2; 430 W KING ST; ABBOTTSTOWN, PA 17301-9771", "Per Owner", True)
(I have an entire class module dedicated to building and executing SQL statements, so it's not really practical to show exactly how I built the InsertSQL string variable. However, I really don't think that is relevant.)
This line works everywhere:
DoCmd.RunSQL InsertSQL
EDIT: App.db definition (note that there is no reference in my project to ADO, only DAO):
Public App As New clsApp
clsApp class module (relevant lines only):
Private m_objDB As Database
Public Property Get db() As Database
Set db = m_objDB
End Property
Private Sub Class_Initialize()
Set m_objDB = CurrentDb
End Sub
Private Sub Class_Terminate()
Set m_objDB = Nothing
End Sub
If you are curious, I use App.db rather than CurrentDB for two main reasons: 1) slight performance gain by not having to call the CurrentDB function repeatedly (call it once then just refer to the object it returns) and 2) properties of the database object like .RecordsAffected always return relevant information. Plus, it's faster to type. And I'm a programmer, so I'm inherently lazy.
EDIT: Let me first apologize to those who have been following this thread and trying to help me. It seems I may have left out the critical details of my problem. The App.db.Execute call takes place inside a class module (clsSQL) and it references a global variable named App which is itself an instance of a different class module (clsApp). I'm guessing the problem is that I am referring to an instance of a class module from within another class module. Can anyone confirm if calling one class module from within another is something that is supported by MDBs and not MDEs? [It is not an issue. I was way over-thinking this. See my answer for the full story.]
Chances are you are using a forms control reference inside the string you are passing to the Execute statement. That is me!ControlName is inside the quotes. Change it so it's outside the quotes. That is change ".... SELECT me!ControlName As Field1, Field2, Field3 ..." to ".... SELECT " & me!ControlName & " As Field1, Field2, Field3 ..."
Thus VBA can convert the value of the control name properly. Docmd.RunSQL does this for you. Execute does not.
Firstly, my sincere apologies to those who tried to help me with this. As I find so frustrating when others ask questions, I left out the key piece of information because I did not think it was relevant. Here was the full initialize procedure for my clsApp class module:
Private Sub Class_Initialize()
Application.Echo True
m_bEcho = True
Set m_objDB = CurrentDb
m_sUser = GetUserName
Set m_objStatus = New clsStatus
m_sPgmName = Application.CurrentProject.Name
m_sPgmName = Left(m_sPgmName, InStr(m_sPgmName, ".mdb") - 1)
End Sub
The key line is that last one. When I compiled into an ".mde", the string ".mdb" was no longer present in the CurrentProject.Name. As a result, the call to InStr() returned 0. And 0 - 1 = -1. So I was passing a negative number as the Length parameter for the Left() function. However, the length parameter cannot be negative. It is most definitely an "Invalid procedure call or argument."
My simple fix was to remove the "mdb" from ".mdb" and just check for the location of the "." This also works correctly with the ".accdb" and ".accde" 2007 extensions.
Again, my apologies.
What version of DAO do you have in your references? It might be that the older version is working fine on the mdb but fails on the mde. Try setting this to the latest version and compile it again