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.
Related
Sub Save7()
Dim NextRow As Range
Set NextRow = Range("AC" & Sheets("Sheet1").UsedRange.Rows.Count)
Sheet3.Range("AC14:AG14").Copy
Sheet1.Activate
NextRow.PasteSpecial Paste:=xlValues, Transpose:=False
Application.CutCopyMode = False
Set NextRow = Nothing
End Sub
My purpose of this code is to copy data ( Five columns of 'NO' in AC14 to AG14) from sheet 3 and paste to sheet 1 where the last active cell is at.
The code above is working well, however I made some modification to the sheet tab name for sheet 1. Sheet 1 is now called "Equipment stuffs", while sheet 3 name is remaining unchanged.
After those changes, the macro stopped working. The cause is probably because I don't know how to declare "Equipment stuffs" in the code .
There's no need to do copy/paste to move data from one place on the spreadsheet to another. You should simply assign the Value of the respective Range objects, for example:
Sheet1.Range("NamedRange2").Value = Sheet1.Range("NamedRange2").Value
Also, use code names for the sheets, instead of Sheets("SheetName"), and defined named for the ranges, instead of Range("AC14:AG14", otherwise your code will stop working if the user renames the sheet or inserts or deletes any rows above your reference.
If you want to automate this a little you could collect the active workbook and loop through each sheet using wb.Worksheets. Then collect the name with targetSheet.Name.
Option Explicit
Public Sub getSheet()
Dim wb As Workbook
Dim targetSheet As Worksheet
Set wb = ActiveWorkbook
For Each targetSheet In wb.Worksheets
Debug.Print targetSheet.Name
Next targetSheet
End Sub
I’m brazilian hehe, I understood your question , I’ve a code for alter the data in same worksheet (I’ll attach it here), for you to change the data in another worksheet, you need put on:
Worksheets("NameWorkSheet) Activate
for the VBA that’s refers to this tab.
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.
I currently have 3 sheets: Input, Process, Output and a macro that uses values displayed on the input sheet and various stores on the process sheet. The problem is when the user presses a submit button linked to the macro on the input page the sheet switches to the Process sheet before displaying the Output sheet. I understand that this is because of this line of code:
Worksheets("Process").Select
However whenever I remove it from the macro everything goes madly out of range. Is there any way of selecting a sheet without actually visually moving to it? I need the macro to do its thing and then simply display the output sheet. Thanks in advance!
As #Jeeped stated and referenced, avoid using Select and Activate, in addition it is safer to qualify references.
For example you can use Range("A1").Value to get a value of the cell A1 in the currently active worksheet, but what if the user didn't have that sheet active at the time or another proc had moved the view? you could get the value of cell A1 from potentially any worksheet.
It would be best to create a reference to the worksheet and then send all your work through it, this way you do not need to change the active worksheet and there is no ambiguity about where the range values are coming from.
For example: -
Option Explicit
Dim WkSht_I As Worksheet 'Input
Dim WkSht_P As Worksheet 'Process
Dim WkSht_O As Worksheet 'Output
Public Sub Sample()
Set WkSht_I = ThisWorkbook.Worksheets("Input")
Set WkSht_P = ThisWorkbook.Worksheets("Process")
Set WkSht_O = ThisWorkbook.Worksheets("Output")
MsgBox "Input A1 = " & WkSht_I.Range("A1").Value
MsgBox "Process A1 = " & WkSht_P.Range("A1").Value
MsgBox "Output A1 = " & WkSht_O.Range("A1").Value
Set WkSht_O = Nothing
Set WkSht_P = Nothing
Set WkSht_I = Nothing
End Sub
Converting your procedures to this method should be safer and clearer and you can set the active sheet just once for it to show content while the others or being worked on.
#Gary's method is the best method to go with when you are working with multiple worksheets.
If you are working with only two sheets, (Considering you have activesheet and target sheet) I am going to recommend
With Worksheets("Process")
Debug.Print .Range("A1")
Debug.Print Range("A1")
End With
Notice "." infront of Range.
The "." indicates that it is part of With
In other words, .Range("A1") is same as Worksheets("Process").Range("A1")
Because second Range("A1") does not have "." it is same as Activesheet.Range("B1") even it's inside of the With-End
If the activesheet is Process Then the out put will be same
But when you select worksheet other than Process, because activesheet changed, the output will be different.
This will avoide using Select which changes the activesheet
I'm very new to VBA macros and have taught myself some code but I'm struggling with my current piece of work and can't find the answer I am looking for.
I want to copy a range of cells (B3:N21) from one workbook to another "master" workbook - which seems simple enough - but I would like it to copy into a blank/new worksheet in the Master copy every time the Macro is run.
The range contains formulas, I would only need the values copied to the Master workbook.
Any help with this would be greatly appreciated.
Thanks
Worksheets("Sheet1").Range("C1:C5").Copy
Worksheets("Sheet2").activate
Worksheets("Sheet2").Range("D1:D5").PasteSpecial _
Operation:=xlPasteSpecialOperationAdd
End With
I think you only need paste special, this is an example
try this
Option Explicit
Sub main()
Dim masterWb As Workbook
Dim mySht As Worksheet
Set mySht = ThisWorkbook.ActiveSheet '<~~ assuming you're copying values from active worksheet of the workbook the macro resides in
' beware: if you start the macro while the active sheet is not the one you want, this will lead to unespected results
Set masterWb = Workbooks("Master") '<~~ Change "Master" with whatever name your master workbook must have
' beware: we're assuming "Master" workbook is already open, otherwise this line will throw an error
With masterWb.Worksheets.Add
.Range("B3:N21").Value = mySht.Range("B3:N21").Value
End With
End Sub
mind the comments
the code above can be reduce to a much less verbose (and self explanatory, too) one like follows
Sub main2()
Workbooks("Master").Worksheets.Add.Range("B3:N21").Value = ThisWorkbook.ActiveSheet.Range("B3:N21").Value
End Sub
where apply the same comments of the lengthy code, which is:
assuming you're copying values from active worksheet of the workbook the macro resides in
beware: if you start the macro while the active sheet is not the one you want, this will lead to unespected results
change "Master" with whatever name your master workbook must have
beware: we're assuming "Master" workbook is already open, otherwise an error would be thrown
This code
Sheets(1).Activate
Sheets(2).Range("A1").Select
will fail in VBA because you can only use Select on an object which is Active. I understand this is the case.
What element of the Excel datamodel causes this to be the case? I would think there is an implicit intent from a user/coder to Activate any object immediately prior to using Select - I do not understand why VBA would not make this assumption, and, I am assuming there is a reason this distinction exists.
What part of Excel's datamodel prevents selection without activation?
As brettdj pointed out, you do not have to activate the sheet in order to select a range. Here's a reference with a surprisingly large amount of examples for selecting cells/ranges.
Now as for the why do I have to active the sheet first? I do not believe it is a fault of the datamodel, but simply a limitation of the select method for Ranges.
From experimentation, it looks like there are two requirements to select a range in Excel.
Excel must be able to update the UI to indicate what is selected.
The ranges parent (I.E. the sheet) must be active.
To support this claim, you also cannot select a cell from a hidden sheet.
Sheets(1).Visible = False
Sheets(1).Activate
'The next line fails because the Range cannot be selected.
Sheets(1).Range("A1").Select
Simply put, when it comes to Ranges, you cannot select one you cannot see.
I would have claimed this is a limitation of select all together, except that you can actually select an object in a hidden sheet. Silly Excel.
I know that this is a bit late to the party, but I discovered a hack to do this...
Try this code:
Sheets(1).Activate
Sheets(2).Range("A1").Copy
Sheets(2).Range("A1").PasteSpecial xlPasteFormulas
Application.CutCopyMode = False
Note that it is a hack, but it does the trick!!
#Daniel Cook: thanks for your response, but unfortunately Excel itself doesn't play by the same rules imposed on Excel Macros...
To illustrate, I'll briefly present my current problem...
I'm attempting to re-set a table's contents to a common state. This method will be applied to multiple tables across various sheets:
Public Sub restoreTable()
Dim myTableSheet As Worksheet: Set myTableSheet = Range("Table1").Parent
Dim myTable As ListObject: Set myTable = myTableSheet.ListObjects("Table1")
' --- Clear Table's Filter(s)
If myTable.ShowAutoFilter Then ' table has auto-filters enabled
Call myTable.Range.AutoFilter ' disables autofilter
End If
myTable.Range.AutoFilter ' re-apply autofilter
' --- Sort by Sequence number
Call myTable.Sort.SortFields.Clear ' if not cleared, sorting will not take effect
myTable.Sort. _
SortFields.Add Key:=Range("Table1[[#Headers],[#Data],[Column1]]"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
myTable.Sort.Header = xlYes
myTable.Sort.Orientation = xlTopToBottom
myTable.Sort.SortMethod = xlPinYin
Call myTable.Sort.Apply
myTable.Sort.SortFields.Clear
End Sub
For each use-case below, Table1 is found in Sheet1
Use-Case 1:
Activate Sheet1, select range A1
Run restoreTable
observe: range Sheet1 A1 remains selected
Use-Case 2:
Activate Sheet1, select range A1
Activate Sheet2
Run restoreTable
observe: range Sheet1 A1 is not selected, instead the range Table1[#Data] is selected
Solution
It's absolutely terrible, but this is the best solution I could find
Public Sub resotreTable_preserveSelection()
Dim curSheet As Worksheet: Set curSheet = ActiveSheet
Dim tableSheet As Worksheet: Set tableSheet = Range("Table1").Parent
' Change Sheet
tableSheet.Activate
' Remember Selection / Active Ranges
Dim originalSelection As Range: Set originalSelection = Selection
Dim originalActiveCell As Range: Set originalActiveCell = ActiveCell
' Restore Table
Call restoreTable
' Restore Old Selection
originalSelection.Select
originalActiveCell.Activate
' Change Back to old sheet
curSheet.Activate
End Sub
Note: in this case, the original* ranges are not necessary, but you get the point: you can buffer the original selection and restore it when you're finished
I really don't like excel
Of course you don't have to select or activate the sheet to select/activate the cell. My way is to use "On Error Resume Next" and "On Error GoTo 0". Code below selects first cell in every worksheet of a workbook without selecting it. The worksheets are even very hidden on this stage.
On Error Resume Next
For i_wks = 1 To wb_macro.Worksheets.Count
wb_macro.Worksheets(i_wks).Cells(1).Select
Next i_wks
On Error GoTo 0