Prevent excel from activating new objects (Sheets, Charts, Workbooks) - excel

Okay, I have an excel macro that processes a lot of data and makes a lot of charts. I inherited the code from a guy that recently retired, while the code functions it is very inefficient so I am rewriting it. One of the things I am trying to fix is he activated/selected everything. I prefer to use references and never select/activate anything.
But I am running into a problem that when I create a new workbook or chart, it will steal focus from the original workbook. This is annoying, because I usually don't add the workbook object in my references for my main workbook.
For example, when I use these lines, the new chart/workbook becomes active
Set wb = Workbooks.Add Or Set wC = wb.Charts.Add
I then use Workbooks(FileName).Activate to reactivate the original workbook
I find it annoying to have to do this every time, and was wondering if there was a way to prevent the new objects from becoming active.
Edit: I just realized that other actions cause the charts to steal focus, like moving the chart with this command wC.Move After:=wb.Worksheets(wb.Worksheets.Count)

If you declare an old worksheet after a new one I believe this accomplishes what you want. For example,
Dim newWst As Worksheet
Dim oldWsk As Worksheet
Set newWst = Worksheets.Add
Set oldWst = Worksheets("Sheet1")
Cells(1, 1) = "Test"
worked for me. It added "Test" to the old worksheet.

Related

Is it not a good Idea to use the Set Worksheets method multiple times during code execution?

I am attempting to copy different images to different worksheets of my Workbook using the following code below. I am changing the Target Worksheet Dynamically in a different sub, with a String Variable in the Global Declarations section. I can see the variable being passed to the sub and in fact it works the first pass through the code, but when I attempt to change the "TargetSheetIni" variable to a new sheet, it continues to use the first original sheet as it loops through.
Can you not change a target sheet after using the Set keyword? Should I refer to the sheet directly instead?
Sub Test1()
Dim TargetWS, SourceWS As Worksheet
Set TargetWS = Worksheets(TargetSheetIni)
Set SourceWS = Worksheets("Images")
DoEvents
SourceWS.Shapes(CurrentImageId).Copy
DoEvents
TargetWS.Paste Range(ColumnLetter2 & RwCnter)
DoEvents
End Sub
I think I may have figured it out. As far as I can tell the issue may be that I used the Copy Sheet Functionality in Excel when I originally created the target sheets. And even though I renamed the sheets both on the tab below and in the project editor... for some reason VBA kept targeting only the original sheet
I proved this by changing my code around to explicitly call the sheet I wanted to target like so:
ActiveWorkbook.Worksheets("Sheet2").Paste Range("I2")
And even doing that it would target sheet 1 for the paste command instead of the expected sheet 2. I deleted the three copy sheets and created a new one from scratch and re-executed code and now it targets sheet 2 as expected.
I found this article that sort of explains it I guess...
https://www.spreadsheetsmadeeasy.com/7-common-vba-mistakes-to-avoid/
Ok my last answer may have not been correct. It appears as though for some reason inserting an ws.activate caused my code to start workin.g
Very frustrating fix. as I have always heard to avoid using that.

How to copy a sheet and ensure it is at the end of all other sheets?

sheetCopy.Copy After:=ThisWorkbook.Worksheets(Worksheets.Count)
I use the code above to create a copy of a template worksheet in Excel.
Most of the time it creates the tab at the end which is what I want but sometime it creates new tab somewhere in the middle.
Is there a way to ensure it is copied to the end of the sheets?
(Thisworkbook.Worksheets.Count)
The issue
When working with a Workbook, Worksheet, Range, or other similar objects, it's best to avoid implicit member calls.
Most of these default to whatever is active. For instance, Worksheets.count is the same as ActiveWorkbook.Worksheets.count.
In your code you were almost there as you correctly used ThisWorkbook when accessing the Worksheets Collection; however, the Worksheets.count is defaulting to the ActiveWorkbook.
The Solution
To fix it, I like using With blocks to help shorten the code, and make it easier to be explicit in my refrences.
With ThisWorkbook
sheetCopy.Copy After:=.Worksheets(.Worksheets.Count)
End With

Running excel macro on multiple files

I have an excel spreadsheet with a very complex macro. The spreadsheet takes a file (imported through a button on the sheet), and runs statistics on it. is there any way to automate the macro to run on multiple files in a folder?
Thanks in advance
The macro identifies the worksheets from which it draws data and these sheets are in a workbook, which is also identified. Hence, by changing the name of the source(s) in the code, perhaps even dynamically, you can make the same macro perform its magic on other workbooks.
With that said, since VBA absolutely needs a workbooks to identify a worksheet and must have a worksheet in order to identify a cell, VBA will provide a default for either if the code doesn't mention another. Therefore the innocent Cells(1, 1) or Range("A1:B10") you may see in your code in fact stand for ActiveWorkbook.ActiveSheet.Cells(1, 1) or ActiveWorkbook.ActiveSheet.Range("A1:B10"). Therefore, if you want to change the default workbook for another the process is to first introduce a variable to specify the workbook and worksheet and then change the object assigned to that variable.
In a less generic way, let me presume that you don't have syntax like Range("A1:B10") in your code at all but Worksheets("Sheet1").Range("A1:B10"), identifying the worksheet but not the workbook. Let me further presume that the sheet names are the same in all the workbooks on which you want to run the code. In that case you would make the change as shown below.
Dim Wb As Workbook
Set Wb = ActiveWorkbook 'or perhaps ThisWorkbook (=the one having the code)
' and then change all applicable instances to
Wb.Worksheets("Sheet1").Range("A1:B10")
Now you can change the code to specify another workbook simply with:-
Set Wb = Workbooks("My workbook.xlsm")

Is there an easy way to copy the ActiveSheets .PageSetup?

Seems simple and probably a case of "It's monday" but I can't find the answer to this.. without having to record all the pagesetup variables in strings beforehand, is there a way to copy a worksheets .pageSetup property?
in a macro im running, I create a new page, paste some data, change a bunch of pagesetup settings, then print. I was hoping there was a way i could save the page setup before hand, and apply it again afterwards so my users don't have to worry about fixing settings ever.
I tried:
dim ws as worksheet
set ws = ActiveSheet.pageSetup
'settings change / print
ActiveSheet.pagesetup = ws.pagesetup
that doesnt work because ws.pagesetup is now linked to activesheet.pagesetup, so as soon as i change the active sheets settings, ws's settings get changed too.
I also tried set ws = sheets(1) because the new page is never the first page, but then with that one ActiveSheet.pagesetup = ws.pagesetup doesnt work either, it says object doesn't support this property or method
Is there a simple way? must I have 20 different string variables to hold all the current pagesetup variables?
Thanks
When I want to accomplish something like this I'll save all my formatting and pagesetup stuff to a hidden sheet. At the right time I'll have my code copy and paste everything where I need it. In this case it would probably be easiest to start by making a copy of the hidden sheet. Then dump in your data and work from there.
Rather than coping page setting from a master sheet to other sheets:
Create the master sheet
Establish the page settings for this single sheet
Make copies of the master
Populate the copies with data, formulas, etc.
In other words, anticipate your needs and setup the sheets in advance.

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