Change Datasource of a chartsheet (no embedded sheet) - excel

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")

Related

All properties of Application.ActiveSheet in Excel - Object Browser does not list the properties of Members

Microsoft Docs do not list all the properties of Object Application.ActiveSheet:
https://learn.microsoft.com/en-us/office/vba/api/excel.application.activesheet
It has only included some of the properties like: ActiveSheet.Name. But from VBA scripts I found on the internet, I know that there are more properties such as: ActiveSheet.Type
So I thought maybe I can list them all with this VBA code: from: VBA collection: list of keys
Sub AktivSheet()
Dim key As Variant
For Each key In Application.ActiveSheet
Debug.Print "Key: " & key, "Value: " & Application.ActiveSheet.Item(key)
Next
End Sub
But it didn't work, and Microsoft Docs suggests to use Object Browser. But Object Browser does not list the properties for Application.ActiveSheet:
I'm out of ideas! I just want a list of all ActiveSheet properties. Because I simply need to know the Text Direction of the Active Sheet, as in if it's Left to Right OR Right to Left.
I just want a list of all ActiveSheet properties
You cannot easily get that programmatically with 100% VBA code, VBA has pretty much zero reflection capabilities.
Now, assuming we're not looking for a programmatic way to retrieve object properties, here's how to use the object browser to get what you're looking for.
First, right-click anywhere in the object browser and select the "Show hidden members" option to reveal the full breadth of the libraries you're looking at. This affects the names list dropdown when editing code: you'll be shown hidden members now.
One of the hidden modules in the Excel type library, is a module named Global, with a hidden _Global interface:
That hidden global module is how you can type MsgBox ActiveSheet.Name and it "just works" (assuming there is an ActiveSheet - it could always blow up with error 91 when there's no active workbook open in the Application instance you're working with) even though you haven't specified what Workbook you're working with: implicitly, ActiveSheet is just working off whatever the ActiveWorkbook is.
So ActiveSheet is a property, not an object. It's a property that returns an object, but its declared type is Object.
This means any member call you make directly against ActiveSheet, is implicitly late-bound: you can type MsgBox ActiveSheet.Naem and VBA will happily compile the typo (Option Explicit can't save you here), and only blow up at run-time with error 438 "I can't find that property!".
In order to know what properties the ActiveSheet has, we need to know what run-time type we're looking at. And since a sheet in a Workbook object can be a Worksheet, a Chart, or several other types of legacy "sheet" objects, there is indeed no member accessible at compile-time, because at compile-time the ActiveSheet is just a pointer to an Object, and what type of object that is will only be known at run-time.
So instead of coding against ActiveSheet, we code against Worksheet, because we know the particular sheet we're expecting to work with is a Worksheet object.
Dim Sheet As Worksheet
Set Sheet = ActiveSheet
Now when we type Sheet., we're early-bound (types involved are known and resolved at compile-time) and helpfully provided with a list of all available members:
Every time you access a member (function, property) that returns an Object or a Variant, any member call made against it will be late-bound.
Strive to stay in the early-bound realm: declare local variables as needed, such that the compiler gets to "see" and validate everything! Try typing the below code to feel the difference - whenever you type a . dot and nothing comes up, it's a sign the compiler is losing sight of what's going on and you're possibly moving compile-time errors to run-time:
MsgBox ActiveSheet.DisplayRightToLeft '<~ late bound
Dim Sheet As Worksheet
Set Sheet = ActiveSheet
MsgBox Sheet.DisplayRightToLeft '<~ early bound

Autocomplete / drop down box for arguments past the first full stop

If I am writing an argument in Excel-hosted VBA, for example
ActiveSheet.UsedRange.Select
Halfway through writing the first part -- ActiveSheet -- CTRL+SPACE will autocomplete that part, or I can select the available options from a dropdown list.
However- the Intellisense dropdown box and autocompletion won't work /appear for anything after the first full stop. For example I can't autocomplete the UsedRange property.
Is it possible to make the suggestions appear etc. past writing the first part?
If you look at ActiveSheet in the Object Browser (press F2), you'll see that ActiveSheet is typed as Object. Therefore, Intellisense can't display any further properties specific to the Worksheet. The typing of Object makes sense, because the active sheet might be something other than a standard Excel worksheet -- e.g. a chart sheet or a dialog sheet.
Many global properties are strongly typed, e.g. ActiveWorkbook as Workbook; Intellisense will present the properties and methods just fine.
If you are certain that ActiveSheet when used in this code will always refer to a Worksheet, you could assign it to a Worksheet variable:
Dim wks As Worksheet
Set wks = ActiveSheet
Intellisense would then give you the appropriate properties and methods of the Worksheet object for the strongly-typed variable:
(Theoretically, the Excel object model could have typed ActiveSheet as something like Sheet, which would have all the common members of Worksheet, Chart and DialogSheet. I'm not sure why this wasn't done.)

Invalid use of property Excel VBA

Sub test()
Worksheets("Sheet1").Range("A1").Value = 20
End Sub
This simple code is giving error when I compile it.
activesheet. works fine.
I want to know whats resulting in an error and how to fix it...
looks like it's not identifying the sheets, workbook etc.
The answer depends on which error you get. There can be 2 issues:
1. Workbook not specified
You have more than one workbook and Excel is looking in the wrong workbook for your sheet named "Sheet1", then you need to specify the workbook.
Workbooks("my-workbook").Worksheets("Sheet1").Range("A1").Value = 20
or if it is in the workbook where the code is running at it is better to use
ThisWorkbook.Worksheets("Sheet1").Range("A1").Value = 20
Note that you should avoid ActiveWorkbook which is not very reliable.
2. Wrong worksheet name
There is no worksheet named Sheet1. Check your worksheet names. Note that there are different ways to specify a worksheet.
Specify by number
Worksheets(1).Range("A1")
This uses the position of the worksheet in the tab bar below the worksheets. Note that is not very reliable because position can easily be changed by moving the tabs around.
Specify by tab name
Worksheets("Sheet1").Range("A1")
This is probably the most common method. The worksheet is specified by its tab name. This is more reliable than by number.
Specify by VBA name
Sheet1.Range("A1")
Here the VBA name of the sheet is used. This name can only be changed in the VB editor and is not visible to the user, and has nothing to do with the tab name. Using this ensures that the VBA code still works on the desired worksheet even if a user changes the tab name of the worksheet.
So if the tab name is Sheet1 its VBA name can be Sheet5 and it can be on position 3 in the tab bar.
Using this example …
Worksheets("Sheet1").Range("A1")
Sheet5.Range("A1")
Worksheets(3).Range("A1")
… are all 3 accessing the exact same worksheet just by different names. So better to use meaningful names (and no numbers) here to not confuse.

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 to do to avoid VBA function executed in the wrong work book

I have several excel files that have timer and macros executing. But a big problem is when workbook A's macro is called, while workbook B is active. The macro is executed in the wrong book and failed.
Do I need to put windows().active at the beginning of every function?
If I have different modules how do I pass this workbook name to all of them?
This seems excessive and not right. Is there any good solution to this problem?
Looking forward to your answers
You are on the right track with this
2.If I have different modules how do I pass this workbook name to all of them
I assume that your macro is using the ActiveWorkbook property, or just using Worksheet properties like Range without qualifying them?
Instead of using ActiveWorkbook use ThisWorkbook. Instead of using Range use ThisWoorkbook.Worksheets(1).Range and so forth. Otherwise the macro will assume that the active worksheet is the one you want.
Sub MyMacro
Range("A1").Text = "Test"
End Sub
Try
Sub MyMacro(ByVal oWorksheet as Worksheet)
oWorksheet.Range("A1").Text = "Test"
End Sub
Then pass the worksheet object as a parameter.
You may also find the ThisWorkbook object useful - it is the workbook the macro resides in, or the Application.Caller object, which is the object calling the current macro, for example the Range object if it is a cell formula, or presumably the timer object in your case.
If your macros behave the way you described it, they probably depend explicitly or implicitly on
ActiveWorkbook
or
ActiveSheet
Those kind of dependencies should be avoided, if possible. The macro recorder produces such code, you should change it immediately whenever you have recorded a macro.
For example, if you have some code like
s = Range("A1").Value
Excel implicitly changes that to
s = ActiveSheet.Range("A1").Value
One can avoid that by accessing all cells, ranges, workbook parts etc. by explicitly using the right sheet or workbook object:
Dim sh as Worksheet
Set sh = .... ' Initialize sh the first time where the sheet is created or loaded
'later on:
s = sh.Range("A1").Value
By using a parameters of the form
sh as Worksheet, wb as workbook
for your subs and functions, you can pass the right sheet and workbook between modules, which answers your second question. And if you need access to the workbook where your macro resides, use ThisWorkbook.
I'd go one further... make sure your code doesn't have
Selection
or
ActiveCell
objects within them. You would need to rewrite these using the Range object.

Resources