Modifying a spreadsheet using a VB macro - excel

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.

Related

Excel global variable reseted to empty when navigating through workbooks

I encounter a weird problem that I believe is related to Excel behavior, rather than to my code.
I have a global variable named "numEtape", which is an integer. My code consists in several steps where the user has to type data on a sheet, then press a button which saves the data in an array and increments the "numEtape", before going to the next step.
The code (simplified) looks like this :
Dim numEtape As Integer
Sub AjoutEssai()
numEtape = 2
UFPreAjoutInfos.Show 'Unrelated Userform that asks user for more informations, but doesn't modify "numEtape" or call any other macro
Call InterfaceFiller
End Sub
Sub InterfaceFiller()
Dim rangedubtn As Range
Dim btnPrecedent As Button
Select Case numEtape
Case 2
'Change value of some cells
Case 3
'Change value of some cells
Case 4
'Change value of some cells
Case Is >= 5
'Change value of some cells
Case Else
Debug.Print "Error"
End Select
Set rangedubtn = Sheets("Interface").Range("M3")
Set btnPrecedent = Sheets("Interface").Buttons.Add(rangedubtn.Left, rangedubtn.Top,rangedubtn.Width, rangedubtn.Height)
With btnPrecedent
.OnAction = "mSuivant"
.Caption = "Suivant"
.Name = "btnSuivant"
End With
End Sub
Sub mSuivant()
numEtape = numEtape + 1
Call InterfaceFiller
End Sub
I don't think the code itself is important, what I can expect from it, since I first call AjoutEssai(), is for numEtape to always be greater than 2.
However, when during the steps the user opens and close other excel/office files (that don't have any vba code/macros in it), excel seems to empty numEtape, which makes the Select Case go to the Case Else.
When does excel remove global variables from memory, and is there a way to prevent this behavior from happening?
Public numEtape As Long
A viable option is to use the word public like public numEtape As Long.
Then the variable will be saving its value for as long as the Excel workbook is opened. In older versions of VBA the word was Global (What is the difference between Dim, Global, Public, and Private as Modular Field Access Modifiers?)
Dim numEtape As Long
For using Dim outside of Sub or Function, the variable will be emptied, after the code is over. Take this snippet only:
Dim numEtape As Long
Sub MainTest()
numEtape = 23
End Sub
Once you run it and you hit End Sub the variable would be emptied as well. Check after running the MainTest():
Sub PrintingVariable()
Debug.Print numEtape
End Sub
If you want to save the value, there are 2 basic ways that work:
Write the value in an excel cell
Write the value in a database

Range.Find a value from a separate workbook not working - code check

tl;dr: Running a range.find function on a reference workbook doesn't seem to find text that's definitely there. Please critique my code.
My current goal is to have Textbox B of Userform automatically populated based on user input for Textbox A. I'm trying to make that work by automatically searching a reference workbook for the Textbox A (user input) string and returning a value from an offset cell.
I finally have the search running without vomiting an error at me, but it can't seem to find text that's clearly present. My code:
Function findSLINCalc(calc As String)
'Variables
Dim calcWkbk As Excel.Workbook
Dim slinCell As Excel.Range
Dim lCalc As Excel.Worksheet
Set calcWkbk = Excel.Workbooks("New COR Calculations.xlsx")
Set lCalc = calcWkbk.Sheets(1)
'Search the calculation spreadsheet for SLIN
If Not lCalc.Range("A1:A100").find(calc) Is Nothing Then
MsgBox "I found it!"
findSLINCalc = lCalc.Range("A1:A100").find(calc).Offset(1, 10).Value
Else
MsgBox "Nothing there!"
End If
End Function
Private Sub SLINTB_AfterUpdate()
findSLINCalc (Me.SLINTB.Value)
End Sub
I'm reasonably certain I'm calling the function incorrectly in my subroutine. Even so, running this code as is only gives me the "Nothing there!" message box. Surely it would give me the other one if it found something, right?
Other possibly pertinent info:
The reference workbook will vary in length but not format or file name/location.
The string I'm searching for will be part of a merged cell (columns A+B).
Thanks in advance!

VBA Open PDFs in a list by selecting cells

I am working on a sheet that will allow the users to pull up PDFs based on partial file names in different folders from just clicking on the cell.
My question has two parts.
I want the File Path "fp" to grab the active cell value at the top of the column selected. I want to do this for easy of user use down the road in the event the paths change.
I have looked up several methods to open a PDF from excel. None of which seem to be working and I am unsure why this is.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Activate Macro by click a cell
If Selection.Count = 1 Then
If Not Intersect(Target, Range("A3:B10000")) Is Nothing Then
Call OpenFile
End If
End If
End Sub
Function OpenAnyFile(strPath As String)
'Put this in as "ActiveWorkbook.FollowHyperlink OpenMe" was not working. Was trying the shell method.
Set objShell = CreateObject("Shell.Application")
objShell.Open (strPath)
End Function
Sub OpenFile()
Dim fp As String, fn As String, TheFile As String
fp = "R:\Procurement\Invoices\"
'(((Item 1))) I want it so that fp will return the top value from the top row of this sheet as i would like the path information to be there for easy user modification.
fn = ActiveCell.Value
TheFile = Dir(fp & fn & "*.pdf")
If CBool(Len(fn)) Then
MsgBox ("File Found")
'(((Item 2))) Opening the PDf does not work. Below are two ways i have tried to achieve this.
Call OpenAnyFile(TheFile)
'ActiveWorkbook.FollowHyperlink TheFile
End If
End Sub
The way you use whitespace is very confusing and makes it hard to understand your program.
Your code won't compile as written.
Why use a Function instead of a Sub if you're not returning a value?
You'll get an overflow error if a user presses Ctrl A to select the whole worksheet.
Read about the shell function https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/shell-function
Overflow errors can occur when Target.Count is used. Typically this happens with Ctrl + A. The expanding behavior if Ctrl + A provides very little protection. At best, you must press it three times to first selecting the region, then the used range, and then the entire worksheet. At worst, once is needed.
From MS Docs, re: Range object & Count vs CountLarge properties
https://learn.microsoft.com/en-us/office/vba/api/excel.range.count
The CountLarge property is functionally the same as the Count property, except that the Count property will generate an overflow error if the specified range has more than 2,147,483,647 cells (one less than 2,048 columns). The CountLarge property, however, can handle ranges up to the maximum size for a worksheet, which is 17,179,869,184 cells.
Shell function vs Shell object
The shell function is super easy to use, no need for the overhead that comes with creating an object.
For example, using the shell function to open page 4 of myfile.pdf in Internet Explorer:
Shell("C:\Program Files\Internet Explorer\iexplore.exe " + "C:\myfile.pdf#Page=4")
Note there is a space after `"[...]iexplore.exe [...]" that is important.
Replace the executable file path with the application of your choice
Note: many applications cannot open a PDF to a specific page. Simply omit the part #Page= segment of it's a problem.
Note: you can also get the PID of the new process like this:
vPID = Shell("C:\Program Files\Internet Explorer\iexplore.exe " + "C:\myfile.pdf#Page=4")
You can do this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim rng As Range
If Target.Count = 1 Then
Set rng = Application.Intersect(Target, Me.Range("A3:B10000"))
If Not rng Is Nothing Then
'Pass the cell value and the value from the top row
' of the selected column
OpenFile rng.Value, Me.Cells(1, rng.Column).Value
End If
End If
End Sub
Sub OpenFile(fn As String, fp As String)
'etc
I had a colleague of my give me a hand with this. Thank you for all the help! Answer is below. Let me know if you see anything that can be improved upon.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Selection.Count = 1 Then
If Not Intersect(Target, Range("A3:B10000")) Is Nothing Then
Call OpenFile
End If
End If
End Sub
Sub OpenFile()
Dim filepath As String
Dim filename As Variant
'So that the macro wont try to open nothing
If ActiveCell.Value = "" Then
Exit Sub
End If
'Filepath is at the top of the worksheet above each active cell
filepath = Cells(1, ActiveCell.Column).Value
'File name is just the first portion of the file. the "*" will determine the remaining file name
filename = Dir(filepath & ActiveCell.Value & "*")
'This will open the file below. Needs the filepath and the filenname together although the filepath is inbedded in the filename
If CBool(Len(filename)) Then
ActiveWorkbook.FollowHyperlink (filepath & filename)
End If
End Sub

Date stamp a workbook on opening

I've got a workbook in which I'm hoping to automatically calculate the expiry date on initialization of the file.
My current logic in mind is as follows, but it's giving me an "Ambiguous Name" error message:
Private Sub Workbook_Open()
If Worksheet(1).Range("G30") Is Nothing Then
Range("G30").Value = Now + 120
On Error GoTo 0
End Sub
The workbook is designed to be updated as required, thus it will need to check whether a date stamp has already been marked on.
Would anyone have any suggestions in this case? Many thanks in advance!
that would be:
Private Sub Workbook_Open()
With Worksheets("General Profiling")
If IsEmpty(.Range("G30")) Then .Range("G30").Value = Now + 120
End With
End Sub
since:
Worksheet isn't a valid object reference
someRange Is Nothing
works for checking whether a Range typed variable someRange has been assigned or not
while Worksheets(1).Range("G30") simply defaults to the Value property of that Range object and then you have to check it against being Empty or not
your 2nd range reference (Range("G30").Value) isn't fully qualified
then it'd reference Range("G30") in the currently active worksheet, which could not be the one you want.
using the With Worksheets(1) - End With block and dots (.) before allRange calls will make sure they reference the same (and wanted) worksheet

Excel VBA Recalculate Selection

I've got some Excel spreadsheets that are hitting the database pretty hard (100+ queries against the general ledger table... yikes!). Refreshing just the sheet I'm on (SHIFT+F9) is helpful in some spreadsheets, but I wanted a way to refresh just the selected cells. I'm came up with the following code, placed in the ThisWorkbook object:
Dim currentSelection As String
Private Sub Workbook_Open()
Application.OnKey "+^{F9}", "ThisWorkbook.RecalculateSelection"
End Sub
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
currentSelection = Target.Address
End Sub
Private Sub RecalculateSelection()
Range(currentSelection).Calculate
End Sub
If possible, I'd like to make this more portable, such as storing it in an XLA file and loading it as an Excel addin. Is this possible with the method I'm using? Is there a better way to achieve this?
You should be able to use the following:
Public Sub RecalculateSelection()
Dim rng As Range
Set rng = Application.Selection
rng.Calculate
End Sub
You should place some error handling around the 'Set rng' line, as the user may not have selected a range (e.g. they may have selected a chart).
By using the application object you don't need to capture the Workbook_SheetSelectionChange event.
if your using the method from the accepted answer above you should first check that...
If Not Selection Is Nothing Then
If TypeName(Application.Selection) = "Range" Then
Dim Rng As Range
Set Rng = Application.Selection
Rng.Calculate
End If
End If
Also you might like to add it to the sheet and sheetname context menus.
The name of the two commandbars that you need to do that are...
"Cell" and "Ply"
If you just want to recalculate the currently selected cells, ignoring cells that are dependent on them you can use my RangeCalc addin, downloadable from
http://www.decisionmodels.com/downloads.htm

Resources