Screenupdate vs Selection issue - VBA Excel - excel

I'm facing an odd situation. I have a button on a sheet which runs many functions, being one of those opening another file:
If Not IsItOpen(ENDERECO2) Then
Workbooks.Open Filename:=ENDERECO1
End If
'ENDERECO2 has the file's name
'ENDERECO1 has the full path of the same file
'IsItOpen is a private function as follows:
'Private Function IsItOpen(Name As Variant) As Boolean
' On Error Resume Next
' IsItOpen = Not (Application.Workbooks(Name) Is Nothing)
'End Function
After opening the other workbook, when it isn't already opened, I bring focus to the first sheet, as I want the second one to be opened on the background. To do that, I use:
'At the very beggining of the code
Dim CEL As Range
Set CEL = Selection
'And at the end of it all
CEL.Select
All the described code works perfectly.
The problem I've been having: as this button runs many things at once, I wanted to add an "Application.Screenupdating = False" at the beggining and "... = True" at the end, so it won't flicker too much when calculating. The thing is that when I added the Screenupdating stuff, it will still open the second workbook as desired, but it won't bring the focus back to the main workbook. Instead, it stops at the recently opened workbook and there it stays.
What could be the interference of the Screenupdating on the CEL.Select command?
Any ideas?
Cheers

Thanks Taelsin. Guess when we don't know exactly why, we improvise lol. This worked fine:
If Not IsItOpen(ENDERECO2) Then
Application.ScreenUpdating = True
Workbooks.Open Filename:=ENDERECO1
Application.ScreenUpdating = False
End If
It is good enough. Cheers!

Related

VBA Module doesn't seem to connect to the sheet in Excel

I have been working on debugging this code and I am running into an issue where the code runs fine (no errors) but the changes it is supposed to be making aren't happening in the actual sheet.
What the code is supposed to do:
The goal is to be able to check on saving if any cells have been changed. If they have, it locks all cells with non-blank values and protects the sheet to avoid having those cells edited in future instances.
Here is the code itself:
Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim sMSG As String
sMSG = "Saving the workbook will lock the cells you have entered data into." & vbLf
sMSG = sMSG & "Do you want to go ahead?"
If Not bRangeEdited Then GoTo Xit
If Not Me.ReadOnly Then
Sheet1.Unprotect "password"
With Sheet1.Range("A1:I20")
If MsgBox(sMSG, vbExclamation + vbYesNo) = vbNo Then
Cancel = True
GoTo Xit
End If
If .SpecialCells(xlCellTypeBlanks).Address <> .Address Then
.SpecialCells(xlCellTypeConstants).Locked = True
bRangeEdited = False
End If
End With
Sheet1.Protect Password:="password", UserInterFaceOnly:=True, DrawingObjects:=False, AllowFiltering:=True
End If
Xit:
End Sub
This is based on a common piece of code found on multiple forums which seems to work for everyone else. This code USED to work and then something broke it. I thought it was the Range being wrong (which I fixed) but that didn't solve the problem.
What I've tried:
Running the separate code lines in the Immediate window - everything runs properly
Stepping through the code with F8 and debug.print to check what is being pulled by .SpecialCells(xlCellTypeBlanks).Address - this pulls the entire input range every time, regardless of what is in the cells but pulls the correct range when run in the Immediate window
Stepping through also produces no errors and shows that every if and with statement is working correctly
Running the code from different locations (Sheet1, Module, ThisWorkbook) including separating into multiple subs across different locations - no change
Running the code on a brand-new test workbook with no other macros - still doesn't work
Different methods for locking the cells such as a loop through all cells in the range instead of using SpecialCells - didn't work
Even the Protect/Unprotect lines are not working which leads me to believe that the code is somehow disconnected from the worksheet itself.
Anyone have any ideas how that is possible? Or how to fix it?

Closing different workbook ends code execution of current workbook

