Passing Variables between Subs .. get a ByRef argument error

SpreedsheetCrusader

Board Regular
Joined
Sep 30, 2015
Messages
130
Hi

i am trying to save some ink on a VBA code i am writing a code which is about toggling cells colors on/off by pressing a set of buttons on the sheet (e.g. button 1 toggles cells with yellow color on/off, button 2 toggles cells with green color on/off, and so forth..)

i have this piece of code at the beginning of each Subroutine dedicated to each button to set the range:

Code:
Dim lastrowlastrow = Cells(3, 6).SpecialCells(xlCellTypeLastCell).Row


Dim myrange As Range
Set myrange = Range(Cells(3, 6), Cells(lastrow, 12))

this aint going to change between subs it will remain the same so i tried declaring those two variables in a Sub and calling that Sub inside my buttons dedicated Subs see below:

Code:
Sub Vardef (Lastrow as long,myrange as range)

lastrow = Cells(3, 6).SpecialCells(xlCellTypeLastCell).Row
Set myrange = Range(Cells(3, 6), Cells(lastrow, 12))

end sub

sub ToggleYellow()

Call Vardef (Lastrow,myrange)

.
.
.
.

rest of the code

when i do the above i start getting an Error "Argument ByRef" and the code does not work, i highlight on myrange i find it = nothing

do not know what is the problem it is driving me mad, especially that i used the same technique with other projects and it worked smoothly, however this is the first time i pass a variable that is a range.


looking for your help my spreadsheets brothers and sisters.
 

Excel Facts

Who is Mr Spreadsheet?
Author John Walkenbach was Mr Spreadsheet until his retirement in June 2019.
appreciate any help or opinion on this,

i am trying to make my code less lengthy by passing common variables instead of re-writing them every time in each sub.

browsed some old threads could not grasp how to pass a SET RANGE = argument to multiple subs, CALL doesnt work for me as i have mentioned.

my code currently works fine by repeating the LASTROW and Set MYRANGE variable at every Subroutine i am using but that is counter-intuitive for maintenance especially if i wanted to change the address definition later, that means i would need to change that in every subroutine that i have created.
 
Upvote 0
You need to declare local variables to receive the values assigned by Vardef:
Code:
sub ToggleYellow()
Dim Lastrow As Long myrange As Range

Call Vardef (Lastrow,myrange)
 
Upvote 0
Hi SpreedsheetCrusader

First a very quick point – I think you have a typo in post #1 Code: This line is rubbish

Code:
Dim lastrowlastrow = Cells(3, 6).SpecialCells(xlCellTypeLastCell).Row

_ ...............

_ I am not sure exactly what you are trying to do, or maybe you are trying to do something along the lines of what I was here:
Store Array created by VBA macro “Internally” for use in same VBA macro by next run..or..- [SOLVED]
_ Maybe like me you are trying to understand some basic ideas...
_ But Possibly you are confusing two different things ( or I am ).

_ 1) What I think you want to do:
_ 1a ) Consider this.
Copy the following to any Normal or sheet Module in File

Code:
[color=blue]Option[/color] [color=blue]Explicit[/color]
[color=blue]Dim[/color] rngA1 [color=blue]As[/color] Range [color=darkgreen]'Dim or Private I think works the same to make the variable available in this module[/color]
[color=blue]Private[/color] lr [color=blue]As[/color] [color=blue]Long[/color], cnt [color=blue]As[/color] [color=blue]Long[/color]
[color=darkgreen]'[/color]
[color=blue]Sub[/color] SubAnyOneOfmanySubroutines()
    [color=blue]If[/color] lr = 0 [color=blue]Then[/color] [color=blue]Call[/color] SetLetStuff
[color=blue]Let[/color] cnt = cnt + 1 [color=darkgreen]'A count for how many times you ran the code[/color]
Let rngA1.Value = "Run Number " & cnt & ". Last cell in column A of Third Sheet with something in it is " & lr & "" [color=darkgreen]'you can use this code line in any [color=blue]Sub[/color], without Defining rngA1 as long as SetLetStuff() was run once[/color]
[color=blue]End[/color] [color=blue]Sub[/color]
[color=darkgreen]'[/color]
'
'
[color=blue]Sub[/color] Set[color=blue]Let[/color]Stuff()
[color=blue]Set[/color] rngA1 = ThisWorkbook.Worksheets.Item(3).Range("A1") [color=darkgreen]'Set RngA1 to first cell in Third Sheet[/color]
Let lr = ThisWorkbook.Worksheets.Item(1).Cells(Rows.Count, 1).End(xlUp).Row [color=darkgreen]'Let lr = last cell in column A with somethiong in it[/color]
[color=blue]End[/color] Sub



