Excel VBA: Trigger macro on cut/paste/delete/insert events - vba

I have a conditional formatting rule defined as macro, which deletes the old rules and replaces them with updates ones:
Sub setCondFormat()
Set Table = ActiveSheet.ListObjects("Rules")
Table.Range.FormatConditions.Delete
Set Attribute = Table.ListColumns("Attribute")
With Attribute.DataBodyRange.FormatConditions _
.Add(xlExpression, xlEqual, "=ISEMPTY(A2)")
With .Interior
.ColorIndex = 0
End With
End With
End Sub
The conditional formatting in Excel needs to be updated. Otherwise the
cell ranges in the rules get fragmented.
Let's say you have two rules:
Make $A$1:$A$30 red
Make $B$1:$B$30 blue Now select A10:B10 and copy/paste that to A20:B20.
What Excel will do is to delete the conditional formatting.
For A20:B20 from the rules that applied to those cells and add new
rules that have the formatting for A20:B20. You end up with four
rules:
Make =$A$20 red
Make =$B$20 blue
Make =$A$1:$A$19,$A$21:$A$30 red
Make =$B$1:$B$19,$B$21:$B$30 blue
This happens, when the table structure gets changed through cut/paste/delete/insert events.
How to trigger the above VBA macro on cut/paste/delete/insert events?

You could use a shortcut for your macro
VBA event trigger on copy?
If you don't want to go this way you'll need to use the Windows API:
Is there any event that fires when keys are pressed when editing a cell?

The solution I found is create a new Sheet with the content of your table when you open the Workbook. First you need to create a Module with the Public Variables.
Public OldRange As Range
Public NewRange As Range
Public Table As ListObject
Then, use the event Open of your Workbook.
Private Sub Workbook_Open()
Dim sh As Worksheet
Dim address As String
For Each sh In Worksheets
If sh.Name = "DATA" Then
Worksheets("DATA").Activate
ActiveSheet.Delete
End If
Next
ActiveWorkbook.Sheets.Add After:=Worksheets(Worksheets.Count)
ActiveSheet.Name = "DATA"
Set sh = ActiveWorkbook.Sheets("Plan1")
sh.Activate
Set Table = ActiveSheet.ListObjects("Rules")
Set OldRange = Table.Range
address = Table.Range.address
Table.Range.Copy
Set sh = ActiveWorkbook.Sheets("DATA")
sh.Activate
Range(address).PasteSpecial (xlPasteAll)
End Sub
And then, use the event Worksheet_Change to verify the content of your original table with the earlier saved table.
Private Sub Worksheet_Change(ByVal Target As Range)
Set Table = ActiveSheet.ListObjects("Rules")
If Intersect(Target, Table.Range) Is Nothing Then Exit Sub 'this will guarantee that the change made in your sheet is in your desired table
Set NewRange = Table.Range
Dim rng As Range
Dim rngaddr As String
Dim TableChanged As Boolean
TableChanged = False
For Each rng In NewRange
rngaddr = rng.address
If rng.Value <> ActiveWorkbook.Sheets("DATA").Range(rngaddr).Value Then
'do something
TableChanged = True
End If
Next
End Sub
Remember: you need to save the content of your table every time you changed it.

Related

Update a cell value with Macro VBA

I want to be able to create a button function macro to update stock values based upon one cell that contains the new value and another that identifies the physical cell address.
Very new to VBA and only have a basic understanding
I have tried the below:
Private Sub CommandButton1_Click()
Dim rng As Range
rng = Range(Range("m2").Value2).Select
Set rng.Value = Range("k2").Value
End Sub
So what I want to happen is that when clicking the command button the value in the cell determined by the cell address in M2 is updated to the value in cell K2.
Please help a complete noob trying to learn.
You Set Objects not values:
Private Sub CommandButton1_Click()
Dim rng As Range
Set rng = ActiveSheet.Range("m2")
rng.Value = ActiveSheet.Range("k2").Value
End Sub

Getting data from one sheet to another on the user input

