I've encountered a strange thing: I've joined three workbooks: Personal Data Tracker, Global Tracker and the workbook with pivots and charts. The logic is as it follows: the user clicks on a button after the work is finished so the data is copied to the GL Tracker. Once the change event is triggered in the GL Tracker Table, the last workbook opens, the pivot is refreshed upon the open vent and the wb is closed.
Everything seems to be working fine, however when I run the macro live, at the very end I get an error message about
"Application-defined or object-defined error".
Only OK and Help button displayed, it doesn't make the VBE Open so I could debug it.
Would anyone know what it may be happening even if the whole chain works fine?
Thank you.
Code from the Personal Tracker:
Sub test()
Dim path As String
Dim wb As Workbook
path = ThisWorkbook.path & "\Dest.xlsm"
Application.ScreenUpdating = False
ThisWorkbook.Sheets("Total").Range("R1").Value = Date
Range("R1").Font.Color = VBA.ColorConstants.vbWhite
Worksheets("TOTAL").Range("B2:B13").Copy
On Error GoTo Handler
Workbooks.Open (path)
On Error GoTo 0
Set wb = Workbooks("Dest")
Worksheets("Sheet1").Range("B2").PasteSpecial Paste:=xlPasteValues
Exit Sub
Handler:
MsgBox "Someone else is saving their data at the moment." & vbNewLine & _
"Please try in a few seconds"
End Sub
Code from the GL Tracker:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim MRange As Range
Dim wbPivot As Workbook
Dim pt As PivotTable
Dim ws As Worksheet
Dim Name As String
Dim answer As VbMsgBoxResult
Set MRange = ThisWorkbook.Sheets(1).Range("Table1")
Name = Application.UserName
Application.ScreenUpdating = False
If Not Intersect(Target, MRange) Is Nothing Then
Application.EnableEvents = True
Set wbPivot = Workbooks.Open("C:\Users\jakub\Desktop\Excel - various\Pivot.xlsm")
End If
'refresh
For Each ws In wbPivot.Worksheets
For Each pt In ws.PivotTables
pt.PivotCache.Refresh
pt.Update
pt.RefreshTable
Next
Next
'saving
Application.ScreenUpdating = True
If Application.UserName <> "Jakub Tracz" Then
MsgBox "User not authorised. Workbook will be closed."
wbPivot.Close True
ThisWorkbook.Close True
Else
answer = MsgBox(Prompt:="Do you want to save and close the workbook?", _
Buttons:=vbYesNo + vbQuestion)
Select Case answer
Case vbYes
wbPivot.Close True
ThisWorkbook.Close True
Case vbNo
MsgBox "Welcome, " & Application.UserName
End Select
End If
End Sub
I'm going to give you a proof of concept code as an example for you to use. This will not exactly answer your question with code you can just copy/paste, but you will be able to use this to put it together the way you want it to work instead of me making assumptions about many things and restructuring it myself.
This simply demonstrates how to use a workbook object variable in one routine that can reference another workbook, and how to make changes to that 2nd workbook and save/close it.
Sub Tracker_Update()
Dim wbPivot as Workbook
' open the workbook
Set wbPivot = Workbooks.Open("C:\Users\jakub\Desktop\Excel - various\Test.xlsx")
' optionally make it hidden
wbPivot.Visible = False
With wbPivot
' pretend this code updates the pivot table
.Worksheets(1).Range("A1") = "hello world"
' Close and save it
.Close True
End With
' optionally clear the variable
' this is not really needed in VBA, but if you eventually
' start using VB.NET with Excel as a COM object,
' you will want to know how to do this part when you are done
Set wbPivot = Nothing
End Sub
I think you will like this approach in the end much better in the end anyway, as the code isn't scattered around so much in different places. Easier to debug later, and easier for someone else to understand what you are doing if and when you leave the company.
Related
I have 3 queries (Power Query) in my Excel file that bring information from other different files in table format and in another worksheet I have a button that executes the ThisWorkbook.RefreshAll function.
Sub ButtonRefreshData()
ThisWorkbook.RefreshAll
End Sub
The problem is: Every time I update my queries, either through the button or Excel's own Refresh/Refresh all tool, my Active Worksheet is changed to the tables that I'm refreshing.
I also tried to set the sheet that I want as active after the refresh with something like ThisWorkbook.Sheets("MySheet").Activate, but no results.
How can I update my queries without changing my active sheet?
I'm currently using Excel 2016.
What I'm trying to do: I want the user to be able to update the data in the worksheet, but I don't want him to have direct access to the tables/data in the worksheets that will be hidden. The user must remain in the "main worksheet", which has the update button.
Activate the Previously Active Sheet
I couldn't reproduce your issue. I created two queries but both of them didn't change the ActiveSheet. Here are two ideas, the second being a bit too extreme i.e. it may hang or crash Excel. Your feedback is appreciated.
EDIT
Possibly get rid of all related to ash and just use the explicit Sheet1.Select after twRefresh.
Option Explicit
Sub Test1()
Application.ScreenUpdating = False
Dim ash As Object: Set ash = ActiveSheet
twRefresh
ash.Select
Application.ScreenUpdating = True
End Sub
Sub twRefresh
ThisWorkbook.RefreshAll
End Sub
Initial Ideas
Option Explicit
Sub Test1()
Application.ScreenUpdating = False
Dim ash As Object: Set ash = ActiveSheet
ThisWorkbook.RefreshAll
DoEvents
ash.Select
Application.ScreenUpdating = True
End Sub
Sub Test2()
On Error GoTo ClearError
Application.Visible = False
Dim ash As Object: Set ash = ActiveSheet
ThisWorkbook.RefreshAll
DoEvents
ash.Select
ProcExit:
Application.Visible = True
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume ProcExit
End Sub
Another user who had a similar problem reported that he was unable to resolve the issue and he attributed the cause to the version of Excel he was using, which turned out to be the same as mine.
In this sense, I assume that the cause is some bug in the version: Excel 2016 - 16.0.4266.1001
If you are having the same problem, please try to use newer versions.
I have a CommandButton which opens a UserForm and create a copied Sheet with the name of the ComboBox Value.
This is My Code:
Private Sub CommandButton1_Click()
[UserForm1].Show ' Open UserForm
End Sub
Private Sub CommandButton2_Click()
Dim ws As Worksheet
ActiveWorkbook.Sheets("Sheet1").Visible = True ' Unhide Sheet
Sheets("Sheet1").Copy _
Before:=ActiveWorkbook.Sheets("Sheet1") ' Copy Sheet
Set ws = ActiveSheet
ws.Name = ComboBox1.Value ' Name Sheet
[UserForm1].Hide ' Close UserForm
ActiveWorkbook.Sheets("Sheet1").Visible = False ' Hide Sheet again
End sub
Now my problem is, if there are two machines with name "Machine Type 1" Excel gets an Error. So what do i have to change in my code, that the second sheet would named e.g. "Machine Type 1 (2)?
Thanks for your help.
you could try this
Private Sub CommandButton1_Click()
If IsSheetThere(ComboBox1.Value) Then 'if some sheet with chosen name already there
Sheets(ComboBox1.Value).Copy Before:=Sheets(10) ' copy the existing sheet
With ActiveSheet 'reference just copied sheet
.UsedRange.Clear 'clear its content
Sheets("Sheet1").UsedRange.Copy ActiveSheet.Range("A1") ' copy Sheet1 content and paste into it
End With
Else 'otherwise
Sheets("Sheet1").Copy Before:=Sheets(Sheets.Count) ' make a copy of "Sheet1" sheet
ActiveSheet.Name = ComboBox1.Value 'and rename it as per chosen name
End If
Me.Hide
End Sub
Function IsSheetThere(shtName As String) As Boolean
On Error Resume Next
IsSheetThere = Not Sheets(shtName) Is Nothing
End Function
the code line:
Sheets(ComboBox1.Value).Copy Before:=Sheets(10) ' copy the existing sheet
is the one that leaves Excel the burden of somehow "counting" the number of already existing sheets with the chosen name, and name the new one appropriately
You can use the following sub which calls the below function, just apply the same logic using .Copy
Sub create_new_sheet_with_name(name As String, wb As Workbook, aftersheet As Variant)
Dim i As Integer
i = 2
If sheet_name_exists(name, wb) Then
Do While sheet_name_exists(name & " (" & i & ")", wb)
i = i + 1
Loop
wb.Sheets.Add(after:=aftersheet).name = name & " (" & i & ")"
Else
wb.Sheets.Add(after:=aftersheet).name = name
End If
End Sub
Function sheet_name_exists(name As String, wb As Workbook) As Boolean
For Each sheet In wb.Worksheets
If sheet.name = name Then
sheet_name_exists = True
Exit Function
End If
Next sheet
sheet_name_exists = False
End Function
here's an example of how to use the sub:
Sub test()
create_new_sheet_with_name "hi", ThisWorkbook, ThisWorkbook.Sheets(1)
'this adds a new sheet named "hi" to thisworkbook after thisworkbook.sheets(1)
End Sub
Technically this isn't an answer to this question... but it's better because it will help you solve this and many other coding tasks on your own.
There is a simple way to create VBA code for most basic tasks.
If there's something Excel can do that you want to be able to do programmatically, just Record a Macro of yourself performing the action(s), and then look at the code that Excel generated.
I have a terrible memory, I can't remember commands I used yesterday. So it's not only quicker and less frustrating for others for me to figure it out myself, but the more often I do that, the quicker I'll learn (without asking others to do the thinking for me on a basic question).
I fact, I'm guess that the majority of veteran VBA coders learned at least partly by analyzing recorded macros. I know I did.
I've run into an interesting situation with Excel, and I was wondering if anyone of you knew an answer / solution.
Setup:
I have an empty excel worksheet with a ActiveX ToggleButton on it.
In a VBA code, I change the width of the button to 0, and then change the width to 100. (Why I do that is a different question, its part of a larger project, however, this causes the problem).
Then I set the save-status of the workbook to true, without actually saving the workbook.
Normally, if I now closed the workbook, it would just close, without asking me wether I want to save.
Now, due to the resizing, and even though the .Saved-Status is True, it asks me if I want to save when I close the workbook, and by clicking onto the close Icon of excel, the .Saved-Status is set to "False"
If you want to try for yourself, try the below steps.
Alternatively, I uploaded for convenience the same file here: (https://filebin.ca/3aLbbRxMTdUs/SavePromptUponResize.xlsm)
1) create a new workbook and add a new ACTIVEX toggle button.
2) in the VBA code of the workbook, add the below code
3) save the workbook, close it, and reopen it.
4) You should see a messagebox and after clicking, as second one
5) Now the status of the workbook is "saved"
6) Try to close the workbook -> you will be prompted if you want to save
7) If you check the .saved status now, it would say "false"
Looking forward to your insights!
Private Sub Workbook_Open()
MsgBox "Now a macro will run and resize twice the button you see." & vbCrLf & "Afterwards, the status of the workbook will be set to 'saved'." & vbCrLf & "However, upon closing, excel will still prompt to save."
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Sheets(1)
ws.Shapes("ToggleButton1").Width = 0
ws.Shapes("ToggleButton1").Width = 100
wb.Saved = True
MsgBox "Macro finished, save status is: " & wb.Saved
End Sub
Private Sub CheckSaveStatus()
MsgBox "Save status is: " & ActiveWorkbook.Saved
End Sub
The solution is as simple as this:
Private Sub Workbook_Open()
MsgBox "Now a macro will run and resize twice the button you see." & vbCrLf & "Afterwards, the status of the workbook will be set to 'saved'." & vbCrLf & "However, upon closing, excel will still prompt to save."
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Sheets(1)
Dim boolSaved As Boolean
boolSaved = wb.Saved
ws.Shapes("ToggleButton1").Width = 0
ws.Shapes("ToggleButton1").Width = 100
If boolSaved Then wb.Save
MsgBox "Macro finished, save status is: " & wb.Saved
End Sub
This technique can be used anywhere in your code where you need to do something that 'dirties' the workbook. The beauty of it is that it preserves the save prompt if the user has modified the workbook.
As to why it's happening, best guess is - a(nother) ActiveX bug in Excel.
Please try the below:
Option Explicit
Dim wb As Workbook
Dim ws As Worksheet
Dim i As Integer
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If i = 1 Then
wb.Saved = False
Else
wb.Saved = True
End If
End Sub
Private Sub Workbook_Open()
MsgBox "Now a macro will run and resize twice the button you see." & vbCrLf & "Afterwards, the status of the workbook will be set to 'saved'." & vbCrLf & "However, upon closing, excel will still prompt to save."
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
ws.Shapes("ToggleButton1").Width = 0
ws.Shapes("ToggleButton1").Width = 100
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
i = 1
End Sub
My experience has been that this Excel bug is triggered as a result of using VBA code to change the value of some properties of some ActiveX/OLE controls. I don't have a complete list of such properties but here's a few:
CommandButton:
Enabled property
Height property
TextBox:
ForeColor property
BackColor property
I use a generalized system (similar to Nikolaos Polygenis's solution), including extensive explanation due to this bug's subtlety, as follows:
In a standard module, define a project-global variable to indicate the no-save-needed condition:
'*************************************************************************************************************
'EXCEL OLE/ACTIVE-X SAVE-BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND
'
'(See the Workbook_BeforeClose event handler, in the ThisWorkbook module, for a complete explanation.)
'
Public SuppressWkBkSaveMsg As Boolean
'
'END SAVE-BUG WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND
'*************************************************************************************************************
In the ThisWorkbook module, place the following Workbook_BeforeClose event handler, with full explanation:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'*************************************************************************************************************
'EXCEL OLE/ACTIVE-X SAVE-BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND
'
'Excel has a very subtle bug in which, if you change the value of some properties of some ActiveX/OLE
'controls, the parent workbook's Saved property will not function correctly until the next actual workbook
'save event. That is, you can subsequently set the workbook's Saved property to True but Excel will still
'prompt the user about whether to save the workbook when closing it. In fact, if you check the value of the
'Saved property in a Workbook_BeforeClose event handler, it will be defined as False even though no changes
'have been made to the workbook after explicitly setting it to True!
'
'The most effective workaround is to defer the override of the workbook's Saved property until the Close event
'has been actually initiated: declare the project-global SuppressWkBkSaveMsg variable in a standard module,
'add this Workbook_BeforeClose event handler to the ThisWorkbook module, and then set SuppressWkBkSaveMsg to
'True whenever it's necessary to suppress Excel's user-inquiry about whether to save changes to the workbook
'before closing it.
'
If SuppressWkBkSaveMsg Then Me.Saved = True
'
'END SAVE-BUG WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND
'*************************************************************************************************************
End Sub
In any location in the code, in any module, whenever it's necessary to suppress Excel's user-inquiry about whether to save changes to the workbook before closing it, set SuppressWkBkSaveMsg to True:
'Do stuff that doesn't need to be saved, including modifications to OLE/ActiveX control-objects...
'
'*************************************************************************************************************
'EXCEL OLE/ACTIVE-X SAVE-BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND
'
'(See the Workbook_BeforeClose event handler, in the ThisWorkbook module, for a complete explanation.)
'
SuppressWkBkSaveMsg = True
'
'END SAVE-BUG WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND
'*************************************************************************************************************
'
'...
I have an Excel macro that creates another workbook for the user to modify. When I try to enter data in a cell in the new workbook, however, I get the error "The cell or chart you're trying to change is on a protected sheet." The sheet in the macro workbook is, in fact, protected, but that sheet is not visible at the time I get the error. When I try to close the visible, newly created workbook, it’s the macro workbook that closes. The last thing my macro does is activate the sheet in the new workbook. What do I have to do to make that work? Having the macro close its own workbook solves the problem, of course, but that’s not what I want to do, since the user needs another macro in the same workbook to process the changes on the new sheet.
The program has over 6000 lines of code (so far), but here is one of the routines that causes the problem.
Private Sub z3_BudgetPrepUpd()
'Build a new workbook initialized to let the user modify data
'for an existing fiscal-quarter budget.
'When this routine executes,
' UserForm1 is still active.
Dim strTracer As String 'error-handling tracer for this subroutine
Dim strFyrQtr As String
On Error GoTo abend
If blnAbort Then Exit Sub
If blnAbortGlobal Then Exit Sub
'Find out which ListBox item the user selected:
If UserForm1.ListBox1.ListCount < 1 Then GoTo aa9000 'ListBox is empty
If UserForm1.ListBox1.ListIndex < 0 Then 'No item selected
strMsgTitle = udtPrm.msgTtl
strMsgPrompt = "Please select a fiscal quarter to update."
Call z0_MsgBox
GoTo aa9000
End If
strFyrQtr = UserForm1.ListBox1.Value 'Selected item in ListBox
'Close UserForm1:
UserForm1.Hide
ThisWorkbook.Sheets(c_WKS_WELCOME).Activate
'Build the udtBgt() array with data for the specified quarter:
lngBgtHiNdx = -1
Call zz_GetBudgetForQtr(strFyrQtr)
If blnAbort Then GoTo aa9000
'Build a new workbook for the user to update budget amounts:
Workbooks.Add
Set wkbNewBook = ActiveWorkbook
'Save the names of the default worksheets
'so we can delete them later:
strDfltSheets() = z0_SheetNames(wkbNewBook)
'Build a worksheet with data from the udtBgt() array:
Call z3_BuildBudgetUpdSheet
If blnAbort Then GoTo aa9000
'Delete the default worksheets:
Call z0_DeleteSheets(wkbNewBook, strDfltSheets())
If blnAbort Then GoTo aa9000
wkbNewBook.Sheets(c_WKS_IPT_BUDGET).Activate
'Excel 2016 Bug:
'We need to close ThisWorkbook to allow the user
'to work with the book we just created:
Application.DisplayAlerts = False
ThisWorkbook.Close
aa9000:
Exit Sub
abend:
lngErr = Err.Number
strErr = Err.Description
blnAbort = True
Application.Cursor = xlDefault 'no more hourglass
strMsgTitle = "Program Error"
strMsgPrompt = "The following error occurred:" & Chr(10) & Chr(10) & _
"Error No. " & CStr(lngErr) & Chr(10) & _
"Error Description: " & strErr & Chr(10) & _
"Subroutine: z3_BudgetPrepUpd" & Chr(10) & _
"Tracer: " & strTracer
Call z0_MsgBox
Resume aa9000
End Sub
You use ThisWorkbook which I agree with. You use ActiveWorkbook which I hardly ever use.
I'd recommend using a variable to store reference to workbooks other than that which houses your code. So use
Dim wb As Excel.Workbook
Set wb = Application.Workbooks.Open("c:\test.xlsm") 'for opening
'* or
Set wb = Application.Workbooks.Add 'for creating a new one
'* or
Set wb = Application.Workbooks.Item("AlreadyOpen.xlsm") 'for referencing one already open
'* from hereon use wb instead of ActiveWorkbook
wb.Worksheets.Item(1).Visible = True
Thanks, everyone, for your interest and suggestions. I have solved the problem by redesigning the application without UserForms or external workbooks. Office 2016 has many issues, and perhaps this is one of them. In any case, I have no more time to research it.
I want to open a workbook (WB1) and then as it's opened, another workbook (WB2) is opened. I want WB2 hidden.
Private Sub Workbook_Open()
Application.ScreenUpdating = False
Workbooks.Open Filename:="C:\WB2.xlsm"
ActiveWindow.Visible = False
End Sub
This is what I have so far and what it does is hide BOTH workbooks. I want WB1 to remain on top and visible.
Thank you! Josh
Private Sub Workbook_Open()
Dim wb as workbook
Application.ScreenUpdating = False
set wb=Workbooks.Open(Filename:="C:\WB2.xlsm")
wb.Windows(1).Visible = False
End Sub
an important part would seem to be how to turn view back on again.
other post is the answer.. i just had to see it work before i commited it.
hope this is enough to explain it, may have been done in less space. thanks.
i would have to agree to post a couple words describing the key working line. i am just novice at vb & have to say that 99% of posts require some research to get a needed variable in there. i believe so enough to add some expletives as hours by many can be spent, trying to find the dang answer, but will refrain : ).
the consequence is: everyone on the planet has to spend 2 to infinite hours.
(thanks for having code, to put code in a box, needs some tweaking for lines to include / space lines interfere?).
what i found: changing out may not work: .Visible and .Hidden
- have no idea what the 1 in windows(1) is for.
sub TEST1() 'in a module
'if want to happen when you open a wb, place in: "ThisWorkbook" module as:
'Private Sub Workbook_Open()
Dim wb As Workbook
'Set wb = Workbooks("WB2.xlsm") 'YES
'Set wb = Workbooks(Filename:="C:\WB2.xlsm") 'untried should work for path eg
'Set wb = Workbooks.Open(Filename:="C:\WB2.xlsm") 'original, with a command added: open
Application.ScreenUpdating = False
If 0 = 0 Then 'set to: if 0 = 1 to skip test
If wb.WINDOWS(1).Visible = False Then 'TOGGLES: press F5 or run macro button
wb.WINDOWS(1).Visible = True
MsgBox "Workbook is NOT Hidden" & Space(10), vbQuestion 'a good test method
Else
wb.WINDOWS(1).Visible = False '<< line to use, to hide wb on open
MsgBox "Workbook is Hidden" & Space(10), vbQuestion 'a good test method
End If
Else
wb.WINDOWS(1).Visible = False '<< line to use, to hide wb on open
end if
End Sub
This seems to be an old post but I thought I'd add in my version of the answer,as I was working on something similar.
Set m_WB = Workbooks("WB2FilePath\WB2.xlsm")
Windows(m_WB.Name).Visible = False
'Do work
'Set WorkBook to visible
Windows(m_WB.Name).Visible = True
Keep in mind, it would be good practice to set WB2 to visible once completed. This would avoid any memory being eaten up in the background without you knowing!
Cheers.