Select referenced range from non-active table via VBA in Excel - excel

Given a certain range rngRange e.g. set rngRange = Workbooks(2).Worksheets(5).Range("C4:P49") If I use rngRange.select it will throw an error message if the table containing the range is not the active window/workbook/table (e.g. by wbWorkbook.Activate and wsWorsheet.Activate). Using rngRange.Activate by itself doesn't work (throws an error), probable a simple problem. But somehow I'm blind today.
Is it possible to "activate" the range directly without activating the workbook/worksheet first? And if not, can I get a workbook/worksheet reference from the range reference somehow (note, the whole thing is inside a function that only gets the range reference, I would like to avoid to add a wbWorkbook/worksheet reference since I have to change all function calls as well)?

rngRange.Parent 'gives you the sheet
rngRange.Parent.Parent 'gives you the workbook
So you can use them to .Activate them.
rngRange.Parent.Parent.Activate
rngRange.Parent.Activate
rngRange.Select
Alternatively as #Viktor mentioned:
Application.Goto rngRange

Related

Updating Names-Collection in VBA

I'm trying to implement a validation for the named ranges in a given workbook.
I first create a list of the named ranges when the workbook is first opened.
Then, when a macro is called, I check if all the named ranges are still in the workbook and use the property Name.ValidWorkbookParameter to check, if the named Range is still valid.
I noticed however, when I overwrite a named range (the value is then e.g. #REF) and set the named Range back to its intended value, then the property Name.ValidWorkbookParameter still returns False. Is there a way to update the Names-Collection, so the properties are shown correctly?
EDIT
Function checkNamedRanges()
Dim nm As Name
For Each nm In Names
Debug.Print nm.ValidWorkbookParameter
Next
End Function

Change Datasource of a chartsheet (no embedded sheet)

I have a dataset with is quite simple. When I add data at the end of it, I want to automatically get that in my chartsheet.
For example:
At this moment, the chartsheet exists with a Datasource "A1:Q10". After I enter some values within the sheet where the Datascource is, it should change to A1:R10.
But it doens't work. Here the code which ends up in
"Application or object-defined error"
Sheets("_Chartsheet-sheet").Select
Activesheet.Chartobjects(1).Activate
ActiveChart.SetSourceDate Source:=Range("A10:" & ActualLetter & "10")
As ashleedawg noted, SetSourceDate isn't a member of the Chart class - using Option Explicit and the VBE's IntelliSense & auto-complete will help prevent these. Writing it off as a typo, there's another problem with the code...
Source:=Range("A10:" & ActualLetter & "10")
Range being unqualified, if that code is written in a standard code module, it implicitly refers to whatever the ActiveSheet is - in this case...
Sheets("_Chartsheet-sheet").Select
...a chart sheet, which isn't a Worksheet and, as such, doesn't have a Range property.
An unqualified Range call, made explicit, reads as follows:
[_Global].Range("address")
The solution is to properly qualify that Range call with the Worksheet object instance you mean to work with - assuming Sheet1:
Source:=Sheet1.Range("address")
That Sheet1 is a global-scope Worksheet object that you get for free, given that your worksheet exists at compile-time. Select the worksheet under "Microsoft Excel Objects" in the VBE's Project Explorer, then look at its properties (F4): you'll notice it has a Name property with a value that corresponds to its "tab name" (the name you'd use to fetch the worksheet by name), but it also has a (Name) property, with a value that corresponds to its "code name" - that's the identifier VBA uses to generate the free global-scope object variable you can (and should) use anywhere in your code whenever you need to refer to that worksheet.
Fetching that object from the Sheets or Worksheets collection by name, puts your code at risk: unless workbook structure is protected, the user can change that "tab name" at any time, and that will break every Worksheets("name") call in your code.
If the code is written in a worksheet's code-behind, then the unqualified Range call is implicitly referring to that worksheet. In that case, explicitly qualifying Range with Me would make the code more... explicit about its intent.
Lastly, .Select and .Activate aren't needed here. Avoid Select and Activate, it's macro-recorder code that's as frail as code gets. Instead, work the object references - here I'm assuming that the code is written in the source sheet's code-behind, hence the Me qualifier in front of Range:
Dim chartSheet As Chart
Set chartSheet = ThisWorkbook.Sheets("_Chartsheet-sheet")
chartSheet.SetSourceData Source:=Me.Range("A10:" & ActualLetter & "10")

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.

VBA Using Vlookup to assign Value to ActiveCell, in another referenced workbook

I've been trying to use vlookup in VBA and it's being quite annoying.
ActiveCell.Value = Application.WorksheetFunction.VLookup(Cells(currentrow, 12), currwor.Worksheets("Sheet1").Range("R4C3:R160C10"), 6, False)
is the code
The main elements I've used are
currwor which has been set to reference another workbook and I'm looking to use vlookup on the range in there.
However it keeps kicking up an error saying subscript out of range.
I suspect currwor is the guilty party in this
Set currwor = ActiveWorkbook (then the workbook in which the above is executed is opened, and that is assigned a workbook name as well.)
I can't use a specific range because I'm trying to perform the vlookup on each cell in a range, and that range changes everytime, and the currwor is to be executed on a particular document I get but the name may vary (format however remains the same.)
Any suggestions on it?
edit: One error is that it's not supposed to be sheet1 but 'Stock Report by Product'. However it's still kicking up the subscript out of range error.
by using Set currwor = ActiveWorkbook
you assign currently active workbook to currwor however you mentioned prior to that it should be a different workbook? meaning you should have a line Set currworb = Workbooks("desired workbook name")?
Yes - Idiotic mistake. As pointed out the sheet name was incorrect and the problem was that in execution I skipped the bit where the worksheet was assigned to currwor.
Idiotic enough that I should consider deleting this post off here.

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.

Resources