Run the Sub SubAnyOneOfmanySubroutines() a few times in debug ( F8 ) mode and see what happens.
You will see it only calls Sub SetLetStuff() the first time.
Once Sub SetLetStuff() has run once you can use lr and rngA1 in any other Procedures
You will also see that if you look at the first cell in the Third Tab (Third sheet) and run the code, then the value in the first cell is updated.
That is basically I think all there is to using variables in different Procedures without repeating the Dim , Let , and Set stuff. So in your case for what I think you want , you would just change the lr or rngA1 in the Sub SetLetStuff(), save the file , close it, reopen , and start again and there you have the change you want without changing all your procedures using lr and rngA1

_ 1b) You could do it a bit better, I think.
Probably having a code in ThisWorkbook Module of the Private Sub Workbook_Open() type which did the Dim and Let Set stuff. But i tried and could not figure out how!

_ ..................

_ 2) ByVal ByRef Stuff ( What i think you do not want to do particularly )
This is Passing Variables from one Procedure to another and is something else (. It does not sound like what you are interested in here.) It concerns how Variables from a calling Procedure are “taken” into the Procedure it calls.
_ No Dimensioning is done outside any Procedure . After the calling procedure stops all variables die. - So I do not think that is what you want....But very briefly, in words,
_ ByVal which is the default, means use for each Sub Procedure taking that variable at the "Signature Line"** a copy of that variable is made, with the value that is in it, in it . In each Procedure it is a different variable which lives and dies for the lifetime of the Procedure. It will not change the value of the variable with the same name given in the original Procedure. The Copy Variable “dies” when the called Procedur ends.
_ ByRef “points” or “references to a specific Variable” or carries across that actually variable as it were at the "Signature Line"**. Although I have never seen it so explained, somehow when the called procedure stops, The variable does not die as in the ByVal case, but somehow the actual variable ( with the original, or, if modified, the modified value, in it ) “drops back” into the calling Procedure ####

Maybe these typical Example codes will help to explain this a bit. Copy all three to any Module and run the first two Codes



Code:
[color=blue]Option[/color] [color=blue]Explicit[/color]
[color=blue]Sub[/color] CallByRefByValCheck()
[color=blue]Dim[/color] varByRef [color=blue]As[/color] Long: [color=blue]Let[/color] varByRef = 1
[color=blue]Dim[/color] varByVal [color=blue]As[/color] Long: [color=blue]Let[/color] varByVal = 2
MsgBox Prompt:="In the calling Sub ( CallByRefByValCheck() ) " & vbLf & "value in varByRef is " & varByRef & vbLf & "value in varByVal is " & varByVal & vbLf & ". I am now taking value 2 and variable varByRef " & vbLf & "across into another Sub " & vbCr & ""
[color=blue]Call[/color] ByRefByValCheck(varByRef, varByVal)
MsgBox Prompt:="I came back, and now " & vbLf & "value in varByRef is " & varByRef & vbLf & "value in varByVal is still " & varByVal
[color=blue]End[/color] [color=blue]Sub[/color]
[color=blue]Sub[/color] WhatIsvarByValandvarByRefNow()
MsgBox Prompt:="varByRef is " & varByRef & vbLf & "VarByVal is " & varByVal
[color=blue]End[/color] [color=blue]Sub[/color]
[color=darkgreen]'[/color]
'
'
Sub ByRefByValCheck(ByRef Rf [color=blue]As[/color] [color=blue]Long[/color], ByVal Vl [color=blue]As[/color] [color=blue]Long[/color]) 'This is the [B][COLOR="#800080"]"Signature Line"**[/COLOR][/B] Rf holds varByRef , Vl holds a copy of varByVal
[color=blue]Let[/color] Rf = 10 [color=darkgreen]'The variable "in" Rf ( which is the actual varByRef from calling [color=blue]Sub[/color] ) is changed[/color]
[color=blue]Let[/color] Vl = 20 [color=darkgreen]'The variable "in" Vl ( which is just a copy of varByVal, not the original ) is changed[/color]
[color=blue]End[/color] Sub