I have an Excel workbook, and in that workbook I have a number of sheets.
One is called main and the other sheet data which holds all the information. The data sheet has people's name and assigned numbers in the same cell.
e.g.: A2 would have: 123 Chris Smith
What I am trying to achieve is for a user to start typing the number anywhere in the main sheet that it scans the data sheet and completes the rest of the data.
So a user would type 123 and the macro would then fill out the rest 123 Chris Smith.
As already mentioned you can use the Worksheet_Change() Event. To Show you how it works, put this code in the main sheet in the editor. Now type any number that matches your data list and click out of the cell, it will be now completed.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim sht As Worksheet
Dim rng As Range
Dim val As Variant: val = Target.Value
Set sht = Worksheets("Data")
If Not IsError(val) And Not IsArray(val) Then
If val <> "" Then
Set rng = sht.Range("A:A").Find(val & "*", LookAt:=xlWhole)
If Not rng Is Nothing Then
Application.EnableEvents = False
Target.Value = rng.Value
Application.EnableEvents = True
End If
End If
End If
End Sub

Use cell value as range to hide columns

I have a spreadsheet that there is a checkbox the purpose of the checkbox is to hide the name of clients in two adjacent columns. Because the spreadsheet changes from time to time the position of the columns changes thus it is currently P:Q but a year ago it was H:I.
I want to store the 'range' in a cell and reference that from my vba and get that to hide the columns. The checkbox is a simple toggle. I have tried various incarnations without success and my latest effort tells me that I have not se up the range properly. The cel I am using for teh range is F4. The code is currently:
Private Sub CheckBox2_Click()
Dim c As Range
Dim Visy As Integer
Dim My_range As String
'My_range is the range of filled rows stored as a range in cell F4
'Visy stores the state of the checkbox
If CheckBox2.Value = True Then
Visy = 1
Else
Visy = 0
End If
'Stop any use of the spread sheet and set variable initial states
Application.EnableEvents = False
My_range = Sheet9.Cells(4, 6).Value
'Hide the columns
Range(My_range).Hidden = Visy
'Sheet9.colums(My_range).Hidden = True
'Re enable application
On Error GoTo 0
Application.EnableEvents = True
End Sub
This is within a single sheet:
Sub qwerty()
My_range = Cells(4, 6).Value
Range(My_range).EntireColumn.Hidden = True
End Sub
Your Private Sub CheckBox2_Click should be in a worksheet's code sheet. I believe this is the worksheet identified by the Sheet9 worksheet .CodeName property.
A Private Sub in a worksheet codesheet does not have to explicitly reference the .Parent worksheet property on any Range object or
Range.Cells object unless you want to reference another worksheet's cells. These are bound to the cells on the worksheet whose codesheet you are on regardless of the ActiveSheet property.
Private Sub CheckBox2_Click()
Range(Cells(4, "F").Text).EntireColumn.Hidden = CBool(Me.Value)
End Sub
Do not confuse a worksheet's Private Sub behavior with a Private Sub on a module code sheet. A module codesheet should always explicitly reference the parent worksheet (and often the parent workbook) regardless of whether the Sub is Public or Private.
You have to use code in context:
Private Sub CheckBox2_Click()
Dim wsh As Worksheet
Dim sRangeName As String
'context!
Set wsh = ThisWorkbook.Worksheets("TypeNameHere")
sRangeName = wsh.Range("F4")
wsh.Range(sRangeName).EntireColumn.Hidden = CheckBox2.Value
Set wsh = Nothing
End Sub
Thanks to all who responded it helped a lot and put me on the right track. As several of you noted context is important and I was mixing private sub and sub and so had a scope problem when it came to ranges. I also from another source had the suggestion to use a named range rather than read a cell value since the columns were always adjacent. I have published the code below in case it is of value to anyone in the future.
Private Sub CheckBox2_Click()
'Requires ClientNameCol to be set to the range to be hidden
Dim Visy As Boolean
'Stop any use of the spread sheet and set variable initial states
Application.EnableEvents = False
'Check if sheet is to be hidden or not
If Worksheets("Client 16").CheckBox2.Value = True Then
Visy = True
Else
Visy = False
End If
'Hide/unhide the columns
With ThisWorkbook
.Worksheets("Client 16").Range("ClientNameCol").EntireColumn.Hidden = Visy
End With
On Error GoTo 0
Application.EnableEvents = True
End Sub

how to make visual basic work on protected worksheets (no password on protection)

