VBA public variables - vba

I am having trouble with the public i as intger portion of my code.
I am using i to keep the value of my current row so i can use this range across
my program. In my for loop it increments i so it will step through a column and search for v
however when i try using "i" in another set of code "i" no longer has a value.
I am not sure how global/public variables work in VBA or what is cause this error.
the problem occurs int Sub "yes" , and sub "no"
at the code
Cells(i,lcol).value=" ok "
and
Cells(i,lcol).value = " updated "
1st set of code is as follows, which gets my value for "i"
Public V As Integer
Public i As Integer
Private Sub Enter_Click()
Dim EmptyRow As Long
'Audit will only search Master Sheet
Worksheets("Master").Activate
'Find empty row value so we can use that for limit of search
With Sheets("Master")
EmptyRow = .Range("A" & Rows.Count).End(xlUp).Row + 1
End With
'i needs to be set to minimum limit
'Begin loop of search
For i = 12 To EmptyRow + 1
If Cells(i, 1).Value = V Then 'AssetNum.Value Then
'Go to compare userform to display
Compare.AssetDisplay.Value = AssetNum.Value
Compare.LocationDisply.Value = Cells(i - 1, 2).Value
Compare.Show
End If
Next i
'If i gets to emptyrow num then go to non found asset userform
Unload Me
NonFoundAsset.Show
End Sub
Private Sub UserForm_Initialize()
'Read in value from asset num to be comapre in loop
AssetNum.Value = V
End Sub
the second set of code im trying to call "i" using the public variable and it has no value
Private Sub No_Click()
Dim ws As Worksheet
Dim lcol As Long
'Make Master Sheet Active
Worksheets("Master").Activate
Set ws = ThisWorkbook.Sheets("Master")
'Finds next empty column
With ws
lcol = .Cells(11, .Columns.Count).End(xlToLeft).Column - 1
End With
'If the displayed location is not the same as the actual location "No" will be
'selected and the Change User Form will be displayed
'The value under the current audit column will be displayed as updated
Cells(i, lcol).Value = " Updated "
Unload Me
AuditChange.Show
End Sub
Private Sub Yes_Click()
Dim ws As Worksheet
Dim lcol As Long
'Make Master Sheet Active
Worksheets("Master").Activate
Set ws = ThisWorkbook.Sheets("Master")
'Finds next empty column
With ws
lcol = .Cells(11, .Columns.Count).End(xlToLeft).Column - 1
End With
'If the location displayed is correct "Yes" will be selected and
'The value "ok" will be displayed in the current audit column
Cells(i, lcol).Value = " Ok "
Unload Me
'Returns to Assetlookup to look for a new asset
Assetlookup.Show
End Sub
I appreciate any help, Im new to VBA and don't understand why this is not working.

I believe a public variabe in a UserForm is only available if the UserForm is running (loaded). To have a truely global variable, declare it in a normal module.
Probably the variable isn't available and VB can't find it in its scope. If Tools, Options, Require variable declarations is turned OFF, VB will create a new variable with that name in the current scope. Hence it looks as if it has "lost" its value.
Tip: don't call global variables something like i, j, n etc. These are typically used as local variables for loops and counters. Use a naming convention that makes clear the variable is global. I always prefix such a variable with g for 'global', e.g.:
Public giLoopCounter As Integer;

It depends where you declare it. You have to refer to that location. So if i is in UserForm1 and you are trying to use it from another form, reference it as.
Cells(UserForm1.i,lcol).value=" ok "
If you put
Option explicit
at the top of the form you are trying to call it from it would tell you that i by itself is not defined in that scope of you code.
EDIT: For additional comments from OP. Asked if i can be public in a click event.
To my knowledge, you can't have public/global variables in an event.
You will have to use a variable local
'Public variables are declared outside (above) all subs and functions
'This will be accessible by all subs functions and events in in the forms or sheets module or wherever it is
Public i As Integer
'This will be accessible by all subs functions and events in in the CURRENT sheet or form. It is private but to the current item
Private i As Integer
Private Sub CommandButton1_Click()
Dim j As count
'Do whatever it is to get that value.
j = 5
'You can access i to use it in you click event code
msgbox i * j
'Or you can set it in the event
i = j
End Sub