_ ...............................................

I am learning myself and there may be more to it. In particular I would be very interested if a Profi out there could do the bit I tried in _1b). After a few hours of Googling and Experimenting i could not see how to do that.

Hope that helps a bit...
Alan

#### P.s. Just in the unlikely case that you did follow my last explanation.... - A very important learning point here is, I think, that using a procedure in such a ByRef way gives you a way to sort of use it like a Function , but better, as you can return as many Variables as you wish , that is to say as many as you have defined ByRef in the "Signature Line"**. ( A Function I believe only returns in typical use one value ( unless it returns Variant Type with an Array() in it) VBA incidentally does often not like “taking across ” collections ( Arrays ) so there may be an interesting connection here that you find an Array() will only be taken ByRef
Store Array created by VBA macro “Internally” for use in same VBA macro by next run..or..- [SOLVED] - Page 3
.......
)
 
Upvote 0
You need to declare local variables to receive the values assigned by Vardef:

hi brother.

thanks this works ! now i have one place where the range i created is stored (Vardef sub) and incase i ever needed to change that range i only have to change it in one place only not across all the subs. the only con though is that i have to "dim" LASTROW and MYRANGE across all the subs and CALL VARDEF to activate them.

Brother DocAElstein :

First a very quick point – I think you have a typo in post #1 Code: This line is rubbish

LOL true i wrote it very quick did not copy paste it from my Module forget to write down here correctly.

regarding the rest of your post it is highly valuable i bookmarked it as i need some time to digest it (+ i need some Vodka for this
:stickouttounge:)

as you have mentioned, it is true; i am still amateur and learning my way through VBA and optimizing my code. Storing and passing Arrays is highly important for me to learn your post will definitely help me a lot

thanks for investing your time and helping me out, it means a lot to me:beerchug:
 
Upvote 0
Hi SpreedsheetCrusader

Glad it may or may have helped. ( Hope I don’t encourage you to drink too much )

I am just a part time amateur myself and learnt, as I often do, by trying to answer the Thread!

I am still not quite sure what you are doing. Maybe if i have another quick go at explaining I might help one of us learn a bit more!

It appears at first glance to me that you are doing something along the lines of what I tried to explain in _2). – So , like you said “"dim" LASTROW and MyRange across all the Procedures and Call Vardef() to activate them.“ ... - Again I may have missed the point , but I do not think you need to do that to achieve what you want. Doing what I tried to explain in _1) you just “"dim" LASTROW and MyRange once at the top of the module, and a Sub Procedures equivalent to your Vardef() is only ever called once.
_ .
So, I will do another demo, just limit it to MyRange. What I try to demonstrate with the following codes is that you only “Dim” Myrange at one place ( at the top of the Module ) and you only ever Vardef() once. The only extra line you need in all your subs is at the start
If Not TypeName(MyRange) = "Range" Then Call Vardef()
This line checks if MyRange exists. ( Note Dim MyRange As Range does not make it exist. To exist it must be Set to a Range, which is what Vardef() does )
(If you happen to know that any subs will never be run first, than you can leave out the line
If Not TypeName(MyRange) = "Range" Then Call Vardef() )
in that Sub. – So you only Include that line in any Procedures that may be run first ( After opening the File ) )

Again copy all the following codes to a Module and run, preferably in F8 debug mode, and you see that they all work to using a code line of the form
MyRange.DoSomething
To give you this......................
Using Excel 2007
[Table="width:, class:grid"][tr][td]-[/td][td]
A
[/td][/tr]
[tr][td]
1
[/td][td]Sub_0 did this[/td][/tr]

[tr][td]
2
[/td][td]Sub_1 did this[/td][/tr]

[tr][td]
3
[/td][td]Sub_2 did this[/td][/tr]

[tr][td]
4
[/td][td]SubNeverDoneBeforeSubs_0_1or2 did this[/td][/tr]
[/table]
[Table="width:, class:grid"][tr][td]SpdSht[/td][/tr][/table]
_ ............despite there being no “dim” MyRange in them, and Sub Vardef() is only ever called once. (Just make sure you never run the apply named Procedure “SubNeverDoneBeforeSubs_0_1or2()” before any of the others )

Codes:

