How to use excel built-in constant in VBS [duplicate] - excel

This question already has answers here:
Getting an error `xlValues` is not defined when Cells format to the others
(3 answers)
Closed 6 years ago.
I want to use VBScript to process EXCEL instead of VBA. But I encountered code errors when calling method Range.Find, see below
Function find_range(wb, domain, var)
Dim sheet
Dim rg1, rg2, rg3
Set sheet = wb.Sheets(domain)
Set rg1 = sheet.Range("D:D").Find(var, , xlValues, xlWhole )
If Not rg1 is Nothing Then
'msgbox rg1.Cells(1,1).row
Set rg2 = sheet.Cells(rg1.Cells(1,1).row, 19)
msgbox(rg2.value)
End if
End Function
when executed find_range function, I got
variable is undefined "xlValues" "xlWhole"
error.
So I guess that I cannot just use excel built-in constants this way in VBScript.
So what is the correct way?

Unfortunately these Named Constants are part of the Excel Object Library which VBScript has no way of referencing so the best approach is to lookup the named constants in Object Browser inside Excel VBA or online via various references then create your own named constants and use them in your code.
In this example you are using two enumerations that can be identified by looking up the Range.Find() method.
xlValues is a named constant in the xlFindLookIn enumeration and has the value -4163.
xlWhole is a named constant in the xlLookAt enumeration and has a value of 1.
So once you know the value you can define them and your code should work without any more changes being required.
Const xlValues = -4163
Const xlWhole = 1
Ideally these values should be declared in the global scope of your script so they are accessible to any function or procedure.
You might ask why not specify the numeric value? Well while this is indeed a valid approach if you use the value in multiple places you then have to modify that value in multiple places if the value ever changes (however unlikely in this scenario). With a named constant you make one change and wherever in your code that value is referenced is also changed.
It's also worth noting that VBScript is only interested in the numeric value so technically you can name the constants anything you wish. However, it is good practice to follow the naming convention, especially if you re-used the function code in Excel VBA for example.

Related

Creating a Dynamic Range that works for Excel to Save Name Ranges to a Static Web Page

I have a excel document that has several name ranges that are currently save to static web pages. I have recently tried to conver them to dynamic ranges, and have learned the dynamic named ranges work great within the context of the excel sheet itself, but fail once excel tries to save them as the static web page.
For example, I have a range
YardTabletLists!$EC$1:$EE$101
sometimes the content is exceeds the bounds or I have empty rows on my webpage.
the dynamic alternative is
=OFFSET(YardTabletLists!$EC$1,0,0,COUNTA(YardTabletLists!$EC$1:$EC$10000)+1,3)
If i refernce this name range anywhere within the sheet, it works, but when AutoPublish does it thing, I get the following error.
Error Notice
I was thinking of trying to conver the Dynamic range back to a static range somehow, and then direct the name rage to that Cell.... i.e.
Name range is directed to =E4, and E4 contains YardTabletLists!$EC$1:$EE$101, but I get the feeling that will give me the same issue.
Thanks to those who read this.
Not familiar with static webpage feature.
But possible to define and use “table” instead of “named range”?
With “table” Excel internally handles the correct range whenever the table size is modified.
Seems the static webpage feature supports also “filtered ranges”. Could this be a work-around, e.g. to filter out blank lines (while using max line number as static range)?
Indirect references (your example with E4) are a little tricky in Excel. I only used it once to define a list for a dropdown.
Muss be supported by the feature and requires “special” syntax
Update:
I have found some examples for VBA updating name ranges, and one excellent example that appears to answer the question:
https://excelchamps.com/vba/named-range/#Resizing_a_Named_Range_using_VBA_Dynamic_Named_Range
Sub vba_named_range()
Dim iRow As Long
Dim iColumn As Long
iRow = ActiveSheet.Range("A1").End(xlDown).Row
iColumn = ActiveSheet.Range("A1").End(xlToRight).Column
ActiveSheet.Range("myRange") _
.Resize(iRow, iColumn).Name = "myRange"
End Sub
for some reason the program hangs up at
ActiveSheet.Range("myRange") _
.Resize(iRow, iColumn).Name = "myRange"
does anyone see perhaps a syntax error I am missing?
Thank you :)