Related

VBA - Highlight Cell With Checkbox

Some logic to my process:
In column K on my worksheet I have inserted check boxes from cell K3 - K53 (this could become longer in the future) using the developer tab.
I then associated the check box with the same cell it is placed in.
I formatted the cells in this column by going to 'Format Cells', clicking on 'Custom' then typing in ';;;'. This was to HIDE the 'True/False' text from view.
My next step is to change the cell colour based on the text.
Note:
I have searched through a few forums and combined some code samples from them all, so I will not be able to reference the sources exactly, but below is what I have so far:
Code:
Sub Change_Cell_Colour()
Dim xName As Integer
Dim xChk As CheckBox
Dim rng As Range
Dim lRow As Long
lRow = ActiveWorksheet.Cells(Rows.Count, "B").End(xlUp).Row
Set rng = ActiveWorksheet.Range("K2:K" & lRow)
For Each xChk In ActiveSheet.CheckBoxes
xName = Right(xChk.Name, Len(xChk.Name) - 10)
If (Range(xChk.LinkedCell) = "True") Then
rng.Interior.ColorIndex = 6
Else
rng.Interior.ColorIndex = xlNone
End If
Next
End Sub
I keep getting an error on the line where I try to get the last row.
Code:
lRow = ActiveWorksheet.Cells(Rows.Count, "B").End(xlUp).Row
Error:
Object Required
I am not even sure if the code I have will solve my issue, so any help solving the main issue highlighting a cell based on the check box being checked or not, will be greatly appreciated.
Here's a quick rewrite with LOTS of comments explaining:
Sub Change_Cell_Colour()
Dim xChk As CheckBox
'Be explicit about which worksheet. Leaving it to "Activeworksheet" is going to cause problems
' as we aren't always sure which sheet is active...
'Also in this case we don't need to know the last row. We will iterate checkbox objects, not
' populate rows.
'lRow = ActiveWorksheet.Cells(Rows.Count, "B").End(xlUp).Row
'Again... we don't need this. We just need to iterate all the checkboxes on the sheet
'Set rng = ActiveWorksheet.Range("K2:K" & lRow)
'This is good stuff right here, just change the ActiveSheet to something more explicit
' I've changed this to the tab named "Sheet1" for instance.
For Each xChk In Sheets("Sheet1").CheckBoxes
'Getting the name of the checkbox (but only the last 10 characters)
xName = Right(xChk.Name, Len(xChk.Name) - 10)
'We can check the linked cell's value, but we can also just check if the
' if the checkbox is checked... wouldn't that be easier?
'If (Range(xChk.LinkedCell) = "True") Then
If xChk.Value = 1 Then
'Now we can use the "LinkedCell", but it's a STRING not a RANGE, so we will have
' to treat it as the string name of a range to use it properly
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = 6
Else
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = xlNone
End If
Next
End Sub
Here's the barebones version just to get it working
Sub Change_Cell_Colour()
Dim xChk As CheckBox
'Loop through each checkbox in Sheet1. Set it to color 6 if true, otherwise no color
For Each xChk In Sheets("Sheet1").CheckBoxes
If xChk.Value = 1 Then
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = 6
Else
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = xlNone
End If
Next
End Sub
I'm totally assuming here, but I would imagine you want this macro to fire when a checkbox is clicked. There is a handy Application.Caller that holds the name of the object that caused a macro to be called. You can set the "Assign Macro.." of each checkbox to this new code and then you can figure out which checkbox called the subroutine/macro using application.caller and follow the same logic to toggle it's linked cell color:
Sub Change_Cell_Colour()
Dim xChk As CheckBox
'Who called this subroutine/macro?
Dim clickedCheckbox As String
clickedCheckbox = Application.Caller
'Lets check just this checkbox
Set xChk = Sheets("Sheet1").CheckBoxes(clickedCheckbox)
'toggle its color or colour if you are a neighbour
If xChk.Value = 1 Then
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = 6
Else
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = xlNone
End If
End Sub
highlighting a cell based on the check box being checked or not
Select the sheet and apply a CF formula rule of:
=A1=TRUE
ActiveWorksheet doesn't exist, and because you haven't specified Option Explicit at the top of your module, VBA happily considers it an on-the-spot Variant variable.
Except, a Variant created on-the-spot doesn't have a subtype, so it's Variant/Empty.
And ActiveWorksheet.Cells being syntactically a member call, VBA understands it as such - so ActiveWorksheet must therefore be an object - but it's a Variant/Empty, hence, object required: the call is illegal unless ActiveWorksheet is an actual Worksheet object reference.
Specify Option Explicit at the top of the module. Declare all variables.
Then change ActiveWorksheet for ActiveSheet.

