Setting filename variable once to be used multiple places - excel

I have several macros that require the use of the filename so changing them all when the filename changes is a bit of a pain. I would like to be able to enter it once and have that update everywhere. I've tried and it fails at the "Windows" callout.
Global thisfilename As String
Sub setfilename()
thisfilename = ThisWorkbook.Name
MsgBox thisfilename
End Sub
and this is where it is failing
Sub Copy70io()
'
' copychart Macro
'
Windows("thisfilename").Activate
I'm sure my error is pretty simple but I have no programming experience beyond Google and I haven't been able to figure out how to solve this.
Thanks

You are getting an error because your variable is within quotes. And anything within quotes is considered as a string :)
Try this
Windows(thisfilename).Activate
FindWindow has also given you an alternative.
Here is how I would do it...
Instead of using a Global variable, convert your code to a function. Here is an example
Function wb() As Workbook
Set wb = ThisWorkbook
End Function
And then you can use it as
Sub Copy70io()
wb.Activate
End Sub
Another Example
Function wb() As Workbook
Set wb = Workbooks("Blah Blah")
End Function
Sub Copy70io()
wb.Activate
End Sub

Related

Possible to write Code from cells to VBA?

I was wondering how I would call something in VBA to write its code while running? So I mean if I had the text in A1 read:
sub Write()
Call OtherScript
End Sub
So again that is text inside the cell not in a VBA script. And then in a script while its running it Calls "A1" and the code that's in A1 gets run through VBA without having to actually put it in there.
This is not a real code obviously, I am really just trying to find out if this is possible. A friend that helps me learn to code and works me through a lot of VBA's said he does not know how that would work so Im posting it here to see if possible.
Please, try the following code. Before running it, write in a cell:
sub WriteSomething()
Call OtherScript
End Sub
You cannot create a function/sub named Write because this word is illegal, meaning something else in VBA.
and in the next cell (on the same row):
sub OtherScript()
MsgBox "Hello!"
End Sub
I used "K2". Use it too, or adapt the range from the code. You should also have a Module3 standard module. Please, update the module name with the one existing in your vbProject. Anyhow, the code can also create the module...
Copy the next code and run it:
Sub write_Run_Subs()
'It needs a reference to 'Microsoft Visual Basic For Applications Extensibility x.x'
Dim vbProj As VBProject, objMod As VBComponent, mdlName As String
Dim rngStr As Range, strSub1 As String, strSub2 As String
Set rngStr = Range("K2")
strSub1 = rngStr.value
strSub2 = rngStr.Offset(0, 1).value
mdlName = "Module3" 'of course, it have to exist in ThisWorkbook vbProject
Set vbProj = ThisWorkbook.VBProject
Set objMod = vbProj.VBComponents(mdlName)
objMod.CodeModule.AddFromString strSub1
objMod.CodeModule.AddFromString strSub2
Application.Run mdlName & ".WriteSomething"
End Sub
It is only a simple code without too much error handling, but it should work... If you run it twice, it will insert two such subs, if not preliminarily check their existence.
If adding the necessary reference looks complicated, please firstly run the following code, which will add it:
Sub addExtenssibilityReference()
'Add a reference to 'Microsoft Visual Basic for Applications Extensibility 5.3':
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:="{0002E157-0000-0000-C000-000000000046}", _
Major:=5, Minor:=3
End Sub

Get Codename from property and pass as a variable

I have a function that takes a sheetname as a parameter like
Sub do_things(sheetCodeName as Variant)
sheetCodeName.Cells(1,1) = "Hello"
End Sub
I want to be able to get the codename of a given sheet using something like ActiveSheet.Codename and pass that as the codename parameter to my do_things subroutine. However, I get
Run-Time error '424' Object Required.
This seems to be because ActiveSheet.Codename is a string while actually typing the codename in the subroutine call passes it as a worksheet.
Is there a way for me to gather the codename of a sheet and pass it to my do_things sub without having to manually type it?
The simpler solution is just to pass a Worksheet object:
Sub do_things(ByVal ws As Worksheet)
ws.Cells(1,1).Value = "Hello"
End Sub
You can just pass the ActiveSheet directly if needed. Trying to use the .CodeName is unnecessary.
You could just pass the ActiveSheet, but if you can't do that for some reason you can change your sub:
Sub do_things(sheetCodeName as Variant)
Worksheets(sheetCodeName).Cells(1,1) = "Hello"
End Sub
It doesn't work now because the string is not an object.

