Excel VBA - Loop not adding autofilters to my worksheets - vba

I have some code that (should) loop through all my worksheets and add an autofilter, however for some reason they're not showing up. When I turn on events, I can see that it is quickly being added then removed almost instantly. I'm assuming that this is because of something written earlier in my code, but I have too much code before this to evaluate what is causing the issue... Is there something I can add to guarantee the filters are added? Code:
Dim wsfixer As Worksheet
For Each wsfixer In ActiveWorkbook.Worksheets
With ActiveSheet
.AutoFilterMode = False
.Range("A:S").AutoFilter
End With
On Error Resume Next
Next

If you want to process the code on each worksheet, change
With ActiveSheet
to
With wsfixer
By using With ActiveSheet the code within the With block is being "shortcutted" to using the active sheet (e.g. .AutoFilterMode is treated as ActiveSheet.AutoFilterMode). So you are executing the same code over and over to the one active sheet.

Related

VBA - copy specific rows from one sheet into another sheet when user closes file

I'm new to VBA and I'm struggling a lot with a file I want to build.
I have a main Sheet that in a simple way looks like this (starting at column B - A is an empty column):
main
This is a simplified version just for the example. The first table of the sheet varies from B13 to O92, the second varies from B104 to O114 but some of those rows might be empty.
My goal is to join rows with content from the first area with rows with content from the second area in a different sheet (Sheet1), add to the left a column with 1s and "Cell 0" (content of cell B1). Using the example, the result would be something like this:
Sheet1
Sheet1 will stay hidden as I'm using it as a source of information to a different file. In fact, I may not need the 1s column if I find a way to copy information in a different way - I'm doing it like this (wsSource is Sheet1):
lRow = wsSource.Columns("A").Find(1, SearchDirection:=xlPrevious, LookIn:=xlValues, LookAt:=xlWhole).Row
wsSource.Range("B1:N" & lRow).Copy
I was trying to do it so Sheet1 is "emptied" when the file is opened and edited when file is closed - so that if new rows are added or information updated, it gets into Sheet1 every time.
I've tried several stuff I found online but couldn't make it to work. My main problem is adding the specified rows one after the others but I'm also struggling to reset Sheet1 every time the file is opened and automatically running the macro when file is closed.
Any help would be really appreciated.
Hopefully this will get you started. Both subs need to be pasted in VBE under ThisWorkBook rather module or sheet(n).
The first sub will execute when the workbook is open.
Are you sure you want to clear your sheet under these circumstances?
You will never have access to your table (without workarounds) since it will clear when opening every time.
If this is what you need, see the below method to clear a dynamic range (Col A - D down to last cell used in Col A) on Sheet1 every time the workbook that houses this code is opened.
Private Sub Workbook_Open()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim ClearRange As Range
Application.ScreenUpdating = False
With ws
.Visible = xlSheetVisible
.Range("A1:D" & .Range("A" & .Rows.Count).End(xlUp).Row).ClearContents
.Visible = xlSheetHidden
End With
Application.ScreenUpdating = True
End Sub
Next, the below sub will only execute before the book is closing. This is where the bulk of your code will go to build your table.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Application.ScreenUpdating = False
ThisWorkbook.Sheets(1).Visible = xlSheetVisible
'Code goes here
ThisWorkbook.Sheets(1).Visible = xlSheetHidden
Application.ScreenUpdating = True
End Sub
You will need to qualify your objects (Ranges, Cells, etc.) directly (which sheet) since the code will not be housed in a sheet. The first sub uses a With block to qualify ranges and the second sub directly qualifies all objects with ThisWorkbook.Sheets(1).
Lastly, I recommend writing your code to build your table inside a module (qualify ranges!) so you can test and debug without needing to close/open your book continuously. Once you have the code working in a module, you can copy and paste the code directly into the 2nd sub here and you are good to go!

Can I set a cell in a sheet as the selected one, without activating the sheet?

In a workbook I am creating I have created a procedure which loops through several worksheets clearing out some ranges++, preparing the workbook for a new year. However, I wanted to set a given cell in the sheet, e.g. F5 as the active one before moving on to the next sheet, as that's where it's most likely the users will start inputting data when they move to the sheet.
However, trying to put both .Select and .Activate into line 5 of the code below fails with Select / Activate method of Range class failed.
For Each ws In ThisWorkbook.Worksheets
If InStr(1, ws.CodeName, "Veke_", vbTextCompare) > 0 And Len(ws.CodeName) = 7 Then
Call lås_opp_ark(ws)
Call oppdater_ark_for_nytt_år(ws, førsteSkiftIVeka(dato))
ws.Range("F5").Select
Call lås_ark(ws)
End If
Next ws
Trying to figure out why this didn't work I eventually came across this documentation-page from Microsoft which states that:
Before you can use the Selection property successfully, you must activate a workbook, activate or select a sheet, and then select a range (or other object) using the Select method.
Which I take to mean that using Select on anything but the active worksheet won't work. The page doesn't explicitly state anything about Activate, but I assume the reasons for the errors I'm getting to be the same.
Now, my question is this - is there any way to set the active cell in a nonactive worksheet without activating it first? If not, would activating each sheet in the workbook to set the active cell be very resource intensive? I guess I could check the latter myself, but any input would be interesting too.
No need to select the cell, instead activate it. Try this:
Replace:
ws.Range("F5").Select
with this:
Application.Goto ws.Cells(1), 1 'This activates ws and scrolls to place cell `A1` at the top of the window
Application.Goto ws.Range("F5") 'This selects Cell `F5`
see Application.Goto Method (Excel)