Using a macro to sort data from instrumentation software

So I use an instrumentation software at work that outputs readings to a csv excel file. Because of the length of the test and the readings every 30 seconds, I accumulate a few thousand rows for every 24 hours of the test. When the test runs, it reads things that I don't need readings of so I have to go through the sheet manually and replace the "false" readings with zeros.
What I would like to do is use a command button to prompt the user to select a column to sort and apply a range of values so if it falls within that range, it returns the value to the cell and if it doesn't fall within the range, it returns a 0. I have done this with smaller sheets with extra columns (see attached file) but I'm not super familiar with Macros or VBA so I'm not sure where to get started.
Can anyone give me a hand?
This can be done as follows.
1) Go to Developer tab in the Excel ribbon. Click Insert, choose a Command Button (ActiveX Control) and place it onto the sheet where you want to have it.
2) Double-click the button, this will open up the editor. You should see:
Private Sub CommandButton1_Click()
End Sub
3) Add the line UserForm1.Show inside of this block of code. This should now look like:
Private Sub CommandButton1_Click()
UserForm1.Show
End Sub
4) Now we will create the userform. On the left side of the editor you will see a section called "Projects - VBA Project", in this list you should see your Workbook. Right click the name of your workbook and click Insert -> UserForm.
5) Use the toolbox to drag the correct components into the Userform. You are free to customize this form as you see fit to add or remove functionality based on your needs. I made mine look like this:
6) In the projects tab you will see your userform named UserForm1. Right click this userform and click "View Code". Paste the following code:
Private Sub CommandButton1_Click()
Unload UserForm1
End Sub
Private Sub UserForm_Initialize()
Dim lastColumn As Long
lastColumn = Worksheets("Sheet1").Cells(1,Columns.Count).End(xlToLeft).Column
For i = 1 To lastColumn
UserForm1.ComboBox1.AddItem (Worksheets("Sheet1").Cells(1, i))
Next
End Sub
Private Sub CommandButton2_Click()
Dim columnName As String
Dim columnIndex As Integer
Dim min As Double
Dim max As Double
If Not (ComboBox1.SelText = "" And TextBox1.Text = "" And TextBox2.Text = "") Then
columnName = ComboBox1.SelText
min = TextBox1.Text
max = TextBox2.Text
Dim lastColumn As Long
lastColumn = Worksheets("Sheet1").Cells(1, Columns.Count).End(xlToLeft).Column
For i = 1 To lastColumn
If Worksheets("Sheet1").Cells(1, i).Value = columnName Then
columnIndex = i
End If
Next
Dim cellValue As Double
Dim lastRow As Long
lastRow = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lastRow
cellValue = Worksheets("Sheet1").Cells(i, columnIndex).Value
' MsgBox CStr(cellValue) + "--" + CStr(cellValue > min) + "--" + CStr(cellValue < max)
If Not (cellValue >= min And cellValue <= max) Then
Worksheets("Sheet1").Cells(i, columnIndex).Value = 0
End If
Next
End If
Unload UserForm1
End Sub
7) Change the component names such as CommandButton, ComboBox1, etc. to match your UserForm.
What does the code do?
Initializing: As soon as the UserForm is called it passes through the method UserForm_Initialize(). This method looks through your WorkSheet and determines the name of all the columns. It then adds these to the ComboBox.
Command Button 1: "Cancel", exits the UserForm without doing anything.
Command Button 2: "Confirm", this method goes to your selected column, checks within the desired range. If a value falls outside that range then it will set it to 0.
I hope this help!!