Macro for formatting before print

I've seen similar questions to this but haven't found a good answer. I am trying to have a macro run automatically that formats the footer date and font before printing.
This code doesn't work, but is close on the date/font formatting:
Sub Fix_Footer_Date()
ActiveSheet.PageSetup.CenterFooter = "&14&""Verdana,Bold" & Format(Now, "mmmm dd, yyyy")
End Sub
And something like this will automatically run it before it prints?
Option Explicit
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Your code here
End Sub
How can I combine these into one that works? Any help greatly appreciated.
First thing is that I strongly encourage you to qualify the sheet you wish to operate on versus using ActiveSheet, as ActiveSheet may not always be the sheet you desire to work with.
Second is, I fixed the syntax to get your Page Footer to work as intended. I simply recorded a macro to get the correct syntax (and adjusted it for your formula).
Sub Fix_Footer_Date()
Dim ws as Worksheet
Set ws = ThisWorkbook.Worksheets("mySheet") 'change as needed
ws.PageSetup.CenterFooter = "&""Verdana,Bold""&14" & Format(Now, "mmmm dd, yyyy")
End Sub
Then, just call the macro in your Before_Print event.
Option Explicit
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Fix_Footer_Date 'you could also write the code directly into this event, if you wish.
End Sub

Worksheet_Deactivate() Cannot Use Active Sheet functions

I have 50 datasheets in the project, and nobody remembers to run the save macro when going to another sheet. The bright idea is to use a private sub Worksheet_Deactivate to do the necessary calculations when they select another worksheet. In addition to the 50 datasheets, there are two more worksheets in the workbook for which the calculation must not run. It would be nice if the sub could be put in "Worksheets" rather than replicated 50 times in individual worksheets, but the two other worksheets need to be excluded from processing.
Problem is, the sub defaults to the deactivating worksheet (such as an unqualified "Range.Value =" in the macro code), but the active worksheet is now the worksheet being navigated TO. So any ActiveXXXXX statement directs to the wrong worksheet. Worksheet.Name is disallowed.
Datasheets are numbered 1 to 50. What is needed is a statement early in the deactivate sub similar to
If DeactivatingWorksheet(X) = "BasicInfo" Or "Constants" Then GoTo EndSub
where X is the value of the deactivating worksheet. Of course, X is known only to Excel at the moment of processing.
I can't seem to figure out how to refer to the deactivating worksheet in the macro's IF statement. Any ideas?
Use Workbook_SheetDeactivate(ByVal sh as Object) instead of Worksheet_Deactivate(). The Workbook-level event supplies the name of the sheet being departed, even though in both cases the ActiveSheet has already changed when when event fires. Use sh just like a worksheet variable - sh.Name, sh.ProtectionMode, etc.
Now you don't need 50 subs; just one. Another thing that this allows is, you can "abort" the change to the now ActiveSheet by sh.Activate to the old one (but turn off events or you'll have a lovely infinite loop).
Me also gives the old sheetname and works for the worksheet event, if you still want to go that way. Me is the old one, ActiveSheet is the new one.
If you are using Worksheet_Deactivate and this calls a subroutine in a seperate module, you can pass the name of the deactivating worksheet to the subroutine.
For instance, if your subroutine is something like:
Sub test()
ActiveSheet.Range("whatever") = "something"
ThisWorkbook.Save
End Sub
And you call it from the worksheet like:
Private Sub Worksheet_Deactivate()
Module1.test()
End Sub
You can add a parameter to the subroutine to take the worksheet name, and add a test:
Sub test(worksheetname as string)
If worksheetname <> "dontsavethistab" then
ActiveSheet.Range("whatever") = "something"
'or... you could also do:
Sheets(worksheetName).Range("Whatever") = "something"
ThisWorkbook.Save
End If
End Sub
And call it from your Worksheet_Deactivate event like:
Private Sub Worksheet_Deactivate()
Module1.test (Me.Name)
End Sub
If you wanted to get a little cleaner, instead of the worksheet name you could pass the worksheet object:
Private Sub Worksheet_Deactivate()
Module1.test(Me)
End Sub
Sub test(ws as worksheet)
If ws.name <> "dontsavethistab" then
ws.Range("Whatever") = "something"
ThisWorkbook.Save
End If
End Sub
This way you have the entire worksheet object to do with as you please in your subroutine.