I have a work book with several worksheets that I would like to protect. I am not using a password on the protection. I have some visual basic code associated with this sheet to expand the row width on merged cells. The code will not work when the sheets are protected.
I did find some guidance on adding unprotect code to my code, but can't figure out where to put it and how to address the fact that there is no passord. Further guidance woudl be greatly appreciated!
Here is my code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim NewRwHt As Single
Dim cWdth As Single, MrgeWdth As Single
Dim c As Range, cc As Range
Dim ma As Range
With Target
If .MergeCells And .WrapText Then
Set c = Target.Cells(1, 1)
cWdth = c.ColumnWidth
Set ma = c.MergeArea
For Each cc In ma.Cells
MrgeWdth = MrgeWdth + cc.ColumnWidth
Next
Application.ScreenUpdating = False
ma.MergeCells = False
c.ColumnWidth = MrgeWdth
c.EntireRow.AutoFit
NewRwHt = c.RowHeight
c.ColumnWidth = cWdth
ma.MergeCells = True
ma.RowHeight = NewRwHt
cWdth = 0: MrgeWdth = 0
Application.ScreenUpdating = True
End If
End With
End Sub
You could probably do something like this:
Surround your code with .Unprotect and .Protect
Sub protectSheet()
Dim ws As Worksheet
Set ws = Sheets(1)
With ws
.Unprotect "password"
'Insert Code Here
.Protect "password"
End With
End Sub
try this:
Private Sub Workbook_Open()
Dim wSheet As Worksheet
For Each wSheet In Worksheets
wSheet.Protect Password:="Password_here", _
UserInterFaceOnly:=True
Next wSheet
End Sub
Put this code in 'ThisWorkbook' then use the Workbook_Open Event.
This code protects all the WS everytime you open the WB
but allows macro to run due to UserInterfaceOnly set to true
You need to protect the sheet with password.
If you want a user to edit some cells even if the worksheet is protected then set the locked property of those cells to false before protecting the sheet.
Now when Worksheet_Change is triggered or any procedure is called which is trying to make some changes to excel range (locked cells = true) then you need to Unprotect the Sheet at beginning of the code and protect it at the end again. You may refer #sobin answer for syntax.
Also you may use error handlers and explicitly protect the sheet. This is done to avoid situation wherein the sheet is unprotected and then there is error which comes up for any reason then that would leave the sheets unprotected.

How to delete all row which contains "Grand Total" in Excel automatically?

In my active sheet called Report I have 2 column I & F.
It has repeating cells containing text "Grand Total".
I would like to delete whole row if it contains Grand Total automatically.
VBA code would be nice.
With the following VBA code, you can quickly delete the rows with certain cell value, please do as the following steps:
Select the range that you want to delete the specific row.
Click Developer>Visual Basic, a new Microsoft Visual Basic for applications window will be displayed, click Insert > Module, and input the following code into the Module:
VBA code to Remove entire rows based on cell value(i.e. Grand Total):
Sub Delete_Rows()
Dim rng As Range, cell As Range, del As Range
Set rng = Intersect(Range("A1:D22"), ActiveSheet.UsedRange)
For Each cell In rng
If (cell.Value) = "Grand Total" _
Then
If del Is Nothing Then
Set del = cell
Else: Set del = Union(del, cell)
End If
End If
Next cell
On Error Resume Next
del.EntireRow.Delete
End Sub
Then click "Play/Run" button to run the code, and the rows which have certain value have been removed.
(Note: If you can't see Developer Tab in Excel Do these Steps: 1)Click the Office Button, and then click Excel Options. 2)In the Popular category, under Top options for working with Excel, select the Show Developer tab in the Ribbon check box, and then click OK.)
Using AutoFilter is very efficient. This can also be done without VBA (ie manually)
Main Sub
Sub Main()
Dim ws As Worksheet
Dim rng1 As Range
Dim StrIn As String
Dim rng2 As Range
Set ws = Sheets("Sheet1")
Application.ScreenUpdating = False
Set rng1 = ws.Range("F:F,I:I")
StrIn = "Grand Total"
For Each rng2 In rng1.Columns
Call FilterCull(rng2, StrIn)
Next
Application.ScreenUpdating = True
End Sub
Delete Sub
Sub FilterCull(ByVal rng2, ByVal StrIn)
With rng2
.Parent.AutoFilterMode = False
.AutoFilter Field:=1, Criteria1:=StrIn
.EntireRow.Delete
.Parent.AutoFilterMode = False
End With
End Sub
This should help get you started.
http://msdn.microsoft.com/en-us/library/office/ee814737%28v=office.14%29.aspx#odc_Office14_ta_GettingStartedWithVBAInExcel2010_VBAProgramming101
Also see similar question:
Delete a row in Excel VBA