I have a workbook with 12 sheets and I have placed one Command button in each sheet to hide/unhide rows. For hiding specific rows, I have typed "A" in coloumn A, for every row that needs to be hidden. So code works but it runs forever, takes long time and very slow in hidding or unhiding the rows. In some sheets total number of rows to check are 100 and some sheets it is 750. Please advice the reason for slow running or Is there a better way I can make it work faster. Here is the code:-
Private Sub CommandButton1_Click()
Sheet2.Unprotect ("aaa")
Dim rng As Range
Dim iRow As Range
Dim hidden_status As Boolean
CommandButton1.Caption = "Show / Hide Guidelines"
On Error Resume Next
Set rng = Range("A1:A750")
For Each iRow In rng.Rows
If iRow.Text = "A" Then
With iRow.EntireRow
hidden_status = .Hidden
.Hidden = Not hidden_status
End With
End If
Next iRow
On Error GoTo 0
Sheet2.Protect ("aaa")
End Sub
Each time you hide a row, Excel then is stopping to update your screen (and potentially perform calculations). So to help really speed things up, disable all the screen updates and application events (including calculations) until you've finished hiding.
So with a Sub like this:
Private Sub SetUpdates(ByVal newState As Boolean)
With Application
.ScreenUpdating = newState
.EnableEvents = newState
.DisplayAlerts = newState
End With
End Sub
You can do something like this:
Private Sub CommandButton1_Click()
SetUpdates newState:=False
'--- hide your rows here
SetUpdates newState:=True
End Sub
You could use AutoFilter.
This procedure will hide any row that doesn't contain a value in column A on whichever sheet reference is passed to it. If the filter is already applied it will remove it.
Public Sub HideA(wrkSht As Worksheet)
With wrkSht
If .FilterMode Then
.ShowAllData
Else
.Range("A1", .Cells(.Rows.Count, 1).End(xlUp)) _
.AutoFilter Field:=1, Criteria1:="=", VisibleDropDown:=False
End If
End With
End Sub
On each sheet that contains a button add this code for the button (renaming the procedure as required):
Private Sub CommandButton1_Click()
HideA ActiveSheet
End Sub
The one downside to this is that it doesn't include cell A1 in the filtering.
Related
The moment I type in the text box my rows become hidden and don't come back after clearing the text box. I used the same exact code on a previous table in a different sheet and it worked flawlessly. I believe the error stems from me copying and pasting the entire table into a different sheet and tweaking the code for the new table. I checked on the linked cell and made sure to change the table name. What am I missing?
Private Sub TextBox1_Change()
Application.ScreenUpdating = False
ActiveSheet.ListObjects("Customers").Range.AutoFilter Field:=1, Criteria1:=[C2] & "*", Operator:=xlFilterValues
Application.ScreenUpdating = True
End Sub
Try this code, please. It will use DblClick event. Otherwise, the Change event will be triggered by any character you input. Now, after setting the filter criteria in the text box, you should double click inside it:
Private Sub TextBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean
Dim Tb As ListObject
Set Tb = ActiveSheet.ListObjects("Customers")
Application.ScreenUpdating = False
If TextBox1.Text = "" Then
Tb.AutoFilter.ShowAllData
Else
Tb.Range.AutoFilter field:=1, Criteria1:=CStr([C2]), Operator:=xlFilterValues
End If
Application.ScreenUpdating = True
End Sub
I have written the following macro that calls a Private Sub Worksheet_Calculate() if I confirm that I want to run it. Please find below the first part of the code:
Sub test()
Dim result As VbMsgBoxResult
result = MsgBox("Run Macro?", vbYesNo, "Excel VBA")
If result = vbYes Then
Call Worksheet_Calculate
End If
End Sub
The second code, which is Private Sub Worksheet_Calculate(), shall copy the result of the calculation performed in B2 and paste it in C2 as well as add a timestamp to D2. Columns C and D are populated as values in B2 change, so that I get a list of results and timestamps in C3 and D3, C4 and D4 etc. Here is the code:
Private Sub Worksheet_Calculate()
Dim lastrow As Long
lastrow = Worksheets(1).Cells(Rows.Count, 2).End(xlUp).Row
With Worksheets(1).Cells(lastrow, 2)
.Offset(1, 0) = Cells(2, 1).Value
.Offset(1, 1) = FormatDateTime(Now, vbLongTime)
End With
End Sub
The problem that I am facing is related to the fact that my Public Sub Worksheet_Calculate() is called only once and whenever I recalculate the value of B2 nothing happens.
Is there a way to a) keep second code activated or b) have a button(s) or a tickbox that would activate/deactivate the second code?
Hope it makes sense, thanks in advance!
Worksheet Calculate Event
This is what you put into Module1, or what ever you will call your Module. This is a normal module created with Add Module. I've left the name of the test program intact, but I changed the other because it is used for worksheet events, particularly the Worksheet Calculate event. The test program and the variable blnCalculation enable or disable the whole thing, so in the beginning nothing will be happening before you enable it. You can create a button for it and in its Click event just add Module1.test or just run it from Macros.
Option Explicit
Public TargetValue As Variant
Public blnCalculation As Boolean
Sub test()
Dim result As VbMsgBoxResult
result = MsgBox("Run Macro?", vbYesNo, "Excel VBA")
If result = vbYes Then
blnCalculation = True
ActiveSheet.Calculate
Else
blnCalculation = False
End If
End Sub
Sub SheetCalculate()
Dim lastrow As Long
With ActiveSheet
lastrow = .Cells(Rows.Count, 2).End(xlUp).Row
With .Cells(lastrow, 2)
.Offset(1, 0) = .Parent.Cells(2, 1).Value
.Offset(1, 1) = FormatDateTime(Now, vbLongTime)
End With
End With
End Sub
And the following is what you put into each sheet code aka Object module, which you get by double-clicking on it in VBE. The Worksheet Calculate event happens each time Excel 'decides' to calculate a worksheet, which is in your case probably guaranteed because you say that there is a formula in B2, so the event will happen every time B2 gets calculated. But it won't run the SheetCalculate program unless the value has changed which is checked with the TargetValue variable.
The Worksheet Activate event happens on a worksheet e.g. each time when you select it in the tab. In this case it is used to pass the new B2 value from the newly selected worksheet to the TargetValue variable.
Option Explicit
Private Sub Worksheet_Activate()
If Module1.blnCalculation Then _
Module1.TargetValue = Me.Range("B2").Value
End Sub
Private Sub Worksheet_Calculate()
If Module1.blnCalculation Then
If Me.Range("B2").Value <> Module1.TargetValue Then
Module1.SheetCalculate
End If
End If
End Sub
I know this has been posted as a question numerous times. But I just can't get it working, I've tried numerous methods.
I have code that auto copies specific rows to a new sheet when a specific value is entered into Column B. But this only occurs when assign the marco to a button and manually trigger it. This isn't very efficient when copying over numerous rows. Especially when you're copying over hundreds of rows with only the last few actually changing. I'm hoping this will automatically happen when that value is entered.
So my first sheet is called MASTER and the second sheet is called CON. When Change of Numbers is entered into the MASTER I want to automatically copy these rows into sheet CON.
This code below is situated in The Master Sheet (which is the first). This script is used to hide/unhide specific Columns when values are entered into Column B.
MASTER SHEET
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B:B")) Is Nothing Then
On Error GoTo safe_exit
Application.EnableEvents = False
Dim t As Range
For Each t In Intersect(Target, Range("B:B"))
Select Case (t.Value)
Case "Change of Numbers"
Columns("B:BP").EntireColumn.Hidden = False
Columns("H:BL").EntireColumn.Hidden = True
'do nothing
End Select
Next t
End If
safe_exit:
Application.EnableEvents = True
End Sub
The following script is situated in sheet CON (which is the second sheet). This script is used to auto-copy the rows where X is entered into Column A in the Master sheet. However I have to assign this macro to a button on this sheet. It then grabs all the designated rows each time the macro is triggered.
CON SHEET
Option Explicit
Sub FilterAndCopy()
Dim sht1 As Worksheet, sht2 As Worksheet
Set sht1 = Sheets("MASTER")
Set sht2 = Sheets("CON")
sht2.UsedRange.ClearContents
With Intersect(sht1.Columns("B:BP"), sht1.UsedRange)
.Cells.EntireColumn.Hidden = False ' unhide columns
If .Parent.AutoFilterMode Then .Parent.AutoFilterMode = False
.AutoFilter field:=1, Criteria1:="Change of Numbers"
.Range("A:F, BL:BO").Copy Destination:=sht2.Cells(2, "B")
.Parent.AutoFilterMode = False
.Range("H:BK").EntireColumn.Hidden = True ' hide columns
End With
End Sub
But this still doesn't work without manually running the script.
Your code is not watching for any events to take place. The particular event you want is the Worksheet_Change() event, which is what I see in the second code snippet you provided.
So, you can go about this two ways. One, copy and paste the entire code into this event, or two (which is usually preferred) would be to call the sub within the event handler.
However, for the Worksheet to watch for the Change Event, you need to place this into the worksheet's code module. In the VBE, you will see this as Sheet1, Sheet2, etc.
My recommendation, place your Sub FilterAndCopy() in a standard module. Then in Sheet1's code module, add:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ErrHandler
'Test if criteria is met
If Intersect(Target, Columns("A")) Is Nothing Then
Exit Sub
ElseIf Target.Value = "mySpecificValue" Then
Application.EnableEvents = False
FilterAndCopy
Dim t As Range
For Each t In Intersect(Target, Range("a:a"))
Select Case UCase(t.Value)
Case "X"
Columns("B:C").EntireColumn.Hidden = True
Columns("D:E").EntireColumn.Hidden = False
Case "Y"
Columns("B:C").EntireColumn.Hidden = False
Columns("D:E").EntireColumn.Hidden = True
Case Else
'do nothing
End Select
Next t
End If
ErrHandler:
If Err.Number <> 0 Then
Rem: Optional - Error message and/or err recovery
End If
Application.EnableEvents = True
End Sub
If you first sub works exactly as intended all you need to do is Call the sub from your Worksheet_Change event. Just to be clear, as your Worksheet_Change macro is set-up, it will only call if the change is made on Column A
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("A:A")) Is Nothing Then Exit Sub
Application.EnableEvents = False 'to prevent endless loop
On Error GoTo Finalize 'to re-enable the events
FilterAndCopy
Finalize:
Application.EnableEvents = True
End Sub
I have a macro that hides certain rows when the values in a cell change. However this macro is not running unless you enter the target cell and click on it. I have tried several alternatives but none work for me.
Sheet
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("$b$156").Value = 1 Then Call oculta_4
If Range("$b$156").Value = 2 Then Call oculta_5
If Range("$b$156").Value = 3 Then Call oculta_6
If Range("$b$156").Value = 4 Then Call oculta_7
End Sub
Macro
Sub oculta_4()
Rows("158:176").EntireRow.Hidden = False
Range("$c$158").Select
For Each celda In Range("$c$158:$c$176")
If celda.Value = 0 Then
ActiveCell.EntireRow.Hidden = True
End If
ActiveCell.Offset(1).Select
Next
End Sub
As others have said, to respond to a value changed by a Formula, you need to use Worksheet_Calculate.
As Worksheet_Calculate does not have a Target property, you need to create your own detection of certain cells changing. Use a Static variable to track last value.
You should also declare all your other variables too.
Repeatedly referencing the same cell is slow and makes code more difficult to update. Put it in a variable once, and access that
Select Case avoids the need to use many If's
Don't use Call, it's unnecessary and obsolete.
Adding Application.ScreenUpdating = False will make your code snappy, without flicker
Writing the hidden state of a row takes a lot longer than reading it. So only write it if you need to.
Something like this (put all this code in the code-behind your sheet (that's Hoja1, right?)
Private Sub Worksheet_Calculate()
Static LastValue As Variant
Dim rng As Range
Set rng = Me.Range("B156")
If rng.Value2 <> LastValue Then
LastValue = rng.Value2
Select Case LastValue
Case 1: oculta_4
Case 2: oculta_5
Case 3: oculta_6
Case 4: oculta_7
End Select
End If
End Sub
Sub oculta_4()
Dim celda As Range
Application.ScreenUpdating = False
For Each celda In Me.Range("C158:C176")
With celda.EntireRow
If celda.Value = 0 Then
If Not .Hidden Then .Hidden = True
Else
If .Hidden Then .Hidden = False
End If
End With
Next
Application.ScreenUpdating = True
End Sub
I work with sheets named; Rev00, Rev01, Rev02 etc - among other sheets in my workbook.
It would be very helpful (in order to compare the sub-summaries of different revisions) to set the exact same multiple-filter - as set in active sheet - in only all sheets beginning with "Rev".
This action should most wanted be activated by double Click in Range("A1") or somewhere like that (I dont want button on this one).
If possible next double Click in Range("A1") should reset filters.
Sub Test()
Dim ws As Worksheet, str As String
For Each ws In Worksheets
str = Left(ws.Name, 3)
If str = "Rev" Then
' set filter as in active.sheet
End If
Next ws
End Sub
... and I am stuck ....
will anyone guide me on this?
Yes it is possible. :) Here is a basic sample on how it should work.
Sub Test()
Dim ws As Worksheet, str As String
For Each ws In Worksheets
str = Left(ws.Name, 3)
If UCase(str) = "REV" Then
With ws
'~~> Remove any filters
.AutoFilterMode = False
With <YOUR RANGE>
.AutoFilter Field:=<RELEVANT FIELD>, _
Criteria1:=<YOUR CRITERIA>
'
'~~> Rest of the code
'
End With
'~~> Remove any filters
'.AutoFilterMode = False
End With
End If
Next ws
End Sub
Here you can see Autofilter in action :)
To call the above code by clicking Range A1, you can use the Worksheet_BeforeDoubleClick event.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Intersect(Target, Range("A1")) Is Nothing Then
'
'~~> Your code goes here
'
Cancel = True
End If
End Sub
Regarding your query about making Range A1 respond as an ON/OFF switch, you can use a boolean variable s shown HERE