I have Camp as string. When I write this code, I get an error:
*Me.BoatDesc =< the expression you entered refer to an object that is close*
Here is my code
private Sub Save_Click()
Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
Correct me if I am wrong.
You are using VBA, not VB.Net. Here are some notes
Here is a simple form, it will be open when the code is run. The code will be run by clicking Save. Note that the default for an MS Access bound form is to save, so you might like to use a different name.
This is the form in design view, note that there is a control named BoatDesc and another named Amount, as can only be seen from the property sheet.
The save button have an [Event Procedure], which is the code.
Note that the code belongs to Form2, the form I am working with, and the words Option Explicit appear at the top. This means I cannot have unnamed variables, so it is much harder to get the names wrong.
This is the code to be run by the save button.
Option Compare Database
Option Explicit
Private Sub Save_Click()
''Do not mix up strings, variables, controls and fields
''If you want to know if a control, BoatDesc, equals
''the string "camp", you do not need this
''Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
End Sub
Related
I'm developing a bunch of Excel Macros for making my life easier. One part of different macros is inserting a picture into sheets. For this reason, I would like to save the path to the images in a global location and then access it via a variable (so that I don't have to manually adjust the paths in every macro if it changes). I use one module per macro
In my own module "Variables" I defined a variable as Public or Global and then assigned a value via a sub. If I now access this variable via another module, I get an empty MsgBox.
For test purposes I use a string which I want to display via an MsgBox.
Modul 1:
Public test As String
Sub variablen()
test = "String for Test "
End Sub
Modul 2:
Public Sub testpublic()
MsgBox (test)
End Sub
I recommend to use a constant instead of a variable:
Module 1
Option Explicit
Public Const MyPath As String = "C:\Temp"
Module 2
Option Explicit
Public Sub ShowPath()
MsgBox MyPath
End Sub
I also recommend to activate Option Explicit: In the VBA editor go to Tools › Options › Require Variable Declaration.
If you do it like you did test is empty until it was initialized by running the procedure variablen first. If you use Public Const no initialization is required.
so that I don't have to manually adjust the paths in every macro if it changes
If it ever needs to change, then it semantically isn't a Const. The key to writing code that you don't constantly need to modify is to separate the code from the data.
A file path that sometimes needs to change can be seen as some kind of configuration setting.
Have a module that is able to read the settings from wherever they are, and return the value of a setting given some key.
The settings themselves can live on a (hidden?) worksheet, in a ListObject table with Key and Value columns, and looked up with INDEX+MATCH functions (using the early-bound WorksheetFunction functions will throw run-time errors given a non-existing key string):
Option Explicit
Public Function GetSettingValue(ByVal key As String) As Variant
With SettingsSheet.ListObjects(1)
GetSettingValue = Application.WorksheetFunction.Index( _
.ListColumns("Value").DataBodyRange, _
Application.WorksheetFunction.Match(key, .ListColumns("Key").DataBodyRange, 0))
End With
End Function
The Variant will retain the subtype of the Value, so for a String value you get a Variant/String; for a Date value you get a Variant/Date, for a numeric value you get a Variant/Double, and for a TRUE/FALSE value you get a Variant/Boolean.
Now when the file path needs to change, your code does not:
Dim path As String
path = GetSettingValue("ImageFolderPath")
And if you need more settings, you have no code to add, either:
Dim otherThing As String
otherThing = GetSettingValue("OtherThing")
All you need to do is to make sure the string keys being used match the contents of the Key column in your SettingsSheet.
I developed a Form in Excel (2016) and I am trying (with VBA) to configure its behavior so that if the user selects a particular option button, two additional things happen:
A checkbox further down on the form is automatically checked.
A text box further down on the form automatically displays a set string of text.
More specifically, if the user selects OptionButtonABC, then ...
CheckBoxNone should become checked
TextBoxCompanyName (which does not display any text by default) should now display the string: 'ABC'.
I initially created a subroutine that just targeted condition 1, and everything worked fine. However, when I try to integrate the code required to handle condition 2, things start to unravel.
Below, you will see the code in its most current state. I have a Private Sub that initiates upon the Click event and then immediately defines a variable as a string. I then set up an if/then statement that specifies what should happen IF OptionButtonABC is true. Namely, CheckBoxNone should be selected (this works fine) AND TextBoxCompanyName should now display the string 'ABC'.
Private Sub OptionButtonABC_Click()
Dim Variable_ABC As String
Variable_ABC = ABC
If Me.OptionButtonABC.Value = True Then
Me.CheckBoxNone = True And Me.TextBoxCompanyName.Text = Variable_ABC
End If
End Sub
The desired behavior should (theoretically) be pretty easy to achieve, but my experience with VBA is still pretty limited, so I am reaching out to the community for a bit of advice. As I mentioned above, the code above works for the first condition. However, I am still unable to get the string of text ('ABC') to show up in the text box.
Thanks in advance for any advice you may offer.
Private Sub OptionButtonABC_Click()
Dim Variable_ABC As String
Variable_ABC = "ABC" 'String Values uses double quotes
If Me.OptionButtonABC.Value = True Then
Me.CheckBoxNone = True
Me.TextBoxCompanyName.Text = Variable_ABC
End If
End Sub
The operator AND must be used only in the IF statement comparison, not in what you want to do.
I'm writing some code in MS Access and I reached to the point where user needs to choose on which worksheet of an Excel workbook there need to be performed some operation. I don't know, what name of this worksheet is or on which position it is placed.
I was thinking about a solution which will show user a form (as modal form) with listbox containing all sheets names'. When user click one of them form will show aside A1:J10 range (so user can choose the right one worksheet). After confirming choosen worksheet it will return as worksheet object.
Every thing was great untill I wanted to pass a object variable to the form. In openArgs I can only pass a string. I was even thinking about a class which will open this form but it's still no luck with passing object parameter.
I'm trying to avoid global/public variables.
Any ideas?
Assuming your object is wsObj, can't you just use wsObj.Name ?
Also have a look at wsObj.CodeName, which may be interesting as well.
There are many possibilities to send some value between objects.
A) Using Global vars into ACCESS Vba module
Global yourvariable As String
if you need some different value can use Variant, Single, etc.
B) Using Windows Register
To save value:
SaveSetting "yourprojectname", "Settings", "yourvariable", yourvalue
To retrieve value:
retvalue = GetSetting("yourprojectname", "Settings", "yourvariable", "your_default_value_if_not_exist")
C) Using OpenArg into Open Form command procedure
DoCmd.OpenForm "stDocName", acNormal, "filter_if_needed", "stlinkcriteria", acFormEdit, acWindowNormal, "arguments_forOpenArgs"
On destination form
Private Sub Form_Open(cancel as integer)
your_args=Me.OpenArgs
On all three possible solutions you can send more than one value using a chain with vars and values, an example:
myvar_mutiple="name=John Doe|address=Rua del Percebe 34|location=Madrid|phone=34 91 1234567"
On this example i used "pipe" (AltGr on key 1) char to separate each var=value
Then on destination procedure only need split each pair:
splitvar=Split(myvar_multiple,"|")
With this you obtain for each "splitvar" an element like "name=John Doe"
Do again an split with "=" to obtain variable an value. For each value you can reassign the result to a local vars.
Full code example:
if me.OpenArgs<>"" then
splitvar=Split(me.OpenArgs,"|")
for x=0 to ubound(splitvar)
tmpsplit=Split(splitvar(x),"=")
paramvars=tmpsplit(0)
paramvalue=tmpsplit(1)
select case paramvars
case "name"
stname=paramvalue
case "address"
straddress=paramvalue
case "location"
strlocation=paramvalue
case "phone"
strphone=paramvalue
end select
next
end if
Some recommendations that i use for this code "multiple vars":
- always use Low Case variable or change this:
paramvars=tmpsplit(0)
by
paramvars=lcase(tmpsplit(0))
-if you need to use "=" into value you can change by other alternative char or search the first "=" form left (i used this solution instead Split)
paramvars=trim(lcase(left(splitvar(x),len(splitvar(x))-(len(splitvar(x))-instr(splitvar(x),"="))-1)))
remember that you can send any value and can be converted on destination code. On this sample i use only String so you can use cLng or cInt etc.
Over your solution to select Sheet on excel from Access i think there are better alternatives.
IN the forms Module you can declare a property as object and then set that property once loaded. So in the form module
Option Explicit
Private myObj as object
Property Set DesiredWorksheet(o as object)
set myobj = o
End
and then in your code
Load myform
set myform.desiredworksheet = wsObj
myform.show
Ahh, sorry I was writing Excel not Access!!!
Docmd.openform f
f.desiredworksheet = ws.obj
docmd.openform f, windowmode:=acdialog
ought to work
I came across this similar issue and read the replies: Modeless form that still pauses code execution
I have been attempting to apply in my own situation the suggestion provided by David Zemens. In my situation, I cannot seem to find an approach that incorporates Mr. Zemen's suggestion without also utilizing a GoTo.
I am wondering if there is a better or more elegant solution.
Here is an outline of what I am doing:
I have a UserForm with a Command Button that begins the code execution that will perform several actions on multiple Excel workbooks. As such, there are a number of blocks of code and the successful completion of one block of code allows for the execution of the subsequent block of code.
At a certain point, depending on the situation, the code might require User input; in other situations, the needed data is obtainable from an Excel. If input is needed from the User, another UserForm is displayed.
The User may need to view several different Excel sheets before entering the input, so the UserForm is modeless. So the code comes to a stop until the User enters the needed input and clicks another Command Button.
It is at this point I am having trouble: how to resume the program flow. Is the only way to 'pick-up where it left-off' is by using a GoTo statement? Or is there some way to organize the modules so there is a single consistent program flow, defined in one spot and not duplicated from the point at which User input might be needed?
Here is my take on the problem . Hope I understood the problem correctly.
Assumptions:
There are two user forms.
UserForm1 with a button to start the processing.
UserForm2 with a button to supply intermediate input.
A sub inside a module to start/ launch UserForm1.
VBA Code (for the sub routine)
Sub LaunchUserForm1()
Dim frm As New UserForm1
'/ Launch the main userform.
frm.Show vbModeless
End Sub
VBA Code (for UserForm1)
Private Sub cmdStart_Click()
Dim i As Long
Dim linc As Long
Dim bCancel As Boolean
Dim frm As UserForm2
'/ Prints 1 to 5 plus the value returned from UserForm2.
For i = 1 To 5
If i = 2 Then
Set frm = New UserForm2
'/ Launch supplementary form.
frm.Show vbModeless
'<< This is just a PoC. If you have large number of inputs, better way will be
' to create another prop such as Waiting(Boolean Type) and then manipulate it as and when User
' supplies valid input. Then validate the same in While loop>>
'/ Wait till we get the value from UserForm2.
'/ Or the User Cancels the Form with out any input.
Do While linc < 1 And (linc < 1 And bCancel = False)
linc = frm.Prop1
bCancel = frm.Cancel
DoEvents
Loop
Set frm = Nothing
End If
Debug.Print i + linc
Next
MsgBox "User Form1's ops finished."
End Sub
VBA Code (for UserForm2)
Dim m_Cancel As Boolean
Dim m_prop1 As Long
Public Property Let Prop1(lVal As Long)
m_prop1 = lVal
End Property
Public Property Get Prop1() As Long
Prop1 = m_prop1
End Property
Public Property Let Cancel(bVal As Boolean)
m_Cancel = bVal
End Property
Public Property Get Cancel() As Boolean
Cancel = m_Cancel
End Property
Private Sub cmdlinc_Click()
'/Set the Property Value to 10
Me.Prop1 = 10
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
'/ Diasble X button
Me.Cancel = True
Me.Hide
Cancel = True
End Sub
OK so here are my thoughts.
You have a userform frmSelectUpdateSheet which you wish to use in order to allow the user to select the sheet, when the sheet can't be determined programmatically. The problem is that if you do .Show vbModeless (which allows the user to navigate the worksheet/s), then code continues to execute which either leads to errors or otherwise undesired output.
I think it's possible to adapt the method I described in the previous answer. However, that's out of the question here unless you're paying me to reverse engineer all of your code :P
Assuming you have a Worksheet object variable (or a string representing the sheet name, etc.) which needs to be assigned at this point (and that this variable is Public in scope), just use the CommandButton on the form to assign this based on the selected item in the frmSelectUpdateSheet list box.
This is probably a superior approach for a number of reasons (not the least of which is trying to avoid application redesign for this sort of fringe case), such as:
This keeps your form vbModal, and does prevent the user from inadvertently tampering with the worksheet during the process, etc.
Using this approach, the thread remains with the vbModal displayed frmSelectUpdateSheet, and you rely on the form's event procedures for control of process flow/code execution.
It should be easier (and hence, cheaper) to implement; whether you're doing it yourself or outsourcing it.
It should be easier (and hence, cheaper) to maintain.
NOW, on closer inspection, it looks like you're already doing this sort of approach with the cmdbtnSelect_Click event handler, which leads me to believe there's a related/follow-up problem:
The sheet names (in listbox) are not sufficient for user to identify the correct worksheet. So if the user needs the ability to "scroll" the sheet (e.g., to review data which does not fit in the window, etc.), then add some spinner buttons or other form controls to allow them to navigate the sheet.
I want to add a button to my excel worksheet which should call a macro that can handle one agument (an integer value). Sadly when creating the button, I cannot link any macro that has arguments. Also just typing the macro and the argument does not work.
Is there any simple solution to pass an argument to a macro when a button is pressed?
Yes, you can assign a macro to a button (or other excel controls/menu actions) and pass constant OR variable arguments to it.
In the 'Assign Macro' window (right-click on object and select 'Assign Macro'):
Enclose the macro name in single quotes
e.g. to pass 2 constants: 'Button1_Click("A string!", 7)'
Select 'This Workbook' for the 'Macros in' field
If you wish to pass a variable (like the value of a cell), enclose the parameter in Evaluate()
For example, to pass the value of Sheet1!$A$1 to a button function, you would have the following text in the 'Macro name:' field:
Button1_Click(Evaluate("Sheet1!$A$1"))
If you don't enclose your variable argument with an 'Evaluate' function, excel returns the error 'Formula is too complex to be assigned to an object.'.
I would have included an image if this were allowed on my first post.
Suppose you have a public sub take 1 argument like below (just for explanation purposes.)
And you insert a button on Worksheet like below, and you can not find the macro name when you want to assign your sub to this button.
Now, you can type in your sub name + space + argument manually in single quotes, like below, click ok.
Then you see, problem solved.
Sub ert()
Call ert2(Cells(1,1).Value)
End Sub
Use an activeX control command button and in the button click method, call the sub and pass the argument:
Private Sub CommandButton_Click()
Dim x as Integer
x = 1
Call SomeSub(x)
End Sub
To call this Sub from a button :
Public Sub TestButton(strMessage As String)
MsgBox strMessage
End Sub
... be aware that the Sub won't be listed in the available macros, because it has a parameter. Just type in the call in single quotes : 'TestButton "Hello"'
Called from a regular "forms" button on a worksheet you can do something like this:
Sub TestMe()
Dim c, arr
c = Application.Caller
arr = Split(c, "_")
If UBound(arr) > 0 Then MsgBox "Got " & arr(1)
End Sub
Where the calling button is named (eg) "Button_3"
Or (simpler) right-click the button and enter 'TestMe2 5' (Including the single-quotes)
Sub TestMe2(i)
MsgBox "Got " & i
End Sub
See also: Excel 2010 - Error: Cannot run the macro SelectCell using .onAction
I had trouble with my version of Personal.xlsb!'testForXXXX("Test Test")'. I got an error when clicking the button containing the Macro.
However, I was able to fix it by removing the "(" and ")". So, Personal.xlsb!'testForXXXX "Test Test"' worked (notice the space between testForXXXX and "Test...").
In fact, I didn't need Personal.xlsb! and was just able to use 'testForXXXX "Test Test"'.
I hit the same issue with the assign button not being very useful until I realised that all the potential macros displayed were only the ones in my Personal.xlsb file that took no arguments. Then I typed Personal.xlsb!'macroNameWhichTakesArguments("arg1", ...)' and the sheet picked it up.
I.E. Personal.xlsb!'testForXXXX("Test Test")'
Where the macro testForXXXX take a string as input, then the sheet worked ok.
When you use the assign macro route for a button in excel 2007 at least testForXXXX will not show up in the list of potential macros if it takes args.
I suspect Aaron C got tripped up by this too. Maybe its best not to use a Personal.xlsb file?
I would have liked to comment the answer by QA Collective but I do not have enough reputation point yet. Previous solutions did not work for me, as my macros are in named modules in my VBA project.
With Excel 2013, the way to pass a parameter to a macro using a button that worked in my context is :
'<module name>.<macro name> <parameter>'
e.g
'DataProcessor.disle "myText"'
or
'DataProcessor.editWSProcess True'
(boolean do not need double quotes when passed as parameters)
Please note that single quotes are around the whole expression.