Store range values in a VBA object

I wrote a procedure which multiplies values present in a specified range (A1:B2) by a value passed as a parameter (the value written in the cell D1).
Now, how can I do in order to run again the same macro with a different parameter that will operate on the original values? I'm pretty sure I need some global variable which will tell my procedure that the macro has been run at least one time (I found how to do it) but I don't know how/where to store the original values.
For example, I have
Range("A1").Value = 1
Range("A2").Value = 1
Range("B1").Value = 1
Range("B2").Value = 1
Range("D1").Value = 2
Click the macro button and get the values in the range Range("A1:B2") as the following
Range("A1").Value = 2
Range("A2").Value = 2
Range("B1").Value = 2
Range("B2").Value = 2
Now, after manually editing the Range("D1").Value from 2 to 3 and clicking again the macro button i want my procedure return the following values
Range("A1").Value = 3
Range("A2").Value = 3
Range("B1").Value = 3
Range("B2").Value = 3
The minimalistic procedure could be structure as this
dim rng as Range
dim MyRange as Range
set MyRange = Range("A1:B2")
For each rng in MyRange
rng.value = rng.value*Range("D1").Value
next rng
I hope this is a good example. Thanks
Francesco
If I understood well, what you want is a way to track everytime you called your sub with the button and use this data within this very sub.
One way would be to create a public variable in a module (I usually create a variable module in my projects) and update this data in the sub you call with your button.
'Your public variable inside a module (VariablesModule)
Public param() As Variant
'Your general code
Call procedure(VariablesModule.param)
'Inside your sub
Sub procedure(byVal paramVal as Variant)
'(your code)
ReDim param(0 To n) As Variant
Set param(1) = ABCDEFG
End sub
A second way would be to simply store this data in a range and use/update it within your sub.

populating data from userform checkboxes & optional buttons