Modifying a spreadsheet using a VB macro

I have two spreadsheets... when one gets modified in a certain way I want to have a macro run that modifies the second in an appropriate manner. I've already isolated the event I need to act on (the modification of any cell in a particular column), I just can't seem to find any concrete information on accessing and modifying another spreadsheet (this spreadsheet is located on a different LAN share also... the user has access to both, though).
Any help would be great. References on how to do this or something similar are just as good as concrete code samples.
In Excel, you would likely just write code to open the other worksheet, modify it and then save the data.
See this tutorial for more info.
I'll have to edit my VBA later, so pretend this is pseudocode, but it should look something like:
Dim xl: Set xl = CreateObject("Excel.Application")
xl.Open "\\the\share\file.xls"
Dim ws: Set ws = xl.Worksheets(1)
ws.Cells(0,1).Value = "New Value"
ws.Save
xl.Quit constSilent
You can open a spreadsheet in a single line:
Workbooks.Open FileName:="\\the\share\file.xls"
and refer to it as the active workbook:
Range("A1").value = "New value"
After playing with this for a while, I found the Michael's pseudo-code was the closest, but here's how I did it:
Dim xl As Excel.Application
Set xl = CreateObject("Excel.Application")
xl.Workbooks.Open "\\owghome1\bennejm$\testing.xls"
xl.Sheets("Sheet1").Select
Then, manipulate the sheet... maybe like this:
xl.Cells(x, y).Value = "Some text"
When you're done, use these lines to finish up:
xl.Workbooks.Close
xl.Quit
If changes were made, the user will be prompted to save the file before it's closed. There might be a way to save automatically, but this way is actually better so I'm leaving it like it is.
Thanks for all the help!
Copy the following in your ThisWorkbook object to watch for specific changes. In this case when you increase a numeric value to another numeric value.
NB: you will have to replace Workbook-SheetChange and Workbook-SheetSelectionChange with an underscore. Ex: Workbook_SheetChange and Workbook_SheetSelectionChange the underscore gets escaped in Markdown code.
Option Explicit
Dim varPreviousValue As Variant ' required for IsThisMyChange() . This should be made more unique since it's in the global space.
Private Sub Workbook-SheetChange(ByVal Sh As Object, ByVal Target As Range)
' required for IsThisMyChange()
IsThisMyChange Sh, Target
End Sub
Private Sub Workbook-SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
' This implements and awful way of accessing the previous value via a global.
' not pretty but required for IsThisMyChange()
varPreviousValue = Target.Cells(1, 1).Value ' NB: This is used so that if a Merged set of cells if referenced only the first cell is used
End Sub
Private Sub IsThisMyChange(Sh As Object, Target As Range)
Dim isMyChange As Boolean
Dim dblValue As Double
Dim dblPreviousValue As Double
isMyChange = False
' Simple catch all. If either number cant be expressed as doubles, then exit.
On Error GoTo ErrorHandler
dblValue = CDbl(Target.Value)
dblPreviousValue = CDbl(varPreviousValue)
On Error GoTo 0 ' This turns off "On Error" statements in VBA.
If dblValue > dblPreviousValue Then
isMyChange = True
End If
If isMyChange Then
MsgBox ("You've increased the value of " & Target.Address)
End If
' end of normal execution
Exit Sub
ErrorHandler:
' Do nothing much.
Exit Sub
End Sub
If you are wishing to change another workbook based on this, i'd think about checking to see if the workbook is already open first... or even better design a solution that can batch up all your changes and do them at once. Continuously changing another spreadsheet based on you listening to this one could be painful.

Resources