How can I have the VBA code cancel if the cancel button is pressed. I have...
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim ws As Worksheet
Set ws = Sheets("EDITS")
Dim tbl As ListObject
Set tbl = ws.ListObjects("Table1")
Dim newrow As ListRow
Set newrow = tbl.ListRows.Add
SavePrompt.Show
With newrow
.Range(1) = Now
.Range(2) = SavePrompt.TextBox1.Text
End With
End Sub
and added...
Private Sub CommandButton1_Click()
SavePrompt.Hide
End Sub
And,
Private Sub CommandButton2_Click()
Cancel = True
End Sub
to the Workbook Project Macro section.
But for some reason I'm not sure how to get the code to Cancel since the user realizes they don't want to save the changes they for some reason made to the Workbook. This SavePrompt help diagnose what was changed and when.
I thin your meaning is when user press cancel button , you want to clean list of row.If so just make like the following simple code.
Private Sub ListObject_Delete()
Dim List1 As Microsoft.Office.Tools.Excel.ListObject = _
Me.Controls.AddListObject(Me.Range("A1", "D4"), "List1")
If DialogResult.Yes = MessageBox.Show("Delete the ListObject?", _
"Test", MessageBoxButtons.YesNo) Then
List1.Delete()
End If
End Sub
Related
Is there any way to delete named ranges used in chart series when the chart is being deleted?
I use named ranges quite extensively in my daily work, also for charting. When I create charts I often name data ranges and THEN use them for chart series.
I am looking for a way to delete USED named ranges WHEN I delete the chart. I thought about chart "delete" event, but I cannot find any info about it (does it even exist???).
The second issue is how to determine which ranges have been used for chart series? Deleting the named ranges is easy, but how to actually determine, which ranges have been used in chart series?
All help is MUCH appreciated. Apologies but I cannot provide you with any code, as I have no idea how to set things up
Try the next code please. The USED named ranges cannot be extract directly. I used a trick to extract the ranges form SeriesCollection formula. Then compare them with names RefersToRange.Address and delete the matching name. It (now) returns a boolean value in case of match (only to see it in Immediate Window), but not necessary for your purpose. The code also delete the invalid names (having their reference lost).
Edited: I made some researches and I am afraid it is not possible to create a BeforeDelete event... It is an enumeration of events able to be created for a chart object, but this one is missing. I like to believe that I found a solution for your problem, respectively:
Create a class able to enable BeforeRightClick event. Name it CChartClass and write the next code:
Option Explicit
Public WithEvents ChartEvent As Chart
Private Sub ChartEvent_BeforeRightClick(Cancel As Boolean)
Dim msAnswer As VbMsgBoxResult
msAnswer = MsgBox("Do you like to delete the active chart and its involved Named ranges?" & vbCrLf & _
" If yes, please press ""Yes"" button!", vbYesNo, "Chart deletion confirmation")
If msAnswer <> vbYes Then Exit Sub
Debug.Print ActiveChart.Name, ActiveChart.Parent.Name
testDeleteNamesAndChart (ActiveChart.Parent.Name)
End Sub
Create another class able to deal with workbook and worksheet events, name it CAppEvent and copy the next code:
Option Explicit
Public WithEvents EventApp As Excel.Application
Private Sub EventApp_SheetActivate(ByVal Sh As Object)
Set_All_Charts
End Sub
Private Sub EventApp_SheetDeactivate(ByVal Sh As Object)
Reset_All_Charts
End Sub
Private Sub EventApp_WorkbookActivate(ByVal Wb As Workbook)
Set_All_Charts
End Sub
Private Sub EventApp_WorkbookDeactivate(ByVal Wb As Workbook)
Reset_All_Charts
End Sub
Put the next code in a standard module (need to create a classes array in order to start the event for all existing sheet embedded charts):
Option Explicit
Dim clsAppEvent As New CAppEvent
Dim clsChartEvent As New CChartClass
Dim clsChartEvents() As New CChartClass
Sub InitializeAppEvents()
Set clsAppEvent.EventApp = Application
Set_All_Charts
End Sub
Sub TerminateAppEvents()
Set clsAppEvent.EventApp = Nothing
Reset_All_Charts
End Sub
Sub Set_All_Charts()
If ActiveSheet.ChartObjects.Count > 0 Then
ReDim clsChartEvents(1 To ActiveSheet.ChartObjects.Count)
Dim chtObj As ChartObject, chtnum As Long
chtnum = 1
For Each chtObj In ActiveSheet.ChartObjects
Set clsChartEvents(chtnum).ChartEvent = chtObj.Chart
chtnum = chtnum + 1
Next
End If
End Sub
Sub Reset_All_Charts()
' Disable events for all charts
Dim chtnum As Long
On Error Resume Next
Set clsChartEvent.ChartEvent = Nothing
For chtnum = 1 To UBound(clsChartEvents)
Set clsChartEvents(chtnum).ChartEvent = Nothing
Next ' chtnum
On Error GoTo 0
End Sub
Sub testDeleteNamesAndChart(strChName As String)
Dim rng As Range, cht As Chart, sFormula As String
Dim i As Long, j As Long, arrF As Variant, nRng As Range
Set cht = ActiveSheet.ChartObjects(strChName).Chart
For j = 1 To cht.SeriesCollection.Count
sFormula = cht.SeriesCollection(j).Formula: Debug.Print sFormula
arrF = Split(sFormula, ",")
For i = 0 To UBound(arrF) - 1
If i = 0 Then
Set nRng = Range(Split((Split(sFormula, ",")(i)), "(")(1))
Else
Set nRng = Range(Split(sFormula, ",")(i)) '(1)
End If
Debug.Print nRng.Address, matchName(nRng.Address)
Next i
ActiveSheet.ChartObjects(strChName).Delete
End Sub
Private Function matchName(strN As String) As Boolean
Dim Nm As Name, strTemp As String
For Each Nm In ActiveWorkbook.Names
On Error Resume Next
strTemp = Nm.RefersToRange.Address
If Err.Number <> 0 Then
Err.Clear
Nm.Delete
Else
If strN = strTemp Then
Nm.Delete
matchName = True: Exit Function
End If
End If
On Error GoTo 0
Next
End Function
Use the next events code in the ThisWorkbook module:
Option Explicit
Private Sub Workbook_Open()
InitializeAppEvents
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
TerminateAppEvents
End Sub
Please confirm that it worked as you need
I have an excel file A with a macro and I have to retrive a cell´s adress in another excel file B by the user´s click on it.
The macro looks like this.
In the Class:
Public WithEvents appevent As Application
Private Sub appevent_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
ClickedCell = ActiveCell.Address
End Sub
In the Module
Sub ClickedCellSub()
Dim WbA As Variant, WbB As Variant
WbA = ThisWorkbook.Name
WbB = "B.xlsx"
MsgBox "Please double click on the Assembly SS 00 you want to compare"
Set myobject.appevent = Application
Workbooks(WbB).Sheets(1).Activate
Set myobject.appevent = Nothing
MsgBox ClickedCell
Workbooks(WbA).Activate
End Sub
The problem is, the macro doesn´t wait for the event DoubleClick on the other excel sheet and goes to the end.
How can I stop the macro until the event happens?
Many thanks in advance!
I would use event sinking, but not sure how you have approached it, but this is how i'd use.
In a class use the following :
Private WithEvents wb As Excel.Workbook
Private rng As Excel.Range
Public Event evtCellClicked(rngClicked As Excel.Range)
Public Event evtCellDoubleClicked(rngDoubleCliecked As Excel.Range)
Public Sub init(wbInput As Excel.Workbook)
Set wb = wbInput
End Sub
Public Property Get CellClicked() As Excel.Range
CellClicked = rng
End Property
Private Sub wb_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
Set rng = Target
RaiseEvent evtCellClicked(Target)
End Sub
Private Sub wb_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
Set rng = Target
RaiseEvent evtCellClicked(Target)
End Sub
I changed the code. In the module:
enter Option Explicit
Private mobjApplication As clsApplication
Global ClickedCell As String
Sub Vergleichen()
Dim StruktBer As Variant
StruktBer = Application.GetOpenFilename(, , "Strukturbericht öffnen") '####Name der Datei aus der Strukturberich
Set mobjApplication = New clsApplication
Workbooks.Open StruktBer
MsgBox "Bitte die Sachnummer der Strukturstufe '00' anklicken die man vergleichen möchte"
End Sub
Sub GOFURTHER
In the Class:
Option Explicit
Private WithEvents mobjApplication As Application
Private Sub Class_Initialize()
Set mobjApplication = Application
End Sub
Private Sub mobjApplication_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
ClickedCell = Target.Address
Set mobjApplication = Nothing
Call GOFURTHER
End Sub
The macro stops until the user clicks on the other excel table. Is there any way not to exit the first sub and to remain in the first one?
Why not simply using an InputBox ?
https://learn.microsoft.com/en-us/office/vba/api/excel.application.inputbox
In your prompt you ask the user to select a range in the appropriate document, and that's it.
I'd like to get an Excel macro running as soon as cell "A500"
becomes visible on the screen when scrolling down/up the worksheet.
I remember reading somewhere about an active-x or standard control
that has an "on scrolling into view" event, so this could be done
by placing a control directly on the worksheet near the desired cell.
Finding this control currently eludes me.
A better way of course would be a cell formula, subclassing still is
a bad idea in the long run i guess :)
Sub temp_01() 'Excel Vba
'user scrolls down from cell "A1"
'when the user reaches cell "A500" show the following message:
MsgBox "Chapter 2"
End Sub
As mentioned above, with the help of the Onupdate event, (catches the mousewheel, not clicking on the scrollbars) (Change Sheetname(s) and Range(s) to yours)
In Class called ClsMonitorOnupdate:
Option Explicit
Private WithEvents objCommandBars As Office.CommandBars
Private rMonitor As Range
Private scrol As Boolean
Public Property Set Range(ByRef r As Range): Set rMonitor = r: End Property
Public Property Get Range() As Range: Set Range = rMonitor: End Property
Private Sub Class_Initialize()
Set objCommandBars = Application.CommandBars
End Sub
Private Sub Class_Terminate()
Set objCommandBars = Nothing
End Sub
Private Sub objCommandBars_OnUpdate()
Dim myrng As Range
If ActiveWorkbook.Name <> ThisWorkbook.Name Then Exit Sub
If ActiveSheet.Name <> rMonitor.Parent.Name Then Exit Sub
If TypeName(Selection) <> "Range" Then Exit Sub
If Intersect(Selection, rMonitor) Is Nothing Then Exit Sub
Set myrng = Application.Intersect(ActiveWindow.VisibleRange, ActiveSheet.Range("a500"))
If Not myrng Is Nothing And Not scrol Then scrol = True: MsgBox "chapter"
If myrng Is Nothing And scrol Then scrol = False
End Sub
In the ThisWorkbook section:
Option Explicit
Private sRanges As String
Private cMonitor As ClsMonitorOnupdate
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Set cMonitor = Nothing
End Sub
Private Sub Workbook_Open()
Zetaan ActiveSheet
End Sub
Sub Zetuit()
Set cMonitor = Nothing
End Sub
Sub Zetaan(sht As Worksheet)
Select Case sht.Name
Case "Sheet1": sRanges = "A1:ZZ1000"
Case "Other Sheet": sRanges = "A1:ZZ1000"
Case Else: Exit Sub
End Select
Set cMonitor = New ClsMonitorOnupdate
Set cMonitor.Range = Sheets(sht.Name).Range(sRanges)
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Zetaan Sh
End Sub
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
Set cMonitor = Nothing
End Sub
I have a macro in the 'ThisWorkbook' module set to run 'BeforeSave'. I have two other macros that I also need to run 'BeforeSave'. Can I add additional macros to this module?
I have created my macros in the 'standard' module section, and they work with the selection of the 'Run' button. I have attempted to add the 'Macro/Module names' to the bottom of my 'BeforeSave' macro which has done nothing but give me errors.
Option Explicit
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call HideRows
Call DivAdminApproval
Call ProjNumbrReq
End Sub
Public Sub HideRows()
'When a row begins with X in Travel Expense Codes worksheet, hide the row
Const beginRow As Long = 3
Const endRow As Long = 38
Const chkCol As Long = 14
Dim rowCnt As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Travel Expense Codes")
For rowCnt = endRow To beginRow Step -1
With ws.Cells(rowCnt, chkCol)
.EntireRow.Hidden = (.Value = "X")
End With
Next rowCnt
End Sub
Public Sub ProjNumbrReq()
'Call ProjNumbrReq
With Worksheets("Travel Expense Voucher")
For Each myCell In .Range("U15:U45")
If myCell.Value > 0 And .Cells(myCell.row, "N") = "" Then
MsgBox "Project Number must be provided on each line where reimbursement is being claimed.", vbCritical, "Important:"
Cancel = True
Exit Sub
End If
Next myCell
End Sub
Public Sub DivAdminApproval()
'Call DivAdminApproval
With Worksheets("Travel Expense Voucher")
If Worksheets("Travel Expense Voucher").Cells("F5") = 2 Then
For Each myCell In .Range("O15:O45")
If myCell.Value = 0.58 Then
MsgBox "You have selected reimbursement at the 'HIGH' mileage rate ($.58/mile). To receive reimbursement at this rate, Division Administrator Approval is Required.", vbCritical, "Important:"
Exit Sub
End If
Next myCell
End Sub
The ProjNumbrReq and DivAdminApproval macros have been listed before End Sub, hoping that would call them to work. However, they are not running.
something along these lines, if your macros are not in the 'ThisWorkbook' module then make sure they are defined public (instead of private):
Option Explicit
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call sMacro1
Call sMacro2
Call sMacro3
End Sub
Private Sub sMacro1()
'do something
End Sub
Private Sub sMacro2()
'do something
End Sub
Private Sub sMacro3()
'do something
End Sub
Ultimately, I would like to run a macro after anyone refreshes the workbook, specifically using the Refresh button under the Data tab in Excel.
For the time being, I would be satisfied just getting the BeforeRefresh or AfterRefresh QueryTable events to fire upon pressing the Refresh button.
In addition to the documentation on the Microsoft Dev Center website, the relevant posts I have read include:
Excel VBA - QueryTable AfterRefresh function not being called after Refresh completes
VBA For Excel AfterRefresh Event
There are other posts but I lack the reputation to post them.
Here is what I have:
Under Class Modules (qtclass)
Option Explicit
Private WithEvents qt As Excel.QueryTable
Private Sub qt_AfterRefresh(ByVal Success As Boolean)
MsgBox "qt_AfterRefresh called sucessfully."
If Success = True Then
Call Module2.SlicePivTbl
MsgBox "If called succesfully."
End If
End Sub
Private Sub qt_BeforeRefresh(Cancel As Boolean)
MsgBox "qt_BeforeRefresh called."
End Sub
Under the ThisWorkbook module
Private Sub Workbook_Open()
Dim qtevent As qtclass
Dim qt As QueryTable
Set qt = ThisWorkbook.Worksheets("Data-Fund").ListObjects(1).QueryTable
Set qtevent = New qtclass
End Sub
I have tried variations of the second code block under specific worksheets as well, but have yet to find anything that works. Do I need to somehow dim the QueryTable in question in the Worksheet module?
You haven't actually connected the querytable to the class instance.
Revised qtclass
Option Explicit
Private WithEvents qt As Excel.QueryTable
Public Property Set HookedTable(q As Excel.QueryTable)
Set qt = q
End Property
Private Sub qt_AfterRefresh(ByVal Success As Boolean)
MsgBox "qt_AfterRefresh called sucessfully."
If Success = True Then
Call Module2.SlicePivTbl
MsgBox "If called succesfully."
End If
End Sub
Private Sub qt_BeforeRefresh(Cancel As Boolean)
MsgBox "qt_BeforeRefresh called."
End Sub
New ThisWorkbook code:
Dim qtevent As qtclass
Private Sub Workbook_Open()
Set qtevent = New qtclass
Set qtevent.HookedTable = ThisWorkbook.Worksheets("Data-Fund").ListObjects(1).QueryTable
End Sub
Note that this is quite closely coupled. It would be more re-usable if you were to raise events in the class and declare your qtevent variable WithEvents.
Source: https://www.excelandaccess.com/create-beforeafter-query-update-events/
Class:
Note: Class Name = clsQuery
Option Explicit
Public WithEvents MyQuery As QueryTable
Private Sub MyQuery_AfterRefresh(ByVal Success As Boolean)
If Success Then
Debug.Print "After ReFresh"
End If
End Sub
Private Sub MyQuery_BeforeRefresh(Cancel As Boolean)
Debug.Print "Before ReFresh"
End Sub
Module:
Option Explicit
Dim colQueries As New Collection
Sub InitializeQueries()
Dim clsQ As clsQuery
Dim WS As Worksheet
Dim QT As QueryTable
For Each WS In ThisWorkbook.Worksheets
For Each QT In WS.QueryTables
Set clsQ = New clsQuery
Set clsQ.MyQuery = QT
colQueries.Add clsQ
Next QT
Next WS
End Sub
ThisWorkbook.Event:
Private Sub Workbook_Open()
Call InitializeQueries
End Sub