Defining variables across Subs (w/in module) in VBA

Would like to define reference variables (calls a value from a cell in the sheet using ActiveSheet.Cells[row, col]) in one location in a module, for use across multiple subs in an MS Excel file. The file is an action tracker, the subs automate some of the emailing (each subs opens emails under given conditions). All reference variables are the same for each sub - defining in one place will make maintaining the spreadsheet much simpler.
Tried to define variables above first sub, error message appears on first value (as detailed below). I've searched (a) Global Variables and (b) how to define above the subs. However (a) variables all in the same module (b) error message as detailed below. I haven't located a guide on defining variables using ActiveSheet.Cells() references.
Option Explicit
'Defines variables for all macros:
'Defining Reference Variables
Today = ActiveSheet.Cells(2, 4)
ActionLogTitle = ActiveSheet.Cells(3, 3)
IPT_Leader = ActiveSheet.Cells(7, 7)
(On Today = ActiveSheet.Cells(2,4) error highlights on "2")
Compile error:
Invalid outside procedure
As the compiler is hinting, you cannot write assignments outside of a Sub/Function.
You can declare a function for each variable:
Function MyValue()
MyValue = ActiveSheet.Cells(2, 4).Value
End Function
Ideally you don't use ActiveSheet unless that's really what you want though.
There are a lot of ways to define today, but using a word, which is used by Excel English formula =TODAY(), is probably a discussable idea (although it will work!). In general, consider declaring the variable like this somewhere in the modules:
Public myToday as Date
Then, you may reset it everytime the worksheet is openned:
Private Sub Workbook_Open()
myToday = Date
'or
myToday = Worksheets("Name").Range("D2").Value
End Sub
Anyway, working with Public variables is in general discouraged in any programming language, thus it is probably a better idea to come up with a dedicated function or class for it.

Calling a custom built VBA function when an Excel cell is changed