Code:
[color=blue]Option[/color] [color=blue]Explicit[/color]
[color=blue]Public[/color] MyRange [color=blue]As[/color] Range [color=darkgreen]'( Public allows you not have to "Dim" MyRange in other Modules either! )[/color]
[color=darkgreen]'[/color]
[color=blue]Sub[/color] Sub_0()
    [color=blue]If[/color] [color=blue]Not[/color] TypeName(MyRange) = "Range" [color=blue]Then[/color] [color=blue]Call[/color] Vardef
MyRange.Offset(0, 0).Value = "[color=blue]Sub[/color]_0 did this"
[color=blue]End[/color] [color=blue]Sub[/color]
[color=darkgreen]'[/color]
Sub [color=blue]Sub[/color]_1()
    [color=blue]If[/color] [color=blue]Not[/color] TypeName(MyRange) = "Range" [color=blue]Then[/color] [color=blue]Call[/color] Vardef
MyRange.Offset(1, 0).Value = "[color=blue]Sub[/color]_1 did this"
[color=blue]End[/color] Sub
[color=darkgreen]'[/color]
[color=blue]Sub[/color] [color=blue]Sub[/color]_2()
    [color=blue]If[/color] [color=blue]Not[/color] TypeName(MyRange) = "Range" [color=blue]Then[/color] [color=blue]Call[/color] Vardef
MyRange.Offset(2, 0).Value = "Sub_2 did this"
[color=blue]End[/color] Sub
[color=darkgreen]'[/color]
[color=blue]Sub[/color] [color=blue]Sub[/color]NeverDoneBefore[color=blue]Sub[/color]s_0_1or2()
MyRange.Offset(3, 0).Value = "SubNeverDoneBeforeSubs_0_1or2 did this"
[color=blue]End[/color] Sub
[color=darkgreen]'[/color]
'
'
'
Sub Vardef()
[color=blue]Set[/color] MyRange = ThisWorkbook.Worksheets("SpdSht").Range("A1")
[color=blue]End[/color] Sub


I won’t confuse you ( us ) any more, ( Yet ) . Give you time to sober up after the Vodka!

Alan
 
Last edited:
Upvote 0
Hello DrAelstein,
Me and my workmates use this Forum a Lot, but don't usually Need to Post. (One of us might be registered) .
This Post caught my interest, so Today I registered to ask you something - Is that OK? - still checking the Rules out just now
We are responsible recently for trying to Keep track of everything stored in all our subsiduries, both Purchasing Stores and The Development Stores . It is a Nightmare - Every Store seems to have its own System for counting, and especially working out the Total Value.
We all know a good deal of Excel but VB is new to us all.
We are trying to get everything on one Massive Spreadsheet Range, fed from the Different Codes which try to do some rationalisation from the different Department's Records before feeding into the Main Range ( Sheet1 of a very big Excel File, with a Sheet for every different Store, where all info and Data is written all in different Formats!!! , ). I know we have had Problems when someone changes the Ranges. I think we all try to pass Range Variables through the different Codes ( I will check tomorrow as I am not the best of us with VB things ). The Management Paid a local College Guy to give us some Basic VBA Lessons recently, and I remember this ByRef ByValue stuff came up a Lot.
Your stuff ( -1) seems a lot simpler.
Do you
- think it could be something for us to try ?
- Do you know how Big the Range can be that you have "Outside" your codes ?
Hope it works that you get this message
Jack
:)
 
Upvote 0
@ Jack
Hi Jack,
Hmm.. Not sure if I can answer all that too well.
_ . You see i am still learning, and i am not yet too sure i have really got the point too well on exactly what I am doing here. !!!

_ . I have only just got to understand the ByRef and ByVal through this and another Thread recently
Has IIF changed??? [SOLVED]
_ . The ByRef and ByVal all seems, as you said, a much more complicated way of what I ( think ) I am doing. But I am suspicious. Maybe it is like nobody invented the Wheel yet, so to transport Heavy things we either drag them across the ground at great effort or carry them at great expense in Hovercrafts. Then Nut living in a messy House in Germany suggest some way of keeping in contact with the ground but “rolling” at little effort. It sounds absurd, and they lock him up like they would now if Einstein suggested his special relativity Theory.