I am trying to run foo in Book1.xlsm that runs another sub, bar, in Book2.xlsm. Following is the code for both:
In Book1:
Sub foo()
Dim oldBook As Workbook: Set oldBook = ActiveWorkbook
Dim newBook As Workbook: Set newBook = Workbooks("Book2.xlsm")
Application.Run "'Book2.xlsm'!Module1.bar", oldBook
End Sub
In Book2:
Sub bar(book As Workbook)
book.Close False
Debug.Print "Closed Workbook!"
MsgBox "Closed workbook!"
End Sub
The execution of foo runs perfectly fine and the control is passed onto bar in Book2.xlsm. The line book.Close false runs perfectly fine (closes Book1.xlsm), but code execution stops immediately without any warning (no messages in console or popup). Shouldn't both lines be executed in Book2.xlsm since the control was passed to bar?
I tried this by calling bar workbooks(1) (workbooks(1) = Book1.xlsm) and the entirety of the code runs perfectly fine, as expected. Shouldn't the same happen
How do I preserve code execution in the first scenario, i.e. foo runs bar, change workbooks, close Book1.xlsm and continue execution of bar?
Reconsider who's responsible for what. It's not normal that a workbook is just given any random Workbook object and proceeds to Close it.
Sure you can hack around the fact that you're essentially closing the owner of the call stack, but the root of the problem is, when you have WorkbookA responsible for doing something, and that something involves opening or creating WorkbookB, then it's WorkbookA's responsibility to close WorkbookB when it's done.
Book2 has no business closing anything. Assuming the real macro actually does something more than just closing it, then it should do its thing and then let the caller decide whether to close the workbook or leave it open; Book2!Bar is given a resource that it doesn't own: it's beyond its responsibility to close that resource.
Maybe this can serve as a simple illustration:
Public Sub DoSomething()
Set t = New Thing
UseTheThing t
t.SomeMethod ' error 91: object reference is gone!
End Sub
Private Sub UseTheThing(ByRef t As Thing)
t.Foobar 42
Set t = Nothing ' not your job!
End Sub
Working around the natural order of things (caller -> callee -> back to caller) is neither recommended nor useful. Something is broken in the bigger picture - take a step back and fix the higher level instead of fighting the entire paradigm.
You can use Application.OnTime to accomplish this task.
Book2.xlsm
Public wb As Workbook
Public Sub bar(book As Workbook)
'set the variable for the workbook we want to close - We do this because we cannot pass a workbook object
Set wb = book
'set it to call 1 second from now
Application.OnTime DateTime.DateAdd("s", 1, DateTime.Now), "CloseIt"
End Sub
Sub CloseIt()
wb.Close False
Debug.Print "Closed Workbook!"
MsgBox "Closed workbook!"
End Sub
Even if you use an add-in you may face this problem. I found using the below function very useful in handling such problems. Your call stack root won't be in Book1:
Application.OnTime Now + TimeValue("00:00:01"), "'Book2.xlsm'!Module1.bar", oldBook

VBA Enable Events not working as expected

I have a worksheet_change macro embedded in two sheets within my workbook. They are there to prevent anyone making changes to the sheets. However, I still want the data within the sheets to be refreshed every so often. This does not work.
Two sheets within the workbook are connected via a query to another workbook. Essentially those sheets are a copy of the sheets within the other workbook.
I have embedded Code1 into the two worksheets. This is to prevent anyone making changes to the worksheet but still allow them to view the sheet and copy data from it. It brings up an message box and then undoes the change made by the user. This works fine and I am happy with it.
At the same time I want to be able to refresh the workbook so that the connected sheets are up to date with respect to the other workbook that they are connected to.
To do this I have added a button into the workbook called "Refresh". This button calls Code2. This was done with the intention of disabling events so that the worksheet_change macro is paused to allow for the data to be refreshed.
However, this does not work as the worksheet_change macro still works. I.e after clicking the button, the workbook is refreshed and then any update is undone and the message box is displayed - which isn't what I need.
CODE1
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("A1:Z1000")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
With Application
.EnableEvents = False
.Undo
.EnableEvents = True
End With
' Display a message when one of the designated cells has been
' changed.
' Place your code here.
MsgBox "DO NOT MODIFY THIS SHEET - Any necessary modifications should be made in 'Master Invoice Template' and this sheet will automatically be updated!"
End If
End Sub
CODE2
Sub refresh()
On Error GoTo ErrorHandler
Application.EnableEvents = False
ThisWorkbook.RefreshAll
ErrorHandler:
Application.EnableEvents = True
End Sub
I have scoured the internet for a solution and pretty much everything that I find points me in the direction of enableevents=false, but as described in my post this does not work. Do I need to change the method of solving my problem or am I doing something wrong within my code?
I suspect the undo line of code is causing the problem, but I am not sure!
Any help would be greatly appreciated!
I think I have figured out what was wrong with the code; correct me if I am wrong. The data was taking too long to refresh when Code2 was ran. This meant that the Application.EnableEvents = Ture in Code2 took effect before the data could be fully refreshed and when it finally did complete its update, the Worksheet_Change event was triggered.
I tried using DoEvents after the RefreshAll command but this didn't work either. I have used what I found in this post to work around the problem and the refresh button now works!
Specifically the code that helped is below: I replaced Code2 with this:
Sub Refresh_All_Data_Connections()
For Each objConnection In ThisWorkbook.Connections
'Get current background-refresh value
bBackground = objConnection.OLEDBConnection.BackgroundQuery
'Temporarily disable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = False
'Refresh this connection
objConnection.Refresh
'Set background-refresh value back to original value
objConnection.OLEDBConnection.BackgroundQuery = bBackground
Next
MsgBox "Finished refreshing all data connections"
End Sub
Please let me know if my logic in explaining why the code didn't work is correct - I am still new to VBA and would like to understand the problem fully!