Excel to replace formulas with values

I have an Excel workbook (1) with around 9 sheets that is pulling in and manipulating data from a second workbook (2).
After pulling in the data from workbook (2) I need to be able to replace the formulas in workbook (1) to the resulting values that the formulas have produced, from here I will then save the workbook (1) with the results.
Is there a macro that can do this for me?
On your new workbook some basic code such as:
Sub Value()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Sheets
ws.UsedRange.Value = ws.UsedRange.Value
Next
End Sub
While the OP is dated, I want to make note of a non-loop method that is useful. In certain scenarios, loops can really slow down the code execution. To replace formulas in a cell --without a loop-- try:
With Sheets("example").Range("A1:C1000")
.value = .value
End With
You can revise the reference as necessary, but the execution is seamless, fast, and as a bonus - prevents range highlighting that cannot be cleared if you pursued the .copy + .pastespecial xlPasteValues approach.
What seems to work for me is to use concatenate()
So, for example, the formula I have referencing a cell from another sheet is:
=arrayformula(iferror(index('To Be Processed'!X:X,small(if($A$1='To Be
Processed'!$Y2,row('To Be Processed'!X:X)),row((2:2))),"")))
and if I change to the formula to:
=concatenate(arrayformula(iferror(index('To Be
Processed'!X:X,small(if($A$1='To Be Processed'!$Y2,row('To Be
Processed'!X:X)),row((2:2))),""))))
and it puts in the text value from the reference cell into my second sheet.
Which may or may not be helpful depending on how you populate your sheets--I'm not very good with VBA, though, which means I do more things manually :)

Paste after unprotecting worksheet not working

I have two sheets "Request" and "Changes" both protected by same password. Password has been stored as a constant in another module. I have tested this code when "Changes" sheet in unprotected and it works, however for some reason, if I have to unprotect the sheet before pasting it won't paste.
I am simplifying trying to keep a record of what was deleted in the workbook by placing the info in a protected sheet "Changes" that is hidden.
Sub PODelete()
Dim rng As Range, ws As Worksheet, ws1 As Worksheet, lr As Integer
Set ws = Sheets("Request")
Set ws1 = Sheets("Changes")
lr = ws1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row 'Last non-blank row for column A
On Error Resume Next
Set rng = Application.InputBox("Select a range", Type:=8)
If Not rng Is Nothing Then
' Checks to see if column A is selected
If rng.Cells.Column = 1 Then
' Checks to see that only 1 cell is selected
If rng.Cells.Count > 1 Then
MsgBox "Select only one P.O. Number to remove"
Else
' Select entire row
rng.EntireRow.Copy
' Activate sheet and pastes values
Sheets("Changes").Activate
ws1.Unprotect Password:=worksheetpassword
Cells(lr, 1).EntireRow.Select
Selection.PasteSpecial Paste:=xlPasteValues
ws1.Protect Password:=worksheetpassword, DrawingObjects:=True, Contents:=True, Scenarios:=True
' Deletes range
ws.Activate
ws.Unprotect Password:=worksheetpassword
rng.EntireRow.Delete
ws.Protect Password:=worksheetpassword, DrawingObjects:=True, Contents:=True, Scenarios:=True
End If
Else
MsgBox "Select P.O. Number to remove"
End If
Else
Exit Sub
End If
End Sub
Once again, if I run this macro with the "Changes" sheet unlocked it will work.
The real answer to this is actually not to unprotect the Worksheet at all. Protect it with a different password that isn't stored in the file anywhere, but set the parameter UserInterfaceOnly:=True. This will allow the macro to make changes to the worksheet, and even if the user find the hidden sheet they won't be able to find the password.
Speaking of hiding it, you should really hide it. As far as I know this is only possible through VBA, but you can set the Worksheet to xlSheetVeryHidden, either through the VBE...
...or in code:
Worksheets("Changes").Visible = xlSheetVeryHidden
Then password protect the VBA project with the same password as the Worksheet, and nobody is going to find it but you unless they know the name of the sheet and manually type it into a cell formula:
=Changes!A1
If you want to avoid that possibility, just name the Worksheet something non-obvious or even total gibberish. The likelihood of an unsuspecting user typing this into a cell...
=kusidkeshlkiehas!A1
...is virtually zero.
I have found that occasionally doing an Unprotect on the active sheet will change the active sheet to a different sheet. I haven’t figured out under what circumstances this occurs or why the particular sheet that ends up selected gets selected. Could this be your problem? I solved this problem by having one routine that is called to do all Unprotects. In that routine I save the active sheet name before doing the Unprotect and then re-select the saved sheet. I haven't seen this problem with Protecting a sheet but just to be safeI have done the same thing with a Protect routine.
I tried to copy a range from one work book(selection.copy only once and keeping the workbook open) and through "for loop", to many other work books(ActiveSheet.Unprotect "password", ActiveSheet.Paste).
This works fine during 1st trial but fails during subsequent trials on the same file with a message "Error 1004 Paste Method of Worksheet Failed" suggested by Mr Darrel Dixon and various other solutions of "UserInterfaceOnly:=True" etc but the error is repeating. The sheet appears UNPROTECTED at that stage and copy from source workbook and paste into destination workbook MANUALLY works fine but through VBA macro, it fails.
Finally, I tried repeating "selection.copy" from source workbook after "ActiveSheet.Unprotect" on each destination workbook/each iteration of for loop.
Even though this appears to be inefficient code, this alone solved the problem of PASTE failure after UNPROTECTING a worksheet.

VBA - Run Time Error 1004 'Application Defined or Object Defined Error'

I have an Excel document that copies a template sheet into a new sheet on the first time it runs. Any more sheets that follow this template are appended to the newly created sheet.
I'm getting the error in the title in this section of code:
If Worksheets("User Configuration").Cells(9, 15).Value = 1 Then
Worksheets("Cable Cards Template").Range("A1:J33").Copy
With Worksheets("Cable Cards")
**.Range(Cells(RangeStartRow, RangeStartColumn), Cells(RangeEndRow, RangeEndColumn)).PasteSpecial xlValues**
.Range(Cells(RangeStartRow, RangeStartColumn), Cells(RangeEndRow, RangeEndColumn)).PasteSpecial xlFormats
End With
Worksheets("Cable Cards Template").Shapes("Picture 1").Copy
Worksheets("Cable Cards").Paste Cells(RangeStartRow, RangeStartColumn)
Call Sheets.FormatCableCardRows
End If
Basically if the If statement is true (the cell = 1), then a range on a particular sheet should be copied and pasted into the new sheet at the range given using PasteSpecial for values and formatting. Following that, the "newly created" sheet should have an image copied into the top left cell of the template and then a subroutine is called to format the rows of the new sheet.
I'm getting the error at the first .Range call after the With Worksheets("Cable Cards") statement. I've tried not using the With statement, copying values directly instead of paste-special etc. The weird thing is that this will run on the first go, when the new sheet is created via:
If (RangeStartRow = 1) Then
Worksheets.Add().Name = "Cable Cards" ' Create new sheet with given name only on first cable card
Columns(1).ColumnWidth = 9.43
Columns(6).ColumnWidth = 11
Columns(10).ColumnWidth = 9
Call FormatForA5Printing("Cable Cards", 71)
End If
but on the 2nd go, it fails entirely, with the Run Time Error 1004 'Application Defined or Object Defined Error'. I'd appreciate any help.
Your cells object is not fully qualified. You need to add a DOT before the cells object. For example
With Worksheets("Cable Cards")
.Range(.Cells(RangeStartRow, RangeStartColumn), _
.Cells(RangeEndRow, RangeEndColumn)).PasteSpecial xlValues
Similarly, fully qualify all your Cells object.
Solution #1:
Your statement
.Range(Cells(RangeStartRow, RangeStartColumn), Cells(RangeEndRow, RangeEndColumn)).PasteSpecial xlValues
does not refer to a proper Range to act upon. Instead,
.Range(.Cells(RangeStartRow, RangeStartColumn), .Cells(RangeEndRow, RangeEndColumn)).PasteSpecial xlValues
does (and similarly in some other cases).
Solution #2:
Activate Worksheets("Cable Cards") prior to using its cells.
Explanation:
Cells(RangeStartRow, RangeStartColumn) (e.g.) gives you a Range, that would be ok, and that is why you often see Cells used in this way. But since it is not applied to a specific object, it applies to the ActiveSheet. Thus, your code attempts using .Range(rng1, rng2), where .Range is a method of one Worksheet object and rng1 and rng2 are in a different Worksheet.
There are two checks that you can do to make this quite evident:
Activate your Worksheets("Cable Cards") prior to executing your Sub and it will start working (now you have well-formed references to Ranges). For the code you posted, adding .Activate right after With... would indeed be a solution, although you might have a similar problem somewhere else in your code when referring to a Range in another Worksheet.
With a sheet other than Worksheets("Cable Cards") active, set a breakpoint at the line throwing the error, start your Sub, and when execution breaks, write at the immediate window
Debug.Print Cells(RangeStartRow, RangeStartColumn).Address(external:=True)
Debug.Print .Cells(RangeStartRow, RangeStartColumn).Address(external:=True)
and see the different outcomes.
Conclusion:
Using Cells or Range without a specified object (e.g., Worksheet, or Range) might be dangerous, especially when working with more than one Sheet, unless one is quite sure about what Sheet is active.
Assgining a value that starts with a "=" will kick in formula evaluation and gave in my case the above mentioned error #1004. Prepending it with a space was the ticket for me.