I'd like to preface this question by saying that I am an undergrad in college who knows C++ and has a very rudimentary understanding of VBA.
Now then, as stated in the title I need some help configuring some VBA code for an Excel worksheet so that whenever a cell in a column (specifically the D column) is modified it will automatically update other cells within the same row.
Essentially I want this to work such that when user Bob modifies cell D26 (for example) it will call a custom function I built and insert that code into cell B26 and then repeat with a different function for cell C26.
However, this function needs to be such that if cell D27 is modified it will only modify other cells in row 27, leaving row 26 and prior or subsequent rows alone until such a time as this function is called in D28 and so on.
I'm not entirely sure if this is even possible but I'd be gracious if anybody could help me configure this.
The code I built/scavenged from the internet for my custom function is this:
http://pastebin.com/RE0V2nrT
The second function I want to call for this project is the =TODAY() function built into Excel.
The code I have scraped together so far for checking if the cell has changed is this:
http://pastebin.com/S5E8cmty
If anybody could help me understand how to write what I'm looking for it would be much appreciated. If you have a different approach to solving the issue I would also love to hear it... as long as you could help me then enact your solution, haha!
Anyways, thanks to anybody who replies.
Have a look at the worksheet events available within the Excel namespace.
For this, you would use the Change event
If you double click on the worksheet you want to monitor, you can insert a Worksheet_Change sub. Then you can use the intersect function to check if the changed cell was within your range you want to monitor (e.g. D:D).
You can specify which cells you want to change. Here I just gave an example based on what you asked. This will put the output of your function into cell B[R] and put the current date into cell C[R]. Note that I'm using the Now() function since there is no Today() function in VBA. Since this returns both date and time, I'm using the Format function to get just the date.
Just for fun, let's go a little further into the object model and first get the Worksheet object to which the target range belongs. This is not 100% necessary - you could just rely on ActiveSheet. Now, you probably don't need to do this, and it's mostly just for fun, but it's also worth noting that if you were programmatically making changes to this sheet, but had not activated this sheet first (so another sheet was active) and you had not turned off EnableEvents you would get some strange results :)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim TargetSheet As Worksheet
Set TargetSheet = Target.Parent
With TargetSheet
If Not Application.Intersect(Target, .Range("D:D")) Is Nothing Then
.Cells(Target.Row, 2) = ExtractWindowsUser()
.Cells(Target.Row, 4) = Format(Now(), "YYYY-MM-DD")
End If
End With
End Sub
Explanation
Worksheet change sub is declared like this. The Worksheet objects have pre-defined method stubs for events. Kind of like an interface, though not listed as an interface in the documentation. If you think of it in that concept, this is your event handshake. See the link I posted above for a list of the worksheet events available.
Private Sub Worksheet_Change(ByVal Target As Range)
In the next lines we are getting the worksheet object to which the object named Target belongs. You can see in the sub declaration that Target is declared as an object of the type Range. If you check out the Worksheet object (linked above) or the Range object documentation you'll see that the range object is a member of the worksheet object, and the documentation kind of sucks here, but FYI the worksheet object is contained within the Parent property. Now, originally I had my code using the ActiveSheet member of the Application object - but I've edited it for the reasons given in my answer above.
Dim TargetSheet As Worksheet
Set TargetSheet = Target.Parent
I use With Blocks to save typing the same Worksheet reference in multiple places. A With block just lets me access the members of the namespace specified (in this case members of the object TargetSheet) by typing .SomeMember. The compiler understands that every reference like this refers to whatever is specified in the opening With .... statement. I personally like this for readability, but I also recommend it for maintenance (change reference one place vs many). Also having a single reference gives a tiny, insignificant, probably not worth mentioning performance boost over multiple references as well.
With TargetSheet
Next we check whether or not Target is within the range of cells we want to watch. The If....Then should look familiar enough. For our condition we use the boolean operator Not to check if the result of the intersect function (linked above) Is Nothing. The reason we do this is to check if the return is allocated. If an object is allocated the Not SomeObject Is Nothing condition will evaluate to False. If the object is not allocated (i.e. our Intersect function failed to return anything) then the statement evaluates to True. So, from the Intersect function documentation we know that if our return is allocated, the ranges intersect and the intersecting range object was returned. Thus if we want to know if they intersect, we can just check for the opposite of a failure.
If Not Application.Intersect(Target, .Range("D:D")) Is Nothing Then
The next lines then just execute some code on cells within the same row as Target. We use the Cells member of the worksheet object to specify what cells to modify. Per the documentation, the default property for Cells is Item which lets us access a range object through a row and column index like this: .Cells[Row,Column]. So, I simply use the row of our Target object and the column you wanted (column "A" =1, "B"=2, etc. You can see this by changing excel properties to R1C1 reference style if you are interested).
.Cells(Target.Row, 2) = ExtractWindowsUser()
And I think the Format() and Now() functions are pretty well explained in the documentation.

When to use Set in my VBA code?

To my astonishment, I realized that I could not set a range at some point of my code. And I've found nowhere any mention of this issue.
So I declared (with dim) a Range variable.
And then I do Set Myvar = Worksheets("blabla").Range("A1:C3")
And Excel throw me an error code 9, out of range...
When I put the same line at the top of my code, it works (where I have my other sets).
Is that a known spec of VBA or is there something wrong for me ?
edit:
Apparently, the problem wasn't "set", but the fact that I can select a range only when the workbook is activated.
My code:
Transform_file.Worksheets("general balance").Range(Cells(startline, 1), Cells(LastCellNumber, 1)).Value = "cool"
Works only if I have Transform_file.activate before
Hence the issue
of my question wasn't Set, which can apparently be used anywhere.
I think the problem is that you are not aware how VBA uses some common defaults and assumptions. When you use:
Worksheets("blabla")
Excel assumes you mean this:
ActiveWorkbook.Worksheets("blabla")
I fact it assumes even more:
Application.ActiveWorkbook.Worksheets("blabla")
The best way to avoid these kinds of issues is to be explicit in your code by creating a workbook object when you open the workbook. Like so:
Dim wkb As Workbook
Set wkb = Workbooks.Open("blah.xls")
wkb.Worksheets("blabla")
Or if you your code refers to a worksheet that is in the same workbook as your code then you could use:
ThisWorkbook.Worksheets("blabla")
These assumptions also work for for properties of objects which is why the language has the SET command. So example when you code:
Range("A1")
What Excel will acually return is:
Range("A1").Value
This is because Value is the default property of a Range Object. The language is built this way I assume to save coding - most of the time you would wnat the Value of the range. The problem is when you want to assign the object to a variable and not it's default property which when you need to use SET. SET just says return the entire object.