How do I make VBA complete it's previous command (Refreshing using Bex analyser 7) before moving on to the next one?

I am attempting to create a macro which opens up a file refreshs a query and will then save and close. Currently the macro initiates the refresh however it moves on to the Save and close command before it has finished and therefore nothing changes. I have seen that there are ways off 'pausing' or 'sleeping' for a period of time to allow the command to be completed however I wish to expand this macro to opening multiple workbooks with queries which take differing times to refresh so therefore that would be a last resort. What I have currently utilizes DoEvents however this doesn't seem to be working either.
Note: The refresh works through SAP Bex analyser 7.
My code:
Sub OpenAndRefresh()
Workbooks.Open "QueryRefresh.xls", UpdateLinks:=False
Workbooks("QueryRefresh.xls").Activate
Run "BExAnalyzer.XLA!SAPBEXrefresh", True
DoEvents
Workbooks("QueryRefresh.xls").Close SaveChanges:=False
End Sub
Any help or guidance would be greatly appreciated.
I have a macro for this purpose, and I've never had this issue:
Public Sub Refresh_All()
Dim filepathstr As String
Dim filename As String
Dim wbk As Workbook
filepathstr = Sheet1.Range("filepath").Value
For Each cell In Sheet1.Range("workbooks")
If Not cell.Value = "" Then
filename = cell.Value
Set wbk = Workbooks.Open(filepathstr & filename)
''''**REFRESH**''''''
SAPBexrefresh (True)
Application.DisplayAlerts = False
wbk.Save
wbk.Close False
Application.DisplayAlerts = True
End If
Next cell
End Sub
The main difference here code-wise is that I'm calling the refresh macro directly rather than using the Run command.
EDIT: To make this work you also need to add the "BExAnalyzer" Reference in your project. If you're not familiar with adding references, you need to go into Tools --> References, then click on "BExAnalyzer" out of the long list of available references.
I'm not sure why this would make any difference but as I say, I've always found that my macro finishes refreshing (even when this takes up to 15 minutes) before continuing. I'm also using BEx 7

Auto Open used to execute another macro with VBA

I have an excel book titled Can Opener that is opened by my task scheduler. It opens another workbook on a shared drive that my task scheduler cannot access, and then closes itself.. Can Opener works fine. The problem I am having is that the other workbook has code that, upon open, if it is 7pm system time executes an update macro to get new data from the servers. Once this is done the file saves and closes. The data workbook opens, but no update is occurring, and it does not automatically save and close. I have tied breaking the code down so it would at least update, and this is not working either. I have checked several forums and other locations and I still cannot figure out what the problem is. Can I get a little help?
Sub DataBook_Open()
Application.EnableCancelKey = xlDisabled
If Hour(Now) = 7 And Weekday(Now, vbSunday) < 7 Then
Run_Update
Me.Save
Application.Quit
Else: Me.Save
Application.Quit
End If
End Sub
Run_Update is the name of the macro in my module that merely executes that other macros in the module that create the server connection and run the SQL's. This works fine if manually ran, or run with a button, so I know the error is not here. Also I am trying to run this code every night at 7pm.
I really appreciate any help I can get guys.
Are you sure DataBook_Open() executes at all?
It might not unless you're calling it from Auto_Open() or Workbook_Open().
Try this minimal test files. Replacing the paths with your actual folders.
--can_opener.xlsm--
Sub OpenOtherWorkbook()
Dim sWbkPath As String
sWbkPath = ThisWorkbook.Path & "\" & "test_data.xlsm"
Dim wbkData As Workbook
Set wbkData = Workbooks.Open(sWbkPath)
End Sub
--test_data.xlsm-- in the ThisWorkbook Object
Private Sub Workbook_Open()
If Hour(Now) = 9 Then 'replace with your condition
UpdateData
ThisWorkbook.Save
DoEvents
Else
DoEvents
End If
ThisWorkbook.Close
'Application.Quit
End Sub
Sub UpdateData()
ThisWorkbook.Sheets(1).Range("A1").Value = Format(Now, "yyyy-mm-dd:hh\hmm")
End Sub

Resources