Excel VBA: unable to disable DisplayAlert during drag+drop? - vba

I'm trying to capture a specific drag and drop event in VBA, and would like to disable the popup "There's already data here. Do you want to replace it?" during this event.
I have the basic event of a drag+drop from cell [D1] to cell [E1] captured, but for some reason I'm unable to disable the popup. Does anyone know why?
Thanks so much.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target(1, 1), [D1]) Is Nothing Then
MsgBox "selected " & Target.Address & " - " & Target(1, 1).Value
Application.DisplayAlerts = False
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target(1, 1), [E1]) Is Nothing Then
MsgBox "changed " & Target.Address & " - " & Target(1, 1).Value
End If
End Sub

Try this - it works on my 2013 Excel:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target(1, 1), [E1]) Is Nothing Then
MsgBox "changed " & Target.Address & " - " & Target(1, 1).Value
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target(1, 1), [D1]) Is Nothing Then
MsgBox "selected " & Target.Address & " - " & Target(1, 1).Value
Application.AlertBeforeOverwriting = False
Else
Application.AlertBeforeOverwriting = True
End If
End Sub
This uses the SelectionChange event to catch the user selecting D1 and disables the alert using Application.AlertBeforeOverwriting. Any other selection ensures it's enabled. Dragging the value causes another SelectionChange which now re-enables the alert for any other overwriting.
Also, you ought to use events to trap user clicking in D1 and then changing to another sheet or closing this one as the alerts could remain disabled.

Why did you put
Application.DisplayAlerts = False
after the drag and drop code? Move it before it.

Related

visual basic procedure too large error & ambiguous name detected worksheet_change

I have a large VBA macro which consists of one large Private Sub Worksheet_Change(ByVal Target As Range).
It first gives me the procedure too large error as it is really big.
When I tried to break it into 3 Private Sub Worksheet_Change(ByVal Target As Range).
this error shows up:
ambiguous name detected worksheet_change
any clues I can work around these 2 errors?
thanks in advance
here are my codes, the actual codes have tonnes of conditions and text check for each target address
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = [rng_opt1].Address Then
If [rng_opt1] = "x" Then
If [rng1_1] = "z" then
[rng1_1] = " "
End if
End If
End if
End sub
thanks to #urdearboy, I got it solved, my final codes is like this (much simplified version). it's tricky and took me a while as my target has defined name
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
Application.EnableEvents = False
If Target.Address = [rng_opt1].Address Then
Call Opt1(Target)
ElseIf Target.Address = [rng1_1].Address Then
Call Opt11(Target)
End if
LetsContinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
Sub Opt1(Target As Range)
If Target.Address = [rng_opt1].Address Then
If [rng_opt1] = "x" Or [rng_opt1] = "y" Then
If [rng1_1] = "z"
[rng1_1] = " "
End If
End if
End if
End Sub
Sub Opt11(Target As Range)
If Target.Address = [rng1_1].Address Then
If [rng1_1] = " " Then
If [rng1_2] = " " And [rng1_3] = " " And [rng1_4] = " " Then
[rng1_1] = "y"
[rng1_2] = "x"
End If
End If
End if
End sub
You can only have one WorkSheet_Change event on a worksheet which is why you are getting the Ambiguous Name Detected error.
If your code is too long, try to create your actions in a Sub and then call those subs given certain criteria. This way, you can limit your WorkSheet_Change code to strictly evaluate the Target.
In you WorkSheet_Change code you can have something like:
If Target.Value = “x” Then
Call SubX
ElseIF Target.Value = “y” Then
Call SubY
ElseIF Target.Value = “z” Then
Call SubZ
End IF
SubX ()
‘Do Something
End Sub
SubY ()
‘Do Something
End Sub
SubZ ()
‘Do Something
End Sub
Note:
You will need to disable events before you make any physical change to your sheet otherwise you will find yourself in a infinite loop and crash your instance of excel. Use the below method to avoid this issue:
Application.EnableEvents = False
‘Physical changes to worksheet
Application.EnableEvents = True

Checkboxes are running macro on selected cell row; Need them to run on linked cell row

