I often find that with pivot tables i end up with tons of worksheets from new sheets being created via pivot table drill downs. I thought a great macro would be to display a small userform in the upper right corner of all new sheets created from pivot tables allowing the user to easily delete the sheet and return to the pivot table. However, I cant figure out how to trigger the macro. Any ideas? Basically the macro would need to be automatically triggered anytime a new sheet is created from drilling down in a pivot table.
With out getting too complex, you are probably looking for something like this...
Under "ThisWorkbook" add the following code:
EDITED
This was working for me:
Private Sub Workbook_NewSheet(ByVal Sh As Object)
If Detect_Pivot(Sh) Then
Sh.Activate
frmSheetOptions.Show
End If
End Sub
Function Detect_Pivot(wsNew As Worksheet) As Boolean
Dim wb As Workbook
Dim pvt As PivotTable
Dim bNew As Boolean
Dim i As Integer
i = 0
bNew = True
Set wb = ThisWorkbook
For Each pvt In wsNew.PivotTables
i = i + 1
s = Split(pvt.SourceData, "!")
sName = s(0)
If wsNew.Name = sName Then
bNew = False
Exit For
End If
Next
If i > 0 Then Detect_Pivot = bNew
End Function
Inside the frm you want to display:
Private Sub cmdHide_Click()
frmSheetOptions.Hide
End Sub
Private Sub cmdDelete_Click()
ActiveSheet.Delete
frmSheetOptions.Hide
End Sub
Private Sub UserForm_Initialize()
frmSheetOptions.lblSheetName = ActiveSheet.Name
End Sub
To start an event whenever a new sheet is created, you can use the following example (an answer from Tim Williams). However, I'm not sure how to tell if the new sheet was created from a pivot table. The drill-down might be an event triggered by the pivot table, in which case, maybe you can apply this method to find it. It would take some investigation.
Private WithEvents app As Excel.Application
Sub Init()
Set app = Application 'start capturing events
End Sub
Private Sub app_NewWorkbook(ByVal Wb As Workbook)
Debug.Print "New"
End Sub
Private Sub app_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
Debug.Print "Before close: " & Wb.Name
End Sub
Private Sub app_WorkbookOpen(ByVal Wb As Workbook)
Debug.Print "Open: " & Wb.Name
End Sub
Related
I'm attempting to have a user select a file, then through a UserForm select the worksheet they want to copy from.
The issue is on this line: ReportWbk.Sheets(ws).Cells.Copy Destination:=TargetWbk.Sheets("Test Import").Cells(1, 1)
I have confirmed that the file, worksheet selection, and copy function all work as expected. I get a run-time error '9': Subscript out of range. which to me means that the code is looking for the destination sheet in the workbook that the user selected (the 'target' file). Since it doesn't exist there, it errors out.
I'm wondering how to fix this dynamically so that the code will work without naming the workbook specifically. I might run this with other workbooks open and in different workbooks.
I verified that workbooks("WorkbookName").Sheets("Test Import").Cells(1, 1) works as I want but I'm looking to make the "WorkbookName" part dynamic / automatically select the workbook that the macro was run from.
The main code is directly below and the userform code is below that in case that's part of the issue:
Private Sub GetRange()
Dim ReportWbk As Workbook 'workbook with report data
Dim Report As Integer 'name of file with report data
Dim FD As FileDialog
Dim TargetWbk As Workbook 'this workbook
Dim ws As Variant
Set TargetWbk = ThisWorkbook
Set FD = Application.FileDialog(msoFileDialogFilePicker)
Report = FD.Show
'cancel pressed
If Report <> -1 Then Exit Sub
'open selected workbook
Set ReportWbk = Workbooks.Open(FD.SelectedItems(1))
ws = SelectSheet.Selection(ReportWbk)
'cancel pressed
If ws = vbCancel Then GoTo exitsub
ReportWbk.Sheets(ws).Cells.Copy Destination:=TargetWbk.Sheets("Test Import").Cells(1, 1)
exitsub:
ReportWbk.Close False
'changes the color of the destination worksheet tab
Worksheets("Test Import").Tab.Color = RGB(25, 25, 25)
End Sub
UserForm Code:
Private Sub cmdOK_Click()
Me.Tag = Me.ListBox1.Value
Me.Hide
End Sub
Private Sub cmdCancel_Click()
Me.Tag = vbCancel
Me.Hide
End Sub
Function Selection(ByVal wb As Object) As Variant
Dim ws As Worksheet
For Each ws In wb.Worksheets
'add all sheets to listbox
Me.ListBox1.AddItem ws.Name
Next ws
'default - select first sheet in list
With Me.ListBox1
.ListIndex = 0
.MultiSelect = fmMultiSelectSingle
End With
Me.Show
'return result
Selection = Me.Tag
Unload Me
End Function
Private Sub UserForm_Initialize()
Me.Caption = "Select Sheet"
Me.CmdOK.Caption = "OK"
Me.cmdCancel.Caption = "Cancel"
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
'disable X closing form
Cancel = CBool(CloseMode = 0)
If Cancel = -1 Then Me.Tag = vbCancel: Me.Hide
End Sub
I know this is a simple thing but I've been away from VBA for a few years and rusty as heck. Many thanks in advance!
I have a userform with two controls. One combobox called ComboBox1 and an image called image1. I am trying to show a chart on a sheet to this image1 during ComboBox1 change event as below
Private Sub ComboBox1_Change()
Call UpdateChart
End Sub
Private Sub UpdateChart()
Dim sTempFile As String
Dim oChart As Chart
sTempFile = Environ("temp") & "\temp.gif"
Set oChart = Worksheets(UserForm1.ComboBox1.Value).ChartObjects("Chart 1").Chart
oChart.Export Filename:=sTempFile, FilterName:="GIF"
UserForm1.Image1.Picture = LoadPicture(sTempFile)
Kill sTempFile
End Sub
This is working perfectly fine. But once I clicked on the image, ComboBox1 change event is not working anymore. In other words, the chart is not changing according to combobox change if I clicked on the image. Does anyone know why?
PS: I took the above code from some other site and modified to suit my needs.
Edit 1
ComboBox1 populating as below during Userform initialization.
Private Sub UserForm_Initialize()
With UserForm1.ComboBox1
Dim ws As Worksheet
For Each ws In Worksheets
Select Case ws.Name
Case "MainPage", "Raw Data"
Case Else
.AddItem ws.Name
End Select
Next ws
End With
End Sub
ComboBox1 values are the sheet names
Try this - adding Me.Repaint fixed it for me
Private Sub UpdateChart()
Dim sTempFile As String
Dim oChart As Chart
sTempFile = Environ("temp") & "\temp.gif"
Set oChart = Worksheets(Me.ComboBox1.Value).ChartObjects("Chart 1").Chart
oChart.Export Filename:=sTempFile, FilterName:="GIF"
Me.Image1.Picture = LoadPicture(sTempFile)
Me.Repaint '<<<<<
Kill sTempFile
End Sub
FYI within a form's code it's better to use Me instead of (eg) UserForm1 - that way you can rename the form if you need without having to edit all the code.
I have a spreadsheet and I created a sub function (please see below) to make a call to update specific pivot tables. One particular pivot table ("DataModel") has text as values, so, when I created that pivot table I added it to the data model then did a "Add Measure" and created a line code =CONCATENATEX(DataModel,[DataValue],”, “) so I can pivot on the text value.
Anyway, when I run the code below, it updates all the pivot tables I want except for the one made from the data model. Does anyone know how I can use Excel VBA to automate the pivot table made from the data model?
---- Call to module ---
Call RefreshPivots("DataModel")
---- Excel VBA Code ---
Public Sub RefreshPivots(ByVal sheetName As String)
Dim sheetExists As Boolean
sheetExists = False
For Each ws In Worksheets
If ws.Name = sheetName Then sheetExists = True
Next ws
If sheetExists Then
Sheets(sheetName).Visible = True
Sheets(sheetName).Activate
For Each pivotTab In ActiveSheet.PivotTables
pivotTab.PivotCache.Refresh
Next pivotTab
End If
End Sub
Use the RefreshTable method of the PivotTable object instead.
By the way, the pivot table worksheet does not need to be visible in order to refresh the pivot table.
Also, it's considered good programming practice to follow the 'Dedication of Duty' principle. It basically says that a function should be dedicated to do one thing, and nothing else.
So, for example, RefreshPivots should only refresh the pivot tables. Checking whether the worksheet exists should be done before calling your function to update the pivot tables.
I have re-written your code accordingly...
Option Explicit
Sub test()
Dim worksheetName As String
worksheetName = "DataModel"
If Not WorksheetExists(worksheetName) Then
MsgBox "'" & worksheetName & "' not found!", vbExclamation
Exit Sub
End If
RefreshWorksheetPivots worksheetName
'make worksheet visible, if so desired
End Sub
Public Function WorksheetExists(ByVal worksheetName As String) As Boolean
Dim ws As Worksheet
For Each ws In Worksheets
If UCase(ws.Name) = UCase(worksheetName) Then 'case-insensitive comparison
WorksheetExists = True
Exit Function
End If
Next ws
WorksheetExists = False
End Function
Public Sub RefreshWorksheetPivots(ByVal worksheetName As String)
Dim pt As PivotTable
For Each pt In Worksheets(worksheetName).PivotTables
pt.RefreshTable
Next pt
End Sub
I am trying to clear Print Area And Autofilter when excel opens:
Am total novice in Excel vba so Assmebled the followingcode from googling around
This code I have put in ThisWorkbook of Personal.xlsb in the XLstart folder and ofcourse the macro security has been set to enable all macros
Option Explicit
Public WithEvents xlApp As Excel.Application
Private Sub Workbook_Open()
Set xlApp = Application
End Sub
Private Sub Workbook_Close()
Set xlApp = Nothing
End Sub
Private Sub xlApp_WorkbookOpen(ByVal Wb As Workbook)
Application.EnableEvents = False
Call ClrPrntArea
Application.EnableEvents = True
End Sub
Here is the ClrPrntArea
Sub ClrPrntArea()
Dim ws As Object
For i = 1 To ActiveWorkbook.Worksheets.count
With Worksheets(i)
.PageSetup.PrintArea = ""
.PageSetup.FitToPagesWide = 1
End With
Next
End Sub
I will also be putting another macro call to module in personal xlsb for resetting the autofiter once above starts working..Any inputs will be really helpfull
in PERSONAL.xlsb, module ThisWorkbook, try the below; it's nearly the same code as in your request, with some modif's:
application object declared Private
event routine uses the local WB object variable handed over as parameter, instead of the ActiveWorkbook object
replaced For ... Next by For Each ... Next and working with local object variables
trap processing of PERSONAL.xlsb itself
Once you're happy remove all the MsgBox statements (and the Else), they are just to show what is happening and when.
Private WithEvents Excel_App As Excel.Application
' runs when Excel_App encounters a Workbook_Open() event
Private Sub Excel_App_WorkbookOpen(ByVal WB As Workbook)
Dim WS As Worksheet
If WB.Name <> "PERSONAL.xlsb" Then
MsgBox "PERSONAL.xlsb: Excel_App_WorkbookOpen(): " & WB.Name
For Each WS In WB.Worksheets
WS.PageSetup.PrintArea = ""
WS.PageSetup.FitToPagesWide = 1
If WS.FilterMode Then
WS.ShowAllData
End If
Next
Else
MsgBox "PERSONAL.xlsb: Excel_App_WorkbookOpen(): myself"
End If
End Sub
' runs when PERSONAL.xlsb is opened
' assign current Excel application to object variable Excel_App
Private Sub Workbook_Open()
MsgBox "PERSONAL.xlsb: Workbook_Open()"
Set Excel_App = Application
End Sub
Note:
When the event handler doesn't start when you double-click an Excel file (e.g. on your desktop), close all Excel applications and inspect the task manager for additional orphaned Excel processes which need to be killed. It happened to me while playing around with this code
I am creating a macro for my co-workers. They get a file daily and at the end of the day have to copy certain information to another workbook. The macro is to take care of the copying. I want to have a userform with a combobox popup that contains a list of current open workbooks so it knows which file to copy from. How do I set it up so that the selection made there sets a workbook variable with that selection?
What I'm trying to do is:
Sub CopySub()
Dim wb As Workbook
UserForm1.Show
Set wb = Workbooks(ComboBox1.Value)
....Rest of Copy and Paste Code
Below is the code for the userform:
Private Sub OK_Click()
'Take user selection and continue copy and paste code
UserForm1.Hide
End Sub
Private Sub Cancel_Click()
'Cancel everything, end all code
End
End Sub
Private Sub UserForm_Activate()
'Populate list box with names of open workbooks.
Dim wb As Workbook
For Each wb In Workbooks
ComboBox1.AddItem wb.Name
Next wb
End Sub
Your code isn't working now because CopySub doesn't know what\where ComboBox1 is. Also, if the user clicks the form's X to close it instead of pressing the cancel button or clicks the OK button without selecting a workbook, CopySub will keep running.
There are a couple different ways to get the form information. The simplest with your current code is to properly reference ComboBox1 and add a simple test.
Sub CopySub()
Dim wb As Workbook
UserForm1.Show
If UserForm1.ComboBox1.Value = "" Then
Exit Sub
End If
Set wb = Workbooks(UserForm1.ComboBox1.Value)
' rest of code goes here
End Sub
Something else to think about though is ways to make your macro quicker and easier to run. If the only thing on your form is a Combobox for selecting the workbook and users will be starting the macro from a keyboard-shortcut or from the menu, consider having the macro ask if they want to run the macro on the active workbook. Clicking Yes to a question is a lot faster than having to click a dropdown box, select the workbook, and then click OK.
Sub CopySub()
Dim wb As Workbook
If MsgBox("Do you want to run the macro on '" & ActiveWorkbook.Name & "'?", vbQuestion + vbYesNo) = vbYes Then
Set wb = ActiveWorkbook
Else
UserForm1.Show
If UserForm1.ComboBox1.Value = "" Then
Exit Sub
End If
Set wb = Workbooks(UserForm1.ComboBox1.Value)
End If
' rest of code goes here
End Sub
After further searching I found the answer, and its the same as what mischab points out, I didn't create a global variable so there was no way for my userform to communicate with the subroutine. I solved this by declaring a variable with scope for the whole workbook as such:
Public wb1 As String
Sub CopySub()
Dim wbCAR As Workbook
UserForm1.Show
Set wbCAR = Workbooks(wb1)
....Rest of code
and by setting the userform code to such:
Private Sub OK_Click()
wb1 = ComboBox1.Value
UserForm1.Hide
End Sub
Private Sub Cancel_Click()
Unload Me
End
End Sub
Private Sub UserForm_Activate()
'Populate list box with names of open workbooks.
Dim wb As Workbook
For Each wb In Workbooks
ComboBox1.AddItem wb.Name
Next wb
End Sub