I am creating a userform that I want to be able to populate values in a data tab as well as default to certain values.
I think I have text boxes and combo boxes down, but cannot find info on using multiple optional buttons to generate data to one cell depending on the selection.
from the example, my criteria would be "secondary insurance" how do I go about linking them all so that, lets say cell b1 is populated with the selected option?
I'm completely guessing but I think checkboxes are a little more simple, true if checked and false if unchecked.
What I have so far is just a code I came across to fill in a cell with the value of the designated text/combo box and was just going to repeat for each column I need to set a criteria for.
Private Sub CommandButton1_Click()
Dim LastRow As Long, ws As Worksheet
Set ws = Sheets("Sheet1")
LastRow = ws.Range("A" & Rows.Count).End(xlUp).Row + 1 'Finds the last blank row
ws.Range("A" & LastRow).Value = TextBox1.Text 'Adds the TextBox1 into Col A & Last Blank Row
Me.Hide
End Sub
combobox list
Private Sub UserForm_Initialize()
ComboBox1.Value = ("N/A")
ComboBox1.List = Split("N/A Yes No")
End Sub
Please let me know if I lack information and or how to attach my test worksheet, hopefully you can see the picture (I can't on my work server).
Thanks in advance for any and all education.
If the caption of the option button is the same as you want as cell text, then something like this may be what you want to store it:
Private Sub CommandButton1_Click()
Dim LastRow As Range
With Sheets("Sheet1")
Set LastRow = .Rows(.Cells(Rows.Count, 1).End(xlUp).Row + 1).Cells
If OptionButton1 Then
LastRow(2).Value2 = Me.OptionButton1.Caption
ElseIf Me.OptionButton2 Then
LastRow(2).Value2 = Me.OptionButton2.Caption
Else
LastRow(2).Value2 = Me.OptionButton3.Caption
End If
End With
End Sub
This will set the desired cell to the value of the caption of the option button you have selected.
To load the data back in the userform, you could use something like this:
Sub Load_in(Row_To_Load As Long)
Dim MyRow As Range
With Sheets("Sheet1")
Set MyRow = .Rows(Row_To_Load).Cells
If MyRow(2).Value2 = OptionButton1.Caption Then
OptionButton1.Value = True
ElseIf MyRow(2).Value2 = OptionButton2.Caption Then
OptionButton2.Value = True
Else
OptionButton3.Value = True
End If
End With
End Sub
For this, I assumed that the names hasn't been changed. Also if nothing is selected, the third option (N/A) will be used. The same goes for loading it back. If you do not want that, simply change the Else part to ElseIf so it looks like the first 2 options.

VBA causing typed information to go to an incorrect worksheet

I am creating a spreadsheet that creates a reference number on the first worksheet (called database, to be used similarly to a database) and generates a new worksheet. This then gives a reference number on the new worksheet so that they are linked together. This is done by pressing "New Idea" on a UserForm.
Once this is completed it should then go to the newly created worksheet and highlight cell C7. Once this is complete it should close the UserForm and allow the user to be able to type in cell C7 on the new worksheet with no further steps needed.
This works fine if I use F8 to step through the process however if I close the code window and run through the process as a user would it doesn't work as it should.
C7 is highlighted but once you have typed in it and press enter to go to the cell below, what you've typed disappears completely, and whatever you type on the newly generated worksheet is actually entered on another worksheet.
I have a seperate worksheet that contains a button to open the UserForm and all data that is entered on the newly generated worksheet goes to this sheet incorrectly.
My code is below, and is all within the UserForm's code. I have left the ComboBox code below but this isn't relevant to the generation of the new worksheets. All that does is list the created tabs so the user can select a worksheet from the UserForm and go directly there rather than having to scroll sideways.
I'm using Excel 2013. I'm by no means a VBA veteran so any help would be greatly appreciated!
Thanks!
Private Sub UserForm_Initialize()
Me.ComboBox1.List = Worksheets("Database").Range("A2:A10000").Value
End Sub
Private Sub CreateNewIdea_Click()
CopySheet
End Sub
Sub CopySheet()
Dim LastRow As Long
NewReference
LastRow = Sheets("Database").Range("A" & Rows.Count).End(xlUp).Row - 1
ReturnValue = LastRow
Sheets("Idea Template").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = LastRow
Range("C3").Value = LastRow
Me.ComboBox1.List = Worksheets("Database").Range("A2:A10000").Value
Range("C7").Select
Unload Home
End Sub
Sub NewReference()
Dim LastRow As Long
LastRow = Sheets("Database").Range("A" & Rows.Count).End(xlUp).Row
Sheets("Database").Cells(LastRow + 1, "A").Value = Sheets("Database").Cells(LastRow, "A").Value + 1
End Sub
Private Sub ComboBox1_Change()
Worksheets(ComboBox1.Text).Select
End Sub
I've taken the liberty to edit and rewrite the code you've written for greater flexibility.
Option Explicit 'Forces the variable to be declared, undeclared variables are not allowed
Dim DatabaseTable As ListObject 'Makes the variable usable for the entire module
Dim Lastrow As Long
Private Sub UserForm_Initialize()
Set DatabaseTable = ThisWorkbook.Worksheets("Database").ListObjects("References")
'I'm assuming you've formatted the data on the worksheet as a table and named the table "References"
Dim i As Long
Dim DatabaseRows As Long
DatabaseRows = DatabaseTable.ListRows.Count
With Me.ComboBox1
.Value = Empty
.Clear
For i = 1 To DatabaseRows
.AddItem DatabaseTable.DataBodyRange(i, 1)
Next i
End With
End Sub
Private Sub CreateNewIdea_Click()
Set DatabaseTable = ThisWorkbook.Worksheets("Database").ListObjects("References")
Call CopySheet
End Sub
Sub CopySheet() 'Are you calling Sub CopySheet() from other subs besides Private Sub CreateNewIdea_Click()?
Call NewReference
Dim ReturnValue As Long 'I'm declaring this variable because I'm using the option explicit and that doesn't allow undeclared variables
ReturnValue = Lastrow 'Unless ReturnValue is a public variable, it's not doing much here.
ThisWorkbook.Worksheets("Idea Template").Copy After:=ThisWorkbook.Worksheets(Worksheets.Count)
ThisWorkbook.Worksheets("Idea Template (2)").name = Lastrow
ThisWorkbook.Worksheets(CStr(Lastrow)).Cells(1, 3).Value = Lastrow 'Cstr(lastrow) is needed because we want the sheet with the name of the last row, not the nth sheet which is what happens with WorkSheets(Lastrow) as lastrow is a number
Call UserForm_Initialize 'Calls the procedure which fills ComboBox1, if the unload home refers to this form, then this line is redundant since the combobox is filled again when the form is initialized.
ThisWorkbook.Worksheets(CStr(Lastrow)).Cells(7, 3).Select
Unload Home 'If the name of this form is home, you can just 'Unload Me'
End Sub
Sub NewReference() 'Are you calling Sub NewReference from other subs besides Sub CopySheet()?
DatabaseTable.ListRows.Add AlwaysInsert:=False 'Adds a new row to the table on the worksheet "Database"
Lastrow = DatabaseTable.ListRows.Count
If Lastrow = 2 And IsEmpty(DatabaseTable.DataBodyRange(1, 1)) Then 'This if determines if a row was added while the first row does not contain a reference
DatabaseTable.DataBodyRange(Lastrow, 1).Value = 1 'First reference, can be anything you want really
DatabaseTable.ListRows(1).Delete 'First row is deleted, otherwise you'd have an empty first row
Lastrow = Lastrow - 1
Else
DatabaseTable.DataBodyRange(Lastrow, 1).Value = DatabaseTable.DataBodyRange(Lastrow - 1, 1) + 1
End If
End Sub
Private Sub ComboBox1_Change()
If Not Me.ComboBox1 = Empty Then
Worksheets(CStr(Me.ComboBox1)).Select
End If
End Sub
Revised answer
After looking at the document provided by #tomjo and trying to reporduce the problem I found that the problem was caused by the buttons on the sheets. The buttons used were the Form Controls rather than ActiveX Controls.
A macro was assigned to the Form Control The macro was defined in a module as you'd expect. The Macro only called to show the relevant form. It appeared as if the selected sheet, either by the menu on the form or after creating a new sheet from the form wasn't properly activated and the information entered in the sheet that showing was actually entered in the form which was last manually selected. Stepping through the code I found that the proper sheet and cell was active after the selection through the form through Debug.Print ThisWorkbook.ActiveSheet.Name, ThisWorkbook.ActiveSheet.ActiveCell.Address. I failed to discover why, while the correct sheet and cell were active, the information was entered in the last manually selected worksheet and cell.
To verify that the issue was not caused by the file somehow I tried to reproduce the problem with an entirely new file. Again the problem occurred that while the screen was showing the correct sheet and cell as selected (selected through a form, called by a sub in a module, called by a Form Control) the information was actually entered in the last manually selected sheet and cell.
----Edit----
Running the Showform macro (which calls the form to show) from the Macros button under the developer tab in the ribbon, instead of clicking the Form control button which has the ShowForm macro assigned to it, doesn't create the issue.
----End of Edit----
I then only removed the Form control, and the sub in the module which calls to show the form and placed an ActiveX Control (CommandButton) on the sheet and on the relevant Sheet module created:
Private Sub CommandButton1_Click()
Form.Show
End Sub
Without editing the code any further, there were no further issues regarding information being entered on the last manually selected sheet and cell instead of the sheet and cell the screen was showing as selected.
Edited file (The link will be active for a limited time)