What does "Shift:=" mean in this VB6 code?

I've run across the following line in a VB6 application.
mobjParentWrkBk.ExcelWorkBook.Application.Selection.Insert Shift:=xlToRight
Unfortunately Google and other search engines have not been very useful as they seem to omit the := part.
What would be a C# equivalent?
This is Visual Basic syntax for optional named parameters. The Insert function has a parameter named Shift, which is being specified.
C#, as far as I know, doesn't have an equivalent for optional named parameters. Instead, you'd need to call the Insert method, specifying Type.Missing for all parameters other than Shift.
See also the following StackOverflow question: VB.NET := Operator
UPDATE (2008-10-29):
C# 4.0 is set to introduce optional and named parameters. See this blog entry on codebetter.com.
Just to clarify John Rudy's response.
The sytax is optional (small 'o'), meaning you don't have to use it. In my experience, most VB6 coders don't use the syntax and instead prefer regular 'unnamed' parameters.
If you do choose to use it, all subsequent paramters in the sub procedure call must be named.
The named paramter syntax can be used on all parameters, whether or not the corresponding argument was declared using the VBA keyword Optional (capital 'O'). Consider, for example, this (slightly daft) VBA function with two parameters, one required and one Optional:
Private Function TimesTen( _
ByVal Value As Long, _
Optional ByVal Factor As Long = 10 _
) As Long
TimesTen = Value * Factor
End Function
In VBA, I can call it using named parameters for the required parameter (the Optional paramter can simply be omitted in VBA, unlike in C#.NET for which Type.Missing must be used for all omitted Optional parameters):
MsgBox TimesTen(Value:=9)
If I wanted to call it with the parameters in the 'wrong' order (why??) I can achieve this using named parameters:
MsgBox TimesTen(Factor:=11, Value:=9)
Trying to use a regular 'unnamed' parameter call after a named one causes a compile error:
MsgBox TimesTen(Value:=9, 11)
So why do VBA coders use named parameters if VB6 coders rarely do, even though they use essentially the same language? I think it is because the VBA generated by the MS Office applications' macro recorder tends to (always?) generate named parameters and many VBA coders learn programming this way.
VB6 and VB.net has optional parameters in method.
c# has the option to do method overloading.
VB6/VB.net way of saying Shift:=xlToRight allows passing value of a specific parameter by name.
e.g.
public sub mymethod(optional a as integer = -1, optional b as integer=1)
...
end sub
I can call this method with mymethod(b:=10)
For c#, there could be 2 methods for this
void Shift()
{
defaultDirection = directionEnum.Left;
Shift(defaultDirection);
}
void Shift(directionEnum direction)
{
}
1 more thing.
Although you can call method just by passing parameters, adding parameter name when calling makes code a little more readable.
e.g. DoSomethingWithData(CustomerData:= customer, SortDirection:=Ascending)
Hope that helps.
It's really all about Excel and how it handles inserts. If you select a range of cells and right-click Insert you will be asked which direction to shift the cells. This is from the Excel Help:
=======
Insert Method on Range Object
Inserts a cell or a range of cells into the worksheet or macro sheet and shifts other cells away to make space.
expression.Insert(Shift, CopyOrigin)
expression Required. An expression that returns a Range object.
Shift Optional Variant. Specifies which way to shift the cells. Can be one of the following XlInsertShiftDirection constants: xlShiftToRight or xlShiftDown. If this argument is omitted, Microsoft Excel decides based on the shape of the range.
CopyOrigin Optional Variant. The copy origin.
===========
If you don't have the constants defined you can subsitute the following numbers
xlShiftDown: -4121
xlShiftToLeft: -4159
xlShiftToRight: -4161
xlShiftUp: -4162

Resources