I have a workbook in which specific line items are to be completed by a staff member and, once completed, they are to be checked off as complete. This triggers the row/range to the left of the checkbox to be selected, copied and pasted into the next worksheet on the first available row. The current row is then cleared from the first worksheet. Each worksheet has the checkboxes pre-filled in and pre-linked to cells. The issue I'm having is that when the checkbox is selected, the runall macro activates on the row that is currently selected instead of the row that the checkbox resides in and is linked to the cell in. So, for example, if the checkbox is in row M2 but the currently selected cell is B8, the macro will try to copy and paste row 8 instead of the intended row 2. As there is no undo with macros this results in a major headache. Any help would be greatly appreciated!
Sub RUNALLOPEN()
Dim response As VbMsgBoxResult
response = MsgBox("Are you sure you wish to clear this row and send to the Lab?", vbYesNo + vbExclamation, "Confirm Error Resolution")
If response = vbNo Then
Dim cbx As CheckBox
Set cbx = ActiveSheet.CheckBoxes(Application.Caller)
With cbx.TopLeftCell.Offset(0, -1)
cbx.Value = xlOff
End With
Exit Sub
End If
If response = vbYes Then
'rest of code
Call movedataOPEN2LAB
Call clearcellsOPEN
End If
End Sub
Sub movedataOPEN2LAB()
Dim cbx As CheckBox
'Application.Caller returns the name of the CheckBox that called this macro
Set cbx = ActiveSheet.CheckBoxes(Application.Caller)
'.TopLeftCell returns the cell address located at the top left corner of the cbx checkbox
With cbx.TopLeftCell.Offset(0, -1)
'Check the checkbox status (checked or unchecked)
If cbx.Value = xlOn Then
' Checkbox is Checked
Range(Cells(cbx.TopLeftCell.Offset(0, -1).Row, 1), Cells(cbx.TopLeftCell.Offset(0, -1).Row, 11)).Select
Selection.Copy
Sheets("Lab").Select
Range("A" & Rows.Count).End(xlUp).Offset(1).Select
ActiveSheet.Paste
ActiveSheet.Range("H" & Selection.Row).Formula = "=VLOOKUP(INDIRECT(""G"" & ROW()),'Source Data'!$D$1:$J$36,6,FALSE)"
ActiveSheet.Range("I" & Selection.Row).Value = "Lab"
Range("A2").Select
End If
End With
End Sub
Sub clearcellsOPEN()
On Error Resume Next
Worksheets("Open").Activate
Range(Cells(Selection.Row, 1), Cells(Selection.Row, 15)).Select
Selection.SpecialCells(xlCellTypeConstants).ClearContents
Range(Cells(Selection.Row, 1), Cells(Selection.Row, 1)).Select
End Sub
Thank you for your help! Here's what I came up with:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 13 Then
'If UCase(Target.Value) <> "X" Then
' Dim response As VbMsgBoxResult
' response = MsgBox("You must input 'x' in order to move this row.", vbOKOnly + vbExclamation, "ERROR")
' Exit Sub
' End If
If UCase(Target.Value) = "X" Then
response = MsgBox("Are you sure you wish to clear this row and send to the Lab?", vbYesNo + vbExclamation, "Confirm Error Resolution")
If response = vbNo Then
Target.Value = ""
Exit Sub
End If
If response = vbYes Then
'rest of code
Target.Cells.Offset(0, -12).Select
Range(Cells(ActiveCell.Row, 1), Cells(ActiveCell.Row, 11)).Select
Selection.Copy
With Sheets("Lab")
.Select
.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Select
End With
ActiveSheet.Paste
ActiveSheet.Range("H" & Selection.Row).Formula = "=VLOOKUP(INDIRECT(""G"" & ROW()),'Source Data'!$D$1:$J$36,6,FALSE)"
ActiveSheet.Range("I" & Selection.Row).Value = "Lab"
With Sheets("Open")
.Select
On Error Resume Next
Target.Cells.Offset(0, -12).Select
Range(Cells(ActiveCell.Row, 1), Cells(ActiveCell.Row, 14)).Select
Selection.SpecialCells(xlCellTypeConstants).ClearContents
End With
End If
End If
End If
End Sub
There are many other ways to accomplish that than checkboxes... A "cleaner" one that comes to my mind is to use the Change event of the worksheet.
get rid of the checkboxes
Set the title of Column M to "Completed = X"
Use this code in the Table Object:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 13 Then
If uCase(Target.Value) = "X" Then
'--Write your copy-code here maybe ignore/delete the x first
MsgBox "CopyThat!"
End If
End If
End Sub
just a suggestion...

With Block Variable not Set -- Error when workbook Opened

This macro is one that was not written by me, so I'm having trouble understanding the source of the error. I have a macro that's supposed to run on startup to adjust the ribbon to add a button, and another part to remove styles when you select that button. Currently, I get the message: Object variable or With block variable not set. When I select "Debug" it goes to the VBA screen and immediately gives me 3 more error pop-ups that say: Can't execute code in break mode.
The first part of this is the two subs that are to run on startup, which are:
Dim WithEvents app As Application
Private Sub App_WorkbookActivate(ByVal Wb As Workbook)
Module1.MyRibbon.Invalidate
End Sub
Private Sub Workbook_Open()
Set app = Application
End Sub
It highlights the Module1.MyRibbon.Invalidateas the problematic bit. Personally I don't see anything wrong with this per se, but perhaps the problem is in the Module 1? That code contains three subs, as follows:
Public MyRibbon As IRibbonUI
'Callback for customUI.onLoad
Sub CallbackOnLoad(Ribbon As IRibbonUI)
Set MyRibbon = Ribbon
End Sub
'Callback for customButton getLabel
Sub GetButtonLabel(control As IRibbonControl, ByRef returnedVal)
If ActiveWorkbook Is Nothing Then
returnedVal = "Remove Styles"
Else
returnedVal = "Remove Styles" & vbCr &
Format(ActiveWorkbook.Styles.Count, "#" & Application.International(xlThousandsSeparator) & "##0")
End If
End Sub
Sub RemoveTheStyles(control As IRibbonControl)
Dim s As Style, i As Long, c As Long
On Error Resume Next
If ActiveWorkbook.MultiUserEditing Then
If MsgBox("You cannot remove Styles in a Shared workbook." & vbCr & vbCr & _
"Do you want to unshare the workbook?", vbYesNo + vbInformation) = vbYes Then
ActiveWorkbook.ExclusiveAccess
If Err.Description = "Application-defined or object-defined error" Then
Exit Sub
End If
Else
Exit Sub
End If
End If
c = ActiveWorkbook.Styles.Count
Application.ScreenUpdating = False
For i = c To 1 Step -1
If i Mod 600 = 0 Then DoEvents
Set s = ActiveWorkbook.Styles(i)
Application.StatusBar = "Deleting " & c - i + 1 & " of " & c & " " & s.Name
If Not s.BuiltIn Then
s.Delete
If Err.Description = "You cannot use this command on a protected sheet. To use this command, you must first unprotect the sheet (Review tab, Changes group, Unprotect Sheet button). You may be prompted for a password." Then
MsgBox Err.Description & vbCr & "You have to unprotect all of the sheets in the workbook to remove styles.", vbExclamation, "Remove Styles AddIn"
Exit For
End If
End If
Next
Application.ScreenUpdating = True
Application.StatusBar = False
End Sub
I've never written any Activation or Ribbon-related macro, so I have no idea where the error could be. The addin works just find regardless of this message, as the button gets added and it functions as it should when the file isn't a blank file, but I get the error pop-up and the button doesn't get created right on new, blank files. How could I fix this?
I simply deleted:
Private Sub App_WorkbookActivate(ByVal Wb As Workbook)
Module1.MyRibbon.Invalidate
End Sub
No runtime errors on start of excel and no issues when using the script; counts fine and deletes fine. Windows 7, Excel 2010.

saving global variable from private sheet