_ ..Anyways as for the size of it.. I am playing around with this idea in a spreadsheet of about 3500 “columns” by 9000 “rows”, BUT I do not like working in VBA with Spreadsheet Ranges directly, and usually at the code outset I put everything into a “VBA Array”. The thing with Ranges was a bit of a side – track just now here. This all started when I originally started trying to “....Store Array created by VBA macro “Internally” for use in same VBA macro by next run...” in this Thread
Store Array created by VBA macro “Internally” for use in same VBA macro by next run..or..- [SOLVED] - Page 4
and the first thing i said in that Thread was “I bet this is not possible”, having, maybe like you, heard of the more involved ByVal ByRef Stuff for transferring Stuff between Procedures.

_. Maybe if / when you come back with some more specific data i might be able to answer better. BUT if you are thinking of giving me a Big file it may be Better to post in the last Thread i mentioned. BUT Important: Put a URL from this Thread and mention it, so as to keep in with Forum Rules on Cross Posting.

Alan

P.s. The URL is the long http://...... Bit from up there in your browser window
http://www.mrexcel.com/forum/excel-...es-between-subs-get-byref-argument-error.html
 
Upvote 0
Brother Doc thanks for enriching this sub.

just to maintain the thread within scope i will paste what i have attempted to do in details here is a picture of the workbook.

a button toggles yellow color off and another toggle the green color off:

20woyKl0.png


and here is the VBA code for each button:

Code:
Sub vardef(lastrow As Long, myrange As Range)

lastrow = Cells(3, 6).SpecialCells(xlCellTypeLastCell).Row
Set myrange = Range(Cells(3, 6), Cells(lastrow, 12))




End Sub




Sub ToggleYellow()


Dim lastrow As Long
Dim myrange As Range


Call vardef(lastrow, myrange)




Dim togglevalue As Range
Set togglevalue = Range("d1")


Dim cellcolor As Long
cellcolor = 6


Dim Myshape As Shape
Set Myshape = Sheets("sheet1").Shapes("Alpha")


If togglevalue = 0 Then


    For Each cell In myrange
    
    If cell.Interior.ColorIndex = cellcolor Then
    
        cell.Offset(0, 8).Value = cellcolor
        cell.Interior.ColorIndex = xlNone
        cell.Font.ColorIndex = 2
    
    End If
    
    Next cell
    
    togglevalue = 1
    Myshape.ThreeD.BevelTopType = msoBevelRelaxedInset
    
Else


If togglevalue = 1 Then


    For Each cell In myrange
    
    If cell.Offset(0, 8).Value = cellcolor Then
    
        cell.Interior.ColorIndex = cellcolor
        cell.Font.ColorIndex = 1
        cell.Offset(0, 8).Value = ""
    
    End If
    
    Next cell
    
    togglevalue = 0
    Myshape.ThreeD.BevelTopType = msoBevelCircle
    
End If


End If


End Sub


Sub ToggleGreen()


Dim lastrow As Long
Dim myrange As Range


Call vardef(lastrow, myrange)




Dim togglevalue As Range
Set togglevalue = Range("e1")


Dim cellcolor As Long
cellcolor = 14


Dim Myshape As Shape
Set Myshape = Sheets("sheet1").Shapes("Gamma")


If togglevalue = 0 Then


    For Each cell In myrange
    
    If cell.Interior.ColorIndex = cellcolor Then
    
        cell.Offset(0, 8).Value = cellcolor
        cell.Interior.ColorIndex = xlNone
        cell.Font.ColorIndex = 2
    
    End If
    
    Next cell
    
    togglevalue = 1
    Myshape.ThreeD.BevelTopType = msoBevelRelaxedInset
    
Else


If togglevalue = 1 Then


    For Each cell In myrange
    
    If cell.Offset(0, 8).Value = cellcolor Then
    
        cell.Interior.ColorIndex = cellcolor
        cell.Font.ColorIndex = 1
        cell.Offset(0, 8).Value = ""
    
    End If
    
    Next cell
    
    togglevalue = 0
    Myshape.ThreeD.BevelTopType = msoBevelCircle
    
End If


End If


End Sub


the functionality is simply about turning color cells into white with white font and storing the color index or code that was there into an offsetted cell nearby, when the button is pushed again that stored color number will be used to refill the cell color with the color it had before
 
Last edited:
Upvote 0
ByVal which is the default

ByRef is the default, not ByVal.

ByVal passes a copy of the variable or, in the case of an object, a copy of the pointer to the object.
ByRef passes the pointer to the variable. Arrays are passed ByRef.
 
Upvote 0

Forum statistics

Threads
1,223,373
Messages
6,171,693
Members
452,418
Latest member
kennettz

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top