I've got 3 bits of code - trying to make them work together. On double click, if conditions met -- paste happens. If target cell is not empty, then message box with yes/no pops up. If user wants to overwrite the cell, code should do it. But, getting error in Sub Paste() on:
Target.Value = S
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Application.Intersect(Target, Range("k4:s8")) Is Nothing And Target.Value = "" Then
Cancel = True
Call Paste
'Else: MsgBox "If appropriate, clear the cell first. This is to prevent unintended data overwrite.", vbOKOnly + vbCritical, "Heads up" 'this works if MsgBox_YN_Paste won't
Else: Call MsgBox_YN_Paste
End If
End Sub
Sub Paste()
'Tools -> References -> Microsoft Forms 2.0 Object Library
'or you will get a "Compile error: user-defined type not defined"
Dim DataObj As New MSForms.DataObject
Dim S As String
DataObj.GetFromClipboard
S = DataObj.GetText
'Debug.Print S 'print code in the Intermediate box in the Macro editor
Target.Value = S
End Sub
Sub MsgBox_YN_Paste()
Dim AnswerYes As String
Dim AnswerNo As String
AnswerYes = MsgBox("Do you Wish to replace contents of this cell?", vbQuestion + vbYesNo, "Heads Up!")
If AnswerYes = vbYes Then
'Range("A1:A2").Copy Range("C1")
'ActiveCell.Clear
'or?
'Target.Value = ""
Call Paste
Else
'Range("A1:A2").Copy Range("E1")
End If
End Sub
I'd do it like this:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Application.Intersect(Target, Range("k4:s8")) Is Nothing Then Exit Sub
If Len(Target.Value) > 0 Then
If MsgBox("Do you Wish to replace contents of this cell?", _
vbQuestion + vbYesNo, "Heads Up!") <> vbYes Then Exit Sub
End If
Target.Value = ClipBoardText()
Cancel = True
End Sub
'Tools -> References -> Microsoft Forms 2.0 Object Library
Function ClipBoardText() As String
With New MSForms.DataObject
.GetFromClipboard
ClipBoardText = .GetText
End With
End Function
Target is not defined. Try
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Application.Intersect(Target, Range("k4:s8")) Is Nothing And Target.Value = "" Then
Cancel = True
Paste(Target)
'Else: MsgBox "If appropriate, clear the cell first. This is to prevent unintended data overwrite.", vbOKOnly + vbCritical, "Heads up" 'this works if MsgBox_YN_Paste won't
Else: Call MsgBox_YN_Paste
End If
End Sub
Sub Paste(ByVal Target As Range)
'Tools -> References -> Microsoft Forms 2.0 Object Library
'or you will get a "Compile error: user-defined type not defined"
Dim DataObj As New MSForms.DataObject
Dim S As String
DataObj.GetFromClipboard
S = DataObj.GetText
'Debug.Print S 'print code in the Intermediate box in the Macro editor
Target.Value = S
End Sub
I did some cleanup in the code, for clarity of understanding. Note that the return of the MsgBox is an integer numeric type and, therefore, the variable involved (AnswerYes, which I changed to AnswerYesOrNo (Integer), eliminating the other, AnswerNo, which is not used). The 'Debug.Print' actually prints on the VBE Immediate Windows.
Option Explicit
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Application.Intersect(Target, Range("k4:s8")) Is Nothing And Target.Value = "" Then
Cancel = True
Call PasteIt(Target)
'Else: MsgBox "If appropriate, clear the cell first. This is to prevent unintended data overwrite.", vbOKOnly + vbCritical, "Heads up" 'this works if MsgBox_YN_Paste won't
Else: Call MsgBox_YN_Paste(Target)
End If
End Sub
Sub PasteIt(ByRef Target As Excel.Range)
'Tools -> References -> Microsoft Forms 2.0 Object Library
'or you will get a "Compile error: user-defined type not defined"
Dim DataObj As New MSForms.DataObject
Dim S As String
DataObj.GetFromClipboard
S = DataObj.GetText
'Debug.Print S 'print code in the VBE Immediate Window
Target.Value = S
End Sub
Sub MsgBox_YN_Paste(ByRef Target As Excel.Range)
Dim AnswerYesOrNo As Integer
AnswerYesOrNo = MsgBox("Do you Wish to replace contents of this cell?", vbQuestion + vbYesNo, "Heads Up!")
If AnswerYesOrNo = vbYes Then
Call PasteIt(Target)
End If
End Sub
Related
I am trying to automatically lock cells after user has hit save button. The sheets is protected and contains some unlocked cells where user can input data.
Now I want those cells to get locked after user has clicked data. I have multiple sheets in the workbook but I want this to happen just for the first sheet.
Option Explicit
Dim Ws As Worksheet
Private bRangeEdited As Boolean
'Private WithEvents Ws As Worksheet
Private Sub Workbook_Open()
Set Ws = Range("A1:ZZ27").Parent
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim sMSG As String
sMSG = "Beim Speichern der Arbeitsmappe werden die Eingabezellen gesperrt. " & vbLf
sMSG = sMSG & "Möchten Sie fortfahren ?"
If Not bRangeEdited Then GoTo Xit
If Not Me.ReadOnly Then
With Range("A1:ZZ27")
If MsgBox(sMSG, vbExclamation + vbYesNo) = vbNo Then
Cancel = True
GoTo Xit
End If
Unpro_SheeTS
' .Parent.Unprotect "1234"
If .SpecialCells(xlCellTypeBlanks).Address <> .Address Then
.SpecialCells(xlCellTypeConstants).Locked = True
bRangeEdited = False
End If
Pro_SheeTS
' .Parent.Protect "1234"
End With
End If
Xit:
End Sub
Private Sub ws_Change(ByVal Target As Range)
Dim Ws As Worksheet
If Not Intersect(Range("A1:ZZ27"), Target) Is Nothing Then
bRangeEdited = True
End
End Sub
Sub Unpro_SheeTS()
For Each Ws In ActiveWorkbook.Worksheets
Ws.Unprotect ("1234")
Next
Exit Sub
End Sub
Sub Pro_SheeTS()
For Each Ws In ActiveWorkbook.Worksheets
Ws.Protect ("1234")
Next
Exit Sub
End Sub
My guess is that you should have a worksheet_change event function which keeps track of what cell is modified in a list/array/collection as you feel.
On the other hand your Workbook_BeforeSave event would go through the sheets you want and for each of them it would unprotect the sheet, lock the cells (worksheet.range(...).locked = true) then protects the sheet.
(You dont need to protect the workbook for your purpose, it is a different matter)
for you to have better results :
indent your code
refer to ranges from a worksheet explicitly
(worksheet.range)
consider migrating to another language ;)
Unfortunately Worksheet_change is not working for me. I am using a Sheet where the first column is NOW() function. If I click anywhere in the excel the time on this cell changes, but the Worksheet_Change just don't detect it.
A2 is using =NOW()
Anyone knows how to fix this issue? I have tried several different ways and no one works.
'Option Explicit
'Option Base 1
Dim xVal As Double
'Update by Extendoffice 2018/8/22
'Private Sub Worksheet_Calculate(ByVal Target As Range)
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Address = Range("$A$2").Address Then
'Rows(3).Insert Shift:=xlDown
'Range("$A$3").Value = Now
'Range("$B$3").Value = xVal
'Range("$C$3").Value = Range("$C$2").Value
'Else
If xVal <> Range("$B$2").Value Then
Debug.Print xVal & " <- xVal IF"
Debug.Print Range("B2").Text & "<- Text IF"
Rows(3).Insert Shift:=xlDown
Range("$A$3").Value = Now
Range("$B$3").Value = xVal
Range("$C$3").Value = Range("$C$2").Value
End If
End If
Application.EnableEvents = True
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
xVal = Range("$B$2").Value
End Sub
A Worksheet_Calculate Event Study
Google Drive
Cell A2 in worksheet Sheet1 contains the formula =B2.
Sheet1
Option Explicit
Private Sub Worksheet_Calculate()
WsCalc
End Sub
' Only to trigger the calculate event when different cell is selected.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
ThisWorkbook.Worksheets("Sheet1").Range("B2") = Int(Rnd() * 2 + 1)
End Sub
ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
WsCalc
End Sub
Module1
Option Explicit
Public lngSource As Long ' Current Source Value
Sub WsCalc()
Dim rngSource As Range ' Source Cell Range
' Create a reference to Source Cell Range (rngSource).
Set rngSource = ThisWorkbook.Worksheets("Sheet1").Range("A2")
' When the workbook opens, Current Source Valuec (lngSource) is equal to "".
' Therefore:
If lngSource = 0 Then
' Initialize Current Source Value (lngSource) i.e. write value
' of Source Cell Range (rngSource) to Current Source Value (lngSource).
lngSource = rngSource.Value
MsgBox "Monitoring started (lngSource=" & lngSource & ")."
Exit Sub
End If
' If you need disabling events, this is how you implement it. Not needed
' in this code.
' Application.EnableEvents = False
On Error GoTo ProgramError
' Check value of Source Cell Range (rngSource)
' against Current Source Value (lngSource).
If rngSource.Value <> lngSource Then
' The value has changed.
MsgBox "The value has changed from '" & lngSource & "' to '" _
& rngSource.Value & "'."
lngSource = rngSource.Value
Else
' The value hasn't changed (usually no code).
MsgBox "Value NOT changed, still '" & lngSource & "'"
End If
SafeExit:
' MsgBox "Enabling events before exiting."
' Application.EnableEvents = True
Exit Sub
ProgramError:
' Improve this error handling.
MsgBox "An unexpected error occurred."
On Error GoTo 0
GoTo SafeExit
End Sub
I would like to generate some code that allows an end user to select one of many charts from a sheet, after which I will do a bunch of manipulation based on that selection.
I am looking for something similar to the Application.Inputbox Type:=8 that allows for an object selection instead of a range selection.
Am I asking to much of humble old VBA??
It's a lot easier to select the chart first, then run code on the selected chart(s), than it is to pause the code and try to select the chart(s) from within the code.
But it can be done.
You need a userform, called F_ChartChooser with two buttons, btnCancel and btnContinue.
The code in the F_ChartChooser module:
Option Explicit
Private Sub btnCancel_Click()
CancelProcedure
End Sub
Private Sub btnContinue_Click()
ContinueProcedure
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
' so clicking red X doesn't crash
If CloseMode = 0 Then
Cancel = True
CancelProcedure
End If
End Sub
The code in the regular module consists of a main procedure which must get the chart(s) from the user. It has to call the userform modelessly so the user can select charts in the worksheet. This means the code continues running while the form is displayed, so the thing to do is end the sub when the userform is called.
Based on what happens with the userform, the code either continues with CancelProcedure or with ContinueProcedure. Here's the code:
Option Explicit
Dim mfrmChartChooser As F_ChartChooser
Sub Main()
' code here
' need to select chart(s) here
Application.Goto ActiveCell
Set mfrmChartChooser = New F_ChartChooser
mfrmChartChooser.Show vbModeless
End Sub
Sub CancelProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
MsgBox "User canceled.", vbExclamation
End Sub
Sub ContinueProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
If Not ActiveChart Is Nothing Then
' do something with active chart
' this demo is announcing that it was selected
MsgBox """" & ActiveChart.ChartTitle.Text & """ was selected.", vbExclamation
' end of demo code
ElseIf TypeName(Selection) = "DrawingObjects" Then
Dim sh As Shape
Dim vCharts As Variant
Dim nChart As Long
ReDim vCharts(0 To nChart)
For Each sh In Selection.ShapeRange
If sh.HasChart Then
' do something here with each chart
' this demo is building a list of selected charts
nChart = nChart + 1
ReDim Preserve vCharts(0 To nChart)
vCharts(nChart) = sh.Chart.ChartTitle.Text
' end of demo code
End If
Next
' this demo now is showing the list of selected charts
If nChart = 0 Then
MsgBox "No chart selected.", vbExclamation
Else
If nChart = 1 Then
MsgBox """" & vCharts(nChart) & """ was selected.", vbExclamation
Else
Dim sPrompt As String
sPrompt = nChart & " charts selected:" & vbNewLine & vbNewLine
Dim iChart As Long
For iChart = 1 To nChart
sPrompt = sPrompt & """" & vCharts(iChart) & """" & IIf(iChart < nChart, vbNewLine, "")
Next
MsgBox sPrompt, vbExclamation
End If
End If
' end of demo code
Else
' do nothing because no chart was selected
' this demo is announcing that nothing was selected
MsgBox "No chart selected.", vbExclamation
' end of demo code
End If
End Sub
The CancelProcedure and ContinueProcedure routines above have excess code in them just to help with the demo. In real code I would streamline them like this, probably not even bother to notify the user when nothing was selected (they know they canceled, right?), and just process the selected chart(s):
Sub CancelProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
End Sub
Sub ContinueProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
If Not ActiveChart Is Nothing Then
' do something with active chart
ProcessChart ActiveChart
ElseIf TypeName(Selection) = "DrawingObjects" Then
Dim sh As Shape
Dim vCharts As Variant
Dim nChart As Long
ReDim vCharts(0 To nChart)
For Each sh In Selection.ShapeRange
If sh.HasChart Then
' do something here with each chart
ProcessChart sh.Chart
Next
End Sub
I have a userform which I wish to pass a range to. I have tried a couple of different ways to do it but does not seem to work.
Here is the sub code:
Option Explicit
Sub Additional_Comments_Normal()
Dim MSG1 As Integer
Dim msg As String
Dim act As Range
On Error GoTo ErrHandler
'Calls userform
MSG1 = MsgBox("Would you like to add comments", vbYesNo, "Add comments")
If MSG1 = vbYes Then
With AddComments
On Error Resume Next
Set act = Application.InputBox(Prompt:="Please choose files you wish to add comments to", Type:=8)
If act Is Nothing Then
Exit Sub
End If
Application.ScreenUpdating = True
.Show
End With
Else
Exit Sub
End If
ErrHandler:
If Err.Number <> 0 Then
msg = "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & Err.Description
MsgBox msg, , "Error", Err.HelpFile, Err.HelpContext
End If
End Sub
And the userform code is here:
Public act As Range
Private Sub CommandButton1_Click()
Dim ctl As Control
Dim rng As Range
Dim MSG2 As Integer
Dim sfile As String
If act.Column > 1 Then
MsgBox ("Please choose File name from Column 1")
Exit Sub
End If
If act.Row < 4 Then
MsgBox ("Please choose a valid file")
Exit Sub
End If
If Me.TxtComment.Value = "" Then
MsgBox "Please add comments", vbExclamation, "Additional Comments"
Me.TxtComment.SetFocus
Exit Sub
End If
If Me.TxtName.Value = "" Then
MsgBox "Please add your name", vbExclamation, "Additional Comments"
Me.TxtName.SetFocus
Exit Sub
End If
MSG1 = MsgBox("Add Comments ?", vbYesNo, "Add comments")
If MSG1 = vbNo Then
End If
If MSG1 = vbYes Then
act.Offset(0, 16).Value = act.Offset(0, 16).Text & " " & Me.TxtComment.Value
act.Offset(0, 17).Value = act.Offset(0, 17).Text & " " & Me.TxtName.Value
For Each ctl In Me.Controls
If TypeName(ctl) = "TextBox" Then
ctl.Value = ""
End If
Next ctl
AddComments.Hide
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
End If
End Sub
Private Sub CommandButton2_Click()
End Sub
Private Sub CommandButton3_Click()
Unload Me
End Sub
Private Sub UserForm_Click()
End Sub
I then get an error about the act not being defined variable.
Can anyone shed some light on better process for this?
You have set Option Explicit at the top of your code. That means that all variables need to be defined (which is considered good programming practice). So, you have two options to resolve this:
(1) Remove the line Option Explicitfrom your code or
(2) define all of your variables using the Dim command. In this case you'd have to add Dim act as Range to your Sub CommandButton1_Click on the form.
If you want to pass a variable to another sub then you can do so calling that sub with that variable like so:
Call Additional_Comments_Normal(act)
and the sub header neeeds to change like so:
Sub Additional_Comments_Normal(ByVal act as Range)
'(your code)'
End Sub
If "passing a variable to another sub" is too much trouble then you can also save the range somewhere in your file like so:
SomeHiddenSheet.Range("A1").Value2 = act
and in the other sub you can initiate act again:
act = SomeHiddenSheet.Range("A1").Value2
I need users to be able to fill in one row to generate a P.O., and when the P.O. is generated the row below would be unhidden. The P.O. depends on column C, E and G to be filled in.
This code only unhides a row if one of the requirements are met. It also makes the workbook lag.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Row < 14 Or Target.Row > 5000 Or Target.Column < 3 Or Target.Column < 5 Or Target.Column <> 7 Then GoTo ExitMe
Rows(Target.Row + 1).Hidden = False
ExitMe:
Application.EnableEvents = True
End Sub
I need one row to be filled in at a time so the P.O. can be generated properly. If there is a better way please let me know.
This macro also conflicts with my macro for protecting changed cells when the worksheet is saved. This is the error that appears: Run-time error '1004': Unable to set hidden property of the Range class.
It is placed in ThisWorkbook
Option Explicit
Private bRangeEdited As Boolean
Private WithEvents ws As Worksheet
Private Sub Workbook_Open()
Set ws = Range("A14:Y3000").Parent
End Sub
Private 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
With Range("A14:Y3000")
If MsgBox(sMSG, vbExclamation + vbYesNo) = vbNo Then
Cancel = True
GoTo Xit
End If
.Parent.Unprotect "password"
If .SpecialCells(xlCellTypeBlanks).Address <> .Address Then
.SpecialCells(xlCellTypeConstants).Locked = True
bRangeEdited = False
End If
.Parent.Protect "password"
End With
End If
Xit:
End Sub
Private Sub ws_Change(ByVal Target As Range)
If Not Intersect(Range("A14:Y3000"), Target) Is Nothing Then
bRangeEdited = True
End If
End Sub