I am trying to send out an email with updates when the the sheet are saved. To do this I am tracking changes and then trying to save these changes as a global string:
Public outString As String
Public Sub Worksheet_Change(ByVal Target As Range)
Dim colN, rowN As Integer
Dim changeHeading As String
Dim drawingNumber, partNumber As Integer
'Do nothing if more than one cell is changed or content deleted
If Target.Cells.Count > 1 Or IsEmpty(Target) Then Exit Sub
'Stop any possible runtime errors and halting code
On Error Resume Next
Application.EnableEvents = False
colN = Target.Column
rowN = Target.Row
changeHeading = ThisWorkbook.Sheets("List").Cells(1, colN).Value 'Header of the changed cell
partNumber = ThisWorkbook.Sheets("List").Cells(rowN, 2).Value 'Partnumber changed
drawingNumber = ThisWorkbook.Sheets("List").Cells(rowN, 4).Value 'Drawingnumber changed
outString = outString & vbNewLine _
& "PartNumber: " & partNumber & " DrawingNumber: " & drawingNumber _
& " " & changeHeading & ": " & Target & vbNewLine
'Turn events back on
Application.EnableEvents = True
'Allow run time errors again
On Error GoTo 0
End Sub
So this piece of code works nice except if I alter several column on the same row then each change will be presented on a new line instead of the same line, Maybe i have to use a dictionary with partnumber as key to avoid this.
Then in thisworkbook sheet i have the following code
Public outString
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call track
Call missingDrawings
Call updateText
End Sub
However now the outString variable is , so what did I do wrong when declaring the global variable outString?
You seem to have two variables called outString one in the worksheet and one in the workbook. You should only have one. If you leave the one in thisWorkbook (adding As String would be a good idea), then you can access it from the sheet by using ThisWorkbook.outString.

Display old and new values for a cell

I have a worksheet that logs changes that uses have made to cells. It goes as follows
Public OldVal As String
Public NewVal As String
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Or IsEmpty(Target) Then Exit Sub
OldVal = Target.Value
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim LDate As String
If Target.Cells.Count > 1 Then Exit Sub
NewVal = Target.Value
Sheets("corrections").Cells(Rows.Count, "A").End(xlUp)(2).Value = Now & "_Sheet " & ActiveSheet.Name & " Cell " & Target.Address(0, 0) & " was changed from '" & OldVal & "' to '" & NewVal & "'"
OldVal = ""
NewVal = ""
End Sub
The problem im having is that for some reason it will never display the previous value. it will output it only as Sheet FA Cell B5 was changed from '' to '12' even if say for example 10 was in the cell prviously.
I also was curious to know is there a way that you can have it so that this code is not running at all times. Id prefer to have a button you click and at that point it will initiate and start logging changes.
Thanks
I got your posted code to work with a very small change:
Public OldVal As String
Public NewVal As String
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Or IsEmpty(Target) Then Exit Sub
OldVal = Target.Value
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim LDate As String
If Target.Cells.Count > 1 Then Exit Sub
NewVal = Target.Value
Application.EnableEvents = False
Sheets("corrections").Cells(Rows.Count, "A").End(xlUp)(2).Value = Now & "_Sheet " & ActiveSheet.Name & " Cell " & Target.Address(0, 0) & " was changed from '" & OldVal & "' to '" & NewVal & "'"
Application.EnableEvents = True
OldVal = ""
NewVal = ""
End Sub
For your second question, start with:
Application.EnableEvents = False
Hook your button onto a macro like this:
Sub StartLogging()
Application.EnableEvents = True
End Sub
Your code is working fine for me. As for the enable/disable macro, you just need to insert this line before (/above) each IF (in both of your macros). Optional check for a more appropriate cell to store the Yes/No option (rather than "X1"):
If Sheets("corrections").Range("X1") <> "Yes" Then Exit Sub
' where you can change X1 for a more appropriate cell
To create the buttons just add the shapes/objects and assign the macros below:
Sub Enable_Logs()
Sheets("corrections").Range("X1").Value = "Yes"
End Sub
Sub Disable_Logs()
Sheets("corrections").Range("X1").Value = "No"
End Sub
Note! To add buttons with macro assigned to them: press Alt + N, +SH and select a shape. Then, right click on the shape > Assign Macro (and select the corresponding macros). Note! for the 1st time you run the macro, you should manually set the "Yes" value in cell X1.
Thr problem why my OldVal was not showing up was that it was being held in array. So when I told it to look at OldVal(1, 1) it works just as it should. Thanks for the help. The final working code is:
Public OldVal As String
Public NewVal As String
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Sheets("corrections").Range("G1") <> "Yes" Then Exit Sub
OldVal = Target.Value2
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Sheets("corrections").Range("G1") <> "Yes" Then Exit Sub
If Target.Cells.CountLarge > 1 Then Exit Sub
NewVal = Target.Value
Sheets("corrections").Cells(Rows.Count, "A").End(xlUp)(2).Value = Now & "_Sheet " & ActiveSheet.Name & " Cell " & Target.Address(0, 0) & " was changed from '" & OldVal(1, 1) & "' to '" & NewVal & "'"
OldVal = ""
NewVal